具有自定义视图的警报对话框:调整大小以包装视图的内容

我在正在构建的应用程序中一直遇到此问题。请忽略所有的设计缺点和缺乏最佳实践方法,这纯粹是为了展示我无法解决的问题的一个例子。

我有它返回一个基本与自定义集使用.如果这有特定的大小要求,我如何正确调整自身大小以显示自定义中的所有内容?DialogFragmentAlertDialogViewAlertDialog.Builder.setView()ViewDialogView

这是我一直在使用的示例代码:

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,单击一下即可打开 。自定义 () 的宽度必须为 800,高度为 300,这在 覆盖 中正确设置。这将以洋红色绘制其测量的边界以进行调试。ButtonDialogFragmentViewmyViewonMeasure()View

800 宽度比我设备上的默认大小宽,但被剪切而不是正确拉伸。Dialog

我研究了以下解决方案:

我推断出以下两种编码方法:

  1. 获取 的 并使用 覆盖它们WindowManager.LayoutParamsDialogmyDialog.getDialog().getWindow().get/setAttributes()
  2. 使用方法通过setLayout(w, h)myDialog.getDialog().getWindow().setLayout()

我已经在我能想到的任何地方都尝试过它们(覆盖,在创建和显示之后,在,等等),并且如果提供特定值,通常可以使这两种方法正常工作。但无论何时提供,都不会发生任何事情。onStart()onShowListenerDialogLayoutParamsWRAP_CONTENT

有什么建议吗?

编辑:

情况截图:enter image description here

特定值的屏幕截图(此处输入了注释 900,850 未覆盖视图的整个宽度,考虑到正在调整整个窗口,这是有道理的。因此,如果需要另一个 ,则提供基本原因/固定值不合适的原因):WRAP_CONTENTenter image description here


答案 1

我有一个有效的解决方案,说实话,我认为挖掘得太深了,无法获得如此简单的结果。但这里是:

究竟发生了什么:

通过使用层次结构查看器打开布局,我能够检查整个布局以及到底发生了什么:DialogAlertDialogenter image description here

蓝色突出显示是所有高级部分(视觉样式的框架等),从蓝色向下的末尾是组件的位置(红色=标题,黄色=滚动视图存根,可能为列表s,绿色=内容,即自定义视图,橙色=按钮)。WindowDialogAlertDialogAlertDialogDialog

从这里可以清楚地看出,7视图路径(从蓝色的起点到绿色的终点)是未能正确的地方 。查看每个显示所有给定的,并且在某个地方(我猜在顶部)设置了大小。因此,如果您遵循该树,很明显,您在树底部的自定义将永远无法影响.WRAP_CONTENTLayoutParams.widthViewLayoutParams.width = MATCH_PARENTViewDialog

那么,现有的解决方案在做什么呢?

  • 我的问题中提到的两种编码方法都只是简单地获得顶部并修改其.显然,由于树中的所有对象都与父级匹配,如果将顶层设置为静态大小,则整体大小将发生变化。但是,如果将 top 级别设置为 ,则树中的所有其余对象仍在向上查找树以“匹配其父级”,而不是向下查找树以“包装其内容”。ViewLayoutParamsViewDialogWRAP_CONTENTView

如何解决问题:

坦率地说,将影响路径中的所有对象更改为 .LayoutParams.widthViewWRAP_CONTENT

我发现这只能在调用生命周期步骤之后才能完成。所以实现如下:onStartDialogFragmentonStart

@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);
}

然后函数适当地修改层次结构:ViewLayoutParams

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();
}

这是工作结果:enter image description here

这让我感到困惑,为什么整个人都不想使用显式集来处理所有适合默认大小的情况,但我相信有一个很好的理由(有兴趣听到它)。DialogWRAP_CONTENTminWidth


答案 2

dialog.show();

只需使用

dialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, yourHeight);

非常简单的解决方案,但它对我有用。我正在扩展一个对话框,但假设这也适用于DialogFragment。


推荐