AlertDialog with custom view: resize to wrap view contents

I had this problem in the application that I am creating. Please ignore all design flaws and best practices flaws, this is just to show an example of what I cannot solve.

I have a DialogFragment that returns a basic AlertDialog with a custom View set using AlertDialog.Builder.setView() . If this View has a specific size requirement, how do I get Dialog to resize correctly to display all content in a custom View ?

This is an example of the code I used:

 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); } } } } 
Is being created

A Button that opens by pressing DialogFragment . Custom View ( myView ) must have a width of 800 and a height of 300, which is set correctly in the onMeasure() override. This View draws the measured borders in purple for debugging purposes.

The width is 800 wider than the default size of Dialog on my device, but it is cropped and not stretched correctly.

I looked at the following solutions:

  • DialogFragment.getDialog returns null
  • How to control the width and height of the default warning dialog on Android?
  • Alert dialog box or custom alert dialog

I have derived the following two approaches to coding:

  • Get WindowManager.LayoutParams Dialog and override them using myDialog.getDialog().getWindow().get/setAttributes()
  • Using the setLayout(w, h) method via myDialog.getDialog().getWindow().setLayout()

I tried them everywhere that I can think of (overriding onStart() , in onShowListener , after creating and displaying Dialog , etc.) and, as a rule, you can work correctly with both methods if LayoutParams is given a specific value . But whenever WRAP_CONTENT provided, nothing happens.

Any suggestions?

EDIT:

Screenshot: enter image description here

Screenshot of a specific value (note 900, 850 does not cover the entire width of the view, which makes sense, given that the whole window is adjusted. Thus, this provides - if you need another - the reason why WRAP_CONTENT is significant / fixed values ​​are not suitable ): enter image description here

+42
java android android-layout android-dialogfragment android-alertdialog
Feb 16 '13 at 4:54
source share
8 answers

I have a working solution, which, frankly, I think it is too deep to get such a simple result. But here it is:

What exactly is going on:

By opening the Dialog layout with the Hierarchy Viewer , I was able to examine the entire AlertDialog layout and what exactly was happening: enter image description here

The blue icon is all parts of a high level ( Window , frames for the Dialog visual style, etc.) and from the end of the blue down, where are the components for the AlertDialog ( red = name, yellow = scroll delay, possibly for the AlertDialog s list, green = Dialog , i.e. custom view, orange =).

From here it is clear that the 7-view path (from the beginning of blue to the end of green ) was incorrect WRAP_CONTENT . Looking at the LayoutParams.width each View , it turned out that all were given LayoutParams.width = MATCH_PARENT and somewhere (I think at the top) the size was set. Therefore, if you follow this tree, it is clear that your custom View at the bottom of the tree will never affect the size of the Dialog .

So what were the existing solutions?

  • Both coding approaches mentioned in my question just got the top View and modified its LayoutParams . Obviously, with all View objects in the tree corresponding to the parent element, if the top level is set to static size, the whole Dialog will resize. But if the top level is set to WRAP_CONTENT , all other View objects in the tree are still looking at the tree at “MATCH the PARENT”, and not looking down at the tree to “WRAP them CONTENT”.

How to solve a problem:

Wrong, change the LayoutParams.width all View objects in the impact path to WRAP_CONTENT .

I found that this can only be done AFTER onStart life cycle DialogFragment . Thus, onStart is implemented as:

 @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 a function to properly modify the hierarchy of 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(); } 

And here is the working result: enter image description here

This confuses me why the whole Dialog would not want to be WRAP_CONTENT with an explicit set of minWidth to handle all cases that match the default size, but I'm sure there is a good reason for this as it was (it would be interesting to hear).

+52
Feb 17 '13 at 5:25
source share

After

 dialog.show(); 

just use

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

A very simple solution, but it works for me. I am expanding the dialogue, but I assume that this will work for DialogFragment as well.

+27
Jul 18 '13 at 13:32
source share

AlertDialog uses these two window attributes to determine the smallest size that can be such that they float on tablets with a reasonable width in the center of the screen.

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

You can expand the default dialog style of your choice and change the values ​​to apply your own logic.

+7
Apr 7 '15 at 13:36
source share

I found one question with BT answer. The dialog is out of line (not in the center of the screen). To fix this, I added a change to the gravity of the parent layouts. See the updated forceWrapContent () method.

 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(); } 
+2
Sep 01 '15 at 19:53
source share

This worked for me:

 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); 
+2
Mar 23 '16 at 20:48
source share

Using setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);

0
May 21 '15 at
source share

His late reply, but I think I should share this with new visitors so that they know which dialogue they should prefer.

If you want to create your own dialog box with any of your views. Then you can choose the easiest way to do this.

In the question that you need dialogue, you need to wrap the content ,, you can create a parent layout with match_parent with a transparent background and center of gravity. and put in it your main layout. therefore, it will look like a dialog box centered.

BONUS. With this method, you can use scrollview, recycler view, and any type of layout in the dialog box.

In this example, R.layout.dialog_update_name is a sample layout. which you want to inflate in the dialog box.

 public void showCustomDialog(final Context context) { this.context = context; 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_update_name, null, false); findByIds(view); /*HERE YOU CAN FIND YOU IDS AND SET TEXTS OR BUTTONS*/ ((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(); } 
0
Oct 27 '17 at 21:00
source share

just use 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); } } 
-one
Mar 18 '16 at 12:43
source share



All Articles