60

私が構築しているアプリケーションでこの問題が発生しています。設計上の欠点やベスト プラクティス アプローチの欠如はすべて無視してください。これは純粋に、私が解決できない例を示すためのものです。

を使用してカスタムセットDialogFragmentで基本を返すものがあります。これに特定のサイズ要件がある場合、 custom 内のすべてのコンテンツを表示するために を正しくサイズ変更するにはどうすればよいですか?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);
            }
        }
    }
}

Buttonが作成され、クリックすると が開きますDialogFragment。カスタムView( myView) は、幅 800、高さ 300 である必要があり、オーバーライドで正しく設定されていますonMeasure()。これViewは、デバッグの目的で測定された境界をマゼンタで描画します。

800 の幅はDialog、デバイスのデフォルト サイズよりも広いですが、正しく伸ばされるのではなく、切り取られています。

私は次の解決策を見てきました:

次の 2 つのコーディング方法を推測しました。

  1. の を取得し、次WindowManager.LayoutParamsDialog使用してオーバーライドしますmyDialog.getDialog().getWindow().get/setAttributes()
  2. setLayout(w, h)メソッドの使用myDialog.getDialog().getWindow().setLayout()

私は考えられるあらゆる場所でそれらを試しました (オーバーライドonStart()onShowListener、 が作成されて表示された後など)。通常、特定の値が指定されているDialog場合、両方のメソッドを正しく機能させることができます。しかし、供給されても何も起こりません。LayoutParamsWRAP_CONTENT

助言がありますか?

編集:

状況のスクリーンショット: ここに画像の説明を入力

特定の値のスクリーンショット (ここに 900 が入力されていることに注意してください。850 はビューの幅全体をカバーしていません。これは、ウィンドウ全体が調整されていることを考えると理にかなっています。したがって、別の値が必要な場合は、必要な理由WRAP_CONTENT/修正された理由が提供されます)値は適切ではありません): ここに画像の説明を入力

4

8 に答える 8

65

私には、正直なところ、このような単純な結果を得るには深すぎると思う実用的な解決策があります。しかし、ここにあります:

正確に何が起こっているのか:

Hierarchy ViewerDialogでレイアウトを開くことにより、のレイアウト全体と、何が起こっているのかを正確 に調べることができました。AlertDialogここに画像の説明を入力してください

青いハイライトはすべての高レベルの部分(、Window視覚Dialogスタイルのフレームなど)であり、青い終わりから下に向かって、のコンポーネントAlertDialog=タイトル、黄色=スクロールビュースタブ、おそらくリストAlertDialogの場合)があります。 、=Dialogコンテンツ、つまりカスタムビュー、オレンジ=ボタン)。

ここから、7ビューパス(青の始まりからの終わりまで)が正しく失敗していることが明らかでしたWRAP_CONTENTLayoutParams.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_CONTENTminWidth

于 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 は、これら 2 つのウィンドウ属性を使用して最小サイズを定義し、タブレットでは画面の中央に適切な幅でフロートします。

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

BT の回答で 1 つの問題が見つかりました。ダイアログが整列されたままになっています (画面の中央ではありません)。これを修正するために、親レイアウトの重力の変更を追加しました。更新された 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 に答える
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 に答える