5

I'm having a problem with the width of a dialog when it contains a ListView: the dialog always nearly fills the width of the screen. Here's an example of the problem:

overly wide dialog

I need the dialog width to match the icons. I've tried fixing this by playing with dialog styles as described here and here, to no avail. I think those threads are on the wrong track (at least for this problem), because (1) they don't solve the problem and (2) I can get the dialog to size as desired by simply not using a ListView. For instance, if I replace the ListView with a simple TextView (details below), everything sizes as desired:

compact dialog

Here's the code I'm using to display the dialog:

private void showDisplayStyleDialog(int id, int choices, int values) {
    final Dialog dlg = new Dialog(this);
    dlg.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dlg.setContentView(R.layout.display_style_dialog);
    final ListView lv = (ListView) dlg.findViewById(android.R.id.list);
    if (lv != null) {
        displayStyleAdapter.setChoices(choices, values);
        lv.setAdapter(displayStyleAdapter);
        lv.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long itemId) {
                dlg.dismiss();
                setDisplayStyle(DisplayStyle
                        .valueOf(displayStyleAdapter.mValues[position]));
            }

        });
    }
    dlg.setCanceledOnTouchOutside(true);
    dlg.show();
}

I also tried creating the dialog using an AlertDialog.Builder, with no improvement. The dialog layout (display_style_dialog.xml) is:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/white" >
    </ListView>

</LinearLayout>

The adapter loads every row from this layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/holder"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="5dp" >

    <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/display_style_image_bg"
        android:contentDescription="@string/cd_display_style" />

</LinearLayout>

Examining the view hierarchy with the Android Debug Monitor reveals that only the ListView itself is the full width of the dialog; each row is (as desired) sized to match the icon width. It's the ListView itself that is forcing the wide dialog. If I change display_style_dialog.xml to:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/test_string"
        android:textColor="@android:color/black"
        android:background="@android:color/white" >
    </TextView>
</LinearLayout>

and make no other changes to code or resources, the result is the compact dialog shown above. As far as I can tell, the problem is strictly due to the presence of the ListView in the dialog.

I think I can work around this in the short term by placing a number of ImageViews in a VerticalLayout inside a ScrollView. Unfortunately, the arrangement and number of icons varies considerably according to the internal state of the app, so this is not a sustainable approach; in the long run, I really need to use a ListView. Any ideas on how to resolve this issue?

Community
  • 1
  • 1
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521

4 Answers4

6

First, sorry for my bad English, but I'll try my best to explain about my opinion.

ListView could contain any data(ex, string) which is short or long. So, if you set LayoutParams as a wrap_content, there is no way to know how long it is. Depends on data, actual data's width could be longer than screen's width.

So, if you set wrap_content, it will be automatically changed to match_parent/fill_parent(I don't know exactly but one of them)

You can see that also in height. ListView doesn't know how may rows should be drawn. So, in your case, ListView's height will be stretched up to Activity's height.

From Google I/O 2010, they explained why we shouldn't use wrap_content on ListView.

As I know, in your case(ListView's height is wrap_content), getView() will be called as many rows exist, when ListView is initialized.

So, my opinion is that you could set:

  1. exact width_value to Dialog, and set ListView's width to match_parent or fill_parent.

  2. ListView's width by x% of Activity's width.


Add, in your test, TextView in Dialog,

we can know exact width value by measuring text. So, It is possible to use wrap_content.

Sufian
  • 6,405
  • 16
  • 66
  • 120
Steven
  • 86
  • 5
  • First, the dialog does not expand to the height of the activity, so `WRAP_CONTENT` works for that. Second, the contents of the rows are images, and their height can also be calculated, so I don't follow your logic (as compared to using a `TextView`). However, thanks for the link to the GoogleI/O presentation; I'll take a look at that to see if there's an explanation for what's going on. – Ted Hopp Aug 14 '13 at 08:05
  • I hope you solve this quickly. :) Btw, if I add little more, the reason we use listView is we don't want to measure whole child views in listview at once. So, if you use listview it's natural. and dialog doesn't expand to the height of the activity, bcuz list items are only three. but if you add more, dialog will be expanded. and it cause many getView calling. – Steven Aug 14 '13 at 08:16
  • I hope this link also helps people. http://stackoverflow.com/questions/6547154/wrap-content-for-a-listviews-width – Steven Aug 14 '13 at 08:52
1

I think you can fix this by making the Listview layout_width = 50sp. I mean something fix number instead of wrap_content.

arshu
  • 11,805
  • 3
  • 23
  • 21
  • That may be a possibility in the short term. I'll try it and see what happens. (I would need to use dp instead of sp; I'm not displaying text.) However, I would eventually need a solution that does not require a fixed width. – Ted Hopp Aug 14 '13 at 07:37
  • You must use fix the width of the listview and you can possibly use a dimens.xml . You can set different width for different screen resolutions by defining it in the dimens.xml file. That would be the long term fix for your listview. – arshu Aug 14 '13 at 19:15
0

i was finally able to get this to work ..i subclassed Listview and changed how it measured. It seems the listview natively will measure just the first child. So it uses the width of the first child. I changed it to search the listviews children for the widest row.

so your xml for the dialog content would look like this:

<?xml version="1.0" encoding="UTF-8"?>

<com.mypackage.myListView
    android:id="@+id/lv"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
 />

and the myListView would look like this:

public class MyListView extends ListView{

public MyListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // TODO Auto-generated method stub
    int maxWidth = meathureWidthByChilds() + getPaddingLeft() + getPaddingRight();
    super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY), heightMeasureSpec);     
}


 public int meathureWidthByChilds() {
    int maxWidth = 0;
    View view = null;
    for (int i = 0; i < getAdapter().getCount(); i++) {
        view = getAdapter().getView(i, view, this);
        //this measures the view before its rendered with no constraints from parent
        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
        if (view.getMeasuredWidth() > maxWidth){
            maxWidth = view.getMeasuredWidth();
        }
    }
    return maxWidth;
 }
}
j2emanue
  • 60,549
  • 65
  • 286
  • 456
0

Use RecyclerView instead of ListView it doesn't make dialog larger.

Vahan
  • 3,016
  • 3
  • 27
  • 43