带有自定义视图的 AlertDialog:调整大小以包装视图的内容 [英] AlertDialog with custom view: Resize to wrap the view's content
问题描述
我在构建的应用程序中遇到了这个问题.请忽略所有设计缺陷和缺乏最佳实践方法,这纯粹是为了展示我无法解决的示例.
我有 DialogFragment
,它返回一个基本的 AlertDialog
和一个使用 AlertDialog.Builder.setView()
设置的自定义 View
代码>.如果此 View
有特定的大小要求,我如何让 Dialog
正确调整自身大小以显示自定义 View
中的所有内容?
这是我一直在使用的示例代码:
package com.test.test;导入 android.os.Bundle;导入 android.app.Activity;导入 android.app.AlertDialog;导入 android.app.Dialog;导入 android.app.DialogFragment;导入 android.content.Context;导入 android.graphics.Canvas;导入 android.graphics.Color;导入 android.graphics.Paint;导入 android.graphics.Paint.Style;导入 android.view.Gravity;导入 android.view.LayoutInflater;导入 android.view.View;导入 android.view.WindowManager;导入 android.view.View.OnClickListener;导入 android.view.ViewGroup;导入 android.view.ViewGroup.LayoutParams;导入 android.widget.ArrayAdapter;导入 android.widget.Button;导入 android.widget.EditText;导入 android.widget.FrameLayout;导入 android.widget.LinearLayout;导入 android.widget.Spinner;导入 android.widget.TextView;公共类 MainActivity 扩展 Activity {@覆盖protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//使用按钮启动按钮 b = 新按钮(这个);b.setText("启动");b.setOnClickListener(new OnClickListener() {@覆盖public void onClick(View v) {//启动对话框myDialog d = new myDialog();d.show(getFragmentManager(), null);}});setContentView(b);}公共静态类 myDialog 扩展了 DialogFragment {@覆盖公共对话框 onCreateDialog(Bundle savedInstanceState) {//创建对话框AlertDialog.Builder db = new AlertDialog.Builder(getActivity());db.setTitle("测试警报对话框:");db.setView(new myView(getActivity()));返回 db.create();}受保护的类 myView 扩展了视图 {油漆 p = 空;公共 myView(上下文 ct){超级(ct);//为绘图设置paintp = 新油漆();p.setColor(Color.MAGENTA);p.setStyle(Style.STROKE);p.setStrokeWidth(10);}@覆盖protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(800, 300);}@覆盖受保护的无效 onDraw(画布画布){//绘制一个矩形显示视图的边界canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), p);}}}}
一个 Button
被创建,它在点击时打开 DialogFragment
.自定义 View
(myView
) 需要具有 800 的宽度和 300 的高度,这在 onMeasure()
的覆盖中正确设置.此 View
以品红色绘制其测量边界以用于调试目的.
800 宽度比我设备上的默认 Dialog
尺寸宽,但被剪裁而不是正确拉伸.
我查看了以下解决方案:
- ,我能够检查
AlertDialog
的整个布局以及到底发生了什么:蓝色 突出显示所有高级部分(
Window
、Dialog
视觉样式的框架等)以及从末尾开始蓝色下方是AlertDialog
的组件所在的位置(红色 = 标题,黄色 = 滚动视图存根,也许对于列表AlertDialog
s,green =Dialog
内容,即自定义视图,orange = 按钮).>从这里很明显,7 视图路径(从 蓝色 的开始到绿色 的结尾)是未能正确
WRAP_CONTENT
.查看每个View
的LayoutParams.width
发现所有都给定了LayoutParams.width = MATCH_PARENT
和某处(我猜在顶部)大小设置.因此,如果您遵循该树,很明显您在树底部的自定义View
将永远无法影响Dialog代码>.
那么现有的解决方案在做什么?
- 我的问题中提到的两种编码方法都只是获取顶部
View
并修改其LayoutParams
.显然,树中的所有View
对象都与父对象匹配,如果顶级设置为静态大小,则整个Dialog
将改变大小.但是如果顶层设置为WRAP_CONTENT
,树中所有其余的View
对象仍然查找树以匹配他们的父母",而不是俯视树以包装他们的内容".
如何解决问题:
说白了,把影响路径中所有
View
对象的LayoutParams.width
改成WRAP_CONTENT
.我发现这只能在调用
DialogFragment
的onStart
生命周期步骤之后才能完成.所以onStart
是这样实现的:@Override公共无效 onStart() {//必须先调用它!否则视图调整将不会出现在显示的对话框中(很可能被覆盖)super.onStart();forceWrapContent(myCustomView);}
然后函数适当修改
View
层次结构LayoutParams
:protected void forceWrapContent(View v) {//从提供的视图开始查看当前 = v;//向上移动直到失败,修改 LayoutParams做 {//获取父级ViewParent parent = current.getParent();//检查父节点是否存在如果(父母!= null){//获取视图尝试 {当前 =(查看)父级;} catch (ClassCastException e) {//在顶视图时会发生这种情况,不能将其转换为视图休息;}//修改布局current.getLayoutParams().width = LayoutParams.WRAP_CONTENT;}} while (current.getParent() != null);//请求重新完成布局current.requestLayout();}
这是工作结果:
这让我很困惑,为什么整个
Dialog
不想成为WRAP_CONTENT
并带有显式的minWidth
设置来处理适合默认大小,但我相信它的方式有很好的理由(有兴趣听到它).I have been having this problem in an application I am building. Please ignore all of the design shortcomings and lack of best practice approaches, this is purely to show an example of what I cannot solve.
I have
DialogFragment
which returns a basicAlertDialog
with a customView
set usingAlertDialog.Builder.setView()
. If thisView
has a specific size requirement, how do I get theDialog
to correctly resize itself to display all of the content in the customView
?This is the example code I have been using:
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); } } } }
A
Button
is created, which opens theDialogFragment
on a click. The customView
(myView
) is required to have a width of 800 and height of 300 which is correctly set in an override ofonMeasure()
. ThisView
, draws its measured bounds in magenta for debugging purposes.The 800 width is wider than the default
Dialog
size on my device, but is clipped rather than stretching correctly.I have looked through the following solutions:
- DialogFragment.getDialog returns null
- How to control the width and height of the default Alert Dialog in Android?
- Size of Alert Dialog or Custom Alert Dialog
I have deduced the following two coding approaches:
- Get the
WindowManager.LayoutParams
of theDialog
and override them usingmyDialog.getDialog().getWindow().get/setAttributes()
- Using the
setLayout(w, h)
method throughmyDialog.getDialog().getWindow().setLayout()
I have tried them everywhere I can think of (overriding
onStart()
, in aonShowListener
, after theDialog
is created and shown, etc) and can generally get both methods to work correctly if theLayoutParams
are supplied a specific value. But wheneverWRAP_CONTENT
is supplied, nothing happens.Any suggestions?
EDIT:
Screenshot of the situation:
Screenshot of a specific value (note 900 is entered here, 850 doesn't cover the entire width of the View, which makes sense given the entire window is being adjusted. So that provides - if another was needed - reason why
WRAP_CONTENT
is essential / fixed values are not appropriate):解决方案I have a working solution that to be honest, I think digs way too deep to obtain such a simple result. But here it is:
What exactly is happening:
By opening the
Dialog
layout with the Hierarchy Viewer, I was able to examine the entire layout of theAlertDialog
and what exactly what was going on:The blue highlight is all of the high level parts (
Window
, frames for theDialog
visual style, etc) and from the end of the blue down is where the components for theAlertDialog
are (red = title, yellow = a scrollview stub, maybe for listAlertDialog
s, green =Dialog
content i.e. custom view, orange = buttons).From here it was clear that the 7-view path (from the start of the blue to the end of the green) was what was failing to correctly
WRAP_CONTENT
. Looking at theLayoutParams.width
of eachView
revealed that all are givenLayoutParams.width = MATCH_PARENT
and somewhere (I guess at the top) a size is set. So if you follow that tree, it is clear that your customView
at the bottom of the tree, will never be able to affect the size of theDialog
.So what were the existing solutions doing?
- Both of the coding approaches mentioned in my question were simply getting the top
View
and modifying itsLayoutParams
. Obviously, with allView
objects in the tree matching the parent, if the top level is set a static size, the wholeDialog
will change size. But if the top level is set toWRAP_CONTENT
, all the rest of theView
objects in the tree are still looking up the tree to "MATCH their PARENT", as opposed to looking down the tree to "WRAP their CONTENT".
How to solve the problem:
Bluntly, change the
LayoutParams.width
of allView
objects in the affecting path to beWRAP_CONTENT
.I found that this could only be done AFTER
onStart
lifecycle step of theDialogFragment
is called. So theonStart
is implemented like:@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); }
Then the function to appropriately modify the
View
hierarchyLayoutParams
: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(); }
And here is the working result:
It confuses me why the entire
Dialog
would not want to beWRAP_CONTENT
with an explicitminWidth
set to handle all cases that fit inside the default size, but I'm sure there is a good reason for it the way it is (would be interested to hear it).这篇关于带有自定义视图的 AlertDialog:调整大小以包装视图的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 我的问题中提到的两种编码方法都只是获取顶部