60

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

我有DialogFragment一个使用. 如果这有特定的大小要求,我如何才能正确调整自身大小以显示自定义中的所有内容?AlertDialogViewAlertDialog.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);
            }
        }
    }
}

AButton已创建,DialogFragment单击即可打开。custom View( myView) 需要具有 800 的宽度和 300 的高度,这在onMeasure(). ThisView以洋红色绘制其测量范围以用于调试目的。

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

我查看了以下解决方案:

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

  1. 获取WindowManager.LayoutParamsofDialog并使用myDialog.getDialog().getWindow().get/setAttributes()
  2. 使用setLayout(w, h)方法通过myDialog.getDialog().getWindow().setLayout()

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

有什么建议么?

编辑:

情况截图: 在此处输入图像描述

特定值的屏幕截图(注意在此处输入 900,850 不会覆盖视图的整个宽度,考虑到正在调整整个窗口,这是有道理的。因此,如果需要另一个,这提供了WRAP_CONTENT必要/固定的原因值不合适): 在此处输入图像描述

4

8 回答 8

65

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

究竟发生了什么:

通过使用Hierarchy Viewer打开Dialog布局,我能够检查整个布局以及到底发生了什么: AlertDialog在此处输入图像描述

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

从这里可以清楚地看出,7 视图路径(从蓝色的开始到绿色的结束)是错误的WRAP_CONTENT。查看LayoutParams.width每个View显示所有内容都已给出LayoutParams.width = MATCH_PARENT,并且在某处(我猜在顶部)设置了大小。因此,如果您遵循那棵树,很明显您View在树底部的自定义,将永远无法影响Dialog.

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

  • 我的问题中提到的两种编码方法都是简单地获取顶部View并修改其LayoutParams. 显然,View在树中的所有对象都与父对象匹配的情况下,如果将顶层设置为静态大小,则整体Dialog将改变大小。但如果顶层设置为WRAP_CONTENT,树中的所有其余View对象仍在查找树以“匹配其父对象”,而不是向下查找树以“包装其内容”。

如何解决问题:

说白了就是将影响路径LayoutParams.width中所有对象的 改为。ViewWRAP_CONTENT

我发现这只能在调用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人不想WRAP_CONTENT使用一个明确的minWidth集合来处理所有符合默认大小的情况,但我确信它是有充分理由的(有兴趣听到它) .

于 2013-02-17T05:25:05.847 回答
35

dialog.show();

只需使用

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

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

于 2013-07-18T13:32:55.193 回答
16

AlertDialog 使用这两个窗口属性来定义它们可以达到的最小尺寸,以便在平板电脑上它们在屏幕中心以合理的宽度浮动。

http://developer.android.com/reference/android/R.attr.html#windowMinWidthMajor http://developer.android.com/reference/android/R.attr.html#windowMinWidthMinor

您可以扩展您选择的默认对话框样式并更改值以应用您自己的逻辑。

于 2015-04-07T13:36:04.303 回答
6

这对我来说没问题:

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);
于 2016-03-23T20:48:42.620 回答
4

我发现B T 的回答有一个问题。对话框已左对齐(不在屏幕中心)。为了解决这个问题,我添加了更改父布局的重力。请参阅更新的 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();
}
于 2015-09-01T19:53:49.853 回答
3

对话框应该是包装内容

您可以使用带有透明背景重心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();
}
于 2017-10-27T21:00:35.843 回答
0

采用 setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);

于 2015-05-21T15:07:26.510 回答
-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);
    }
}
于 2016-03-18T12:43:59.453 回答