我在我正在构建的应用程序中遇到此问题。请忽略所有设计缺点和缺乏最佳实践方法,这纯粹是为了展示我无法解决的一个例子。
我DialogFragment
使用AlertDialog
返回基本View
,其中包含自定义AlertDialog.Builder.setView()
。如果此View
具有特定尺寸要求,如何让Dialog
正确调整自身大小以显示自定义View
中的所有内容?
这是我一直使用的示例代码:
package com.test.test;
import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Use a button for launching
Button b = new Button(this);
b.setText("Launch");
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Launch the dialog
myDialog d = new myDialog();
d.show(getFragmentManager(), null);
}
});
setContentView(b);
}
public static class myDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Create the dialog
AlertDialog.Builder db = new AlertDialog.Builder(getActivity());
db.setTitle("Test Alert Dialog:");
db.setView(new myView(getActivity()));
return db.create();
}
protected class myView extends View {
Paint p = null;
public myView(Context ct) {
super(ct);
// Setup paint for the drawing
p = new Paint();
p.setColor(Color.MAGENTA);
p.setStyle(Style.STROKE);
p.setStrokeWidth(10);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(800, 300);
}
@Override
protected void onDraw(Canvas canvas) {
// Draw a rectangle showing the bounds of the view
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), p);
}
}
}
}
创建了Button
,点击后会打开DialogFragment
。自定义View
(myView
)的宽度必须为800,高度为300,并在覆盖onMeasure()
时正确设置。这个View
用于测量洋红色的测量边界以进行调试。
800宽度比我设备上的默认Dialog
尺寸宽,但被裁剪而不是正确拉伸。
我查看了以下解决方案:
我推导出以下两种编码方法:
WindowManager.LayoutParams
的{{1}}并使用Dialog
myDialog.getDialog().getWindow().get/setAttributes()
setLayout(w, h)
方法
醇>
我已经尝试了我能想到的任何地方(覆盖myDialog.getDialog().getWindow().setLayout()
,在onStart()
,onShowListener
创建和显示后等等)并且通常可以使这两种方法都能正常工作如果Dialog
提供了特定值,请正确无误。但是只要提供LayoutParams
,就什么都不会发生。
有什么建议吗?
修改
情况截图:
特定值的屏幕截图(在这里输入注释900,850没有覆盖视图的整个宽度,这在整个窗口正在调整的情况下是有意义的。因此,如果需要另一个窗口,则提供 - 请说明原因{ {1}}必不可少/固定值不合适):
答案 0 :(得分:60)
我有一个有效的解决方案,说实话,我认为挖掘方式太深,无法获得如此简单的结果。但这是:
到底发生了什么:
通过使用Hierarchy Viewer打开Dialog
布局,我能够检查AlertDialog
的整个布局以及究竟发生了什么:
蓝色突出显示的是所有高级部分(Window
,Dialog
视觉样式的帧等)以及蓝色的结尾 down是AlertDialog
的组件所在的位置(红色 =标题,黄色 =滚动视图存根,可能用于列表AlertDialog
s,绿色 = Dialog
内容即自定义视图,橙色 =按钮)。
从这里很清楚,7视图路径(从蓝色的开头到绿色的结尾)是未能正确{{ 1}}。查看每个WRAP_CONTENT
的{{1}},显示所有LayoutParams.width
都在View
和某处(我猜在顶部)设置了大小。因此,如果您关注该树,很明显,树底部的自定义LayoutParams.width = MATCH_PARENT
永远不会能够影响View
的大小。
那么现有的解决方案在做什么?
Dialog
并修改其View
。显然,当树中的所有LayoutParams
个对象与父级匹配时,如果顶级设置为静态大小,则整个View
将改变大小。但是,如果顶级设置为Dialog
,则树中所有其余WRAP_CONTENT
个对象仍然查找树以“匹配他们的父母”,而不是俯视树到“WRAP他们的内容”。如何解决问题:
直言不讳地,将影响路径中所有View
个对象的LayoutParams.width
更改为View
。
我发现这只能在调用WRAP_CONTENT
的{{1}}生命周期步骤后才能完成。所以onStart
的实现方式如下:
DialogFragment
然后是适当修改onStart
层次结构@Override
public void onStart() {
// This MUST be called first! Otherwise the view tweaking will not be present in the displayed Dialog (most likely overriden)
super.onStart();
forceWrapContent(myCustomView);
}
的函数:
View
以下是工作结果:
让我感到困惑的是为什么整个LayoutParams
不希望protected void forceWrapContent(View v) {
// Start with the provided view
View current = v;
// Travel up the tree until fail, modifying the LayoutParams
do {
// Get the parent
ViewParent parent = current.getParent();
// Check if the parent exists
if (parent != null) {
// Get the view
try {
current = (View) parent;
} catch (ClassCastException e) {
// This will happen when at the top view, it cannot be cast to a View
break;
}
// Modify the layout
current.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
}
} while (current.getParent() != null);
// Request a layout to be re-done
current.requestLayout();
}
设置明确的Dialog
以处理所有符合默认大小的情况,但我确信这是一个很好的理由(它有兴趣听到它)。
答案 1 :(得分:31)
在
dialog.show();
只需使用
dialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, yourHeight);
非常简单的解决方案,但它适用于我。我正在扩展一个对话框但是假设这也适用于DialogFragment。
答案 2 :(得分:11)
AlertDialog使用这两个窗口属性来定义它们的最小尺寸,以便在平板电脑上它们以合理的宽度浮动在屏幕的中心。
http://developer.android.com/reference/android/R.attr.html#windowMinWidthMajor http://developer.android.com/reference/android/R.attr.html#windowMinWidthMinor
您可以扩展您选择的默认对话框样式,并更改值以应用您自己的逻辑。
答案 3 :(得分:5)
这对我有用:
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
dialog.show();
dialog.getWindow().setAttributes(lp);
答案 4 :(得分:4)
我在B T's answer找到了一个问题。 Dialog已离开alligned(不在屏幕中央)。为了解决这个问题,我添加了更改父布局的重力。请参阅更新的forceWrapContent()方法。
protected void forceWrapContent(View v) {
// Start with the provided view
View current = v;
// Travel up the tree until fail, modifying the LayoutParams
do {
// Get the parent
ViewParent parent = current.getParent();
// Check if the parent exists
if (parent != null) {
// Get the view
try {
current = (View) parent;
ViewGroup.LayoutParams layoutParams = current.getLayoutParams();
if (layoutParams instanceof FrameLayout.LayoutParams) {
((FrameLayout.LayoutParams) layoutParams).
gravity = Gravity.CENTER_HORIZONTAL;
} else if (layoutParams instanceof WindowManager.LayoutParams) {
((WindowManager.LayoutParams) layoutParams).
gravity = Gravity.CENTER_HORIZONTAL;
}
} catch (ClassCastException e) {
// This will happen when at the top view, it cannot be cast to a View
break;
}
// Modify the layout
current.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
}
} while (current.getParent() != null);
// Request a layout to be re-done
current.requestLayout();
}
答案 5 :(得分:2)
对话框应该包含内容
您可以使用 match_parent 与透明背景和重心制作父级布局。 并将您的主要布局置于其下。所以它会看起来像中心定位对话框。
通过这种方法,您可以在对话框中使用Scrollview,RecyclerView和任何类型的布局。
public void showCustomDialog(Context context) {
Dialog dialog = new Dialog(context);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.dialog_layout, null, false);
findByIds(view); /*find your views by ids*/
((Activity) context).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
dialog.setContentView(view);
final Window window = dialog.getWindow();
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
window.setBackgroundDrawableResource(R.color.colorTransparent);
window.setGravity(Gravity.CENTER);
dialog.show();
}
答案 6 :(得分:0)
使用
setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);
答案 7 :(得分:-1)
只需使用AppCompatDialog
即可import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatDialog;
import android.view.Window;
import android.widget.ProgressBar;
public class ProgressDialogCompat extends AppCompatDialog {
public ProgressDialogCompat(Context context) {
super(context);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Context context = getContext();
int padding = context.getResources().getDimensionPixelSize(R.dimen.x_medium);
ProgressBar progressBar = new ProgressBar(context);
progressBar.setPadding(padding, padding, padding, padding);
setContentView(progressBar);
setCancelable(false);
super.onCreate(savedInstanceState);
}
}