bauerca / drag-sort-listview

Android ListView with drag and drop reordering.
3.2k stars 1.44k forks source link

Problems with complex linear layout items #73

Open hzardaryan opened 11 years ago

hzardaryan commented 11 years ago

I have some issues when complex Linear Layout is used for list items. The problem is that item doesn't take the full width even though root layout declares "android:layout_width="fill_parent"". The strange thing is that issue does not appear when using Relative Layout. I have tried mentioned Linear Layout with regular ListView and it was displayed correctly. Is it possible that i am missing some attribute or setting?

My layout looks like this:

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:descendantFocusability="blocksDescendants" android:id="@+id/list_item" style="@style/Item.Main" android:layout_width="fill_parent" android:background="#FF00FF00">

<include
    layout="@layout/list_item_left" />

<LinearLayout
    style="@style/Item.DataLayout">

    <TextView
        android:id="@+id/item_text1"
        style="@style/Item.Text.LightPrimary"
        android:text="Item Text" />

    <TextView
        android:id="@+id/item_text2"
        style="@style/Item.Text.LightSecondary"
        android:text="Item Sub-text" />

</LinearLayout>

<ImageView
    android:id="@+id/item_image"
    style="@style/Item.Icon.Nowplaying" />

<include
    layout="@layout/list_item_right" />

I am attaching a screen shot. screenshot

bauerca commented 11 years ago

Well that's too bad. Could you give me a little more info? Specifically I'd like to see the results of:

Log.d("ustinio", "item width="+dslv.getChildAt(index).getWidth());
Log.d("ustinio", "child width="+dslv.getChildAt(index).getChildAt(0).getWidth());

where dslv is your DragSortListView instance.

hzardaryan commented 11 years ago

Ok, i have slightly modified the requested log, but the idea is the same:

Log.d("DEBUG", "item " + i + " width= " + list.getChildAt(i).getWidth()); Log.d("DEBUG", "child " + i + " width= "

And here is the result: 02-11 11:06:29.875: D/DEBUG(9739): item 0 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 0 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 1 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 1 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 2 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 2 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 3 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 3 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 4 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 4 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 5 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 5 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 6 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 6 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 7 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 7 width= 720 02-11 11:06:29.875: D/DEBUG(9739): item 8 width= 720 02-11 11:06:29.875: D/DEBUG(9739): child 8 width= 720

It is pretty strange though

bauerca commented 11 years ago

Very strange.

DSLV wraps each list item provided by your adapter with a very lightweight custom ViewGroup called DragSortListItem. "item n width= 720" is the width of the wrapper. "child n width= 720" is the width of the wrapped item, in your case, the LinearLayout.

I assume that 720 is the desired width since everything is 720 in the above log output (i.e. I don't see a wide item). This means that dslv is in fact measuring the width of your LinearLayout appropriately, but the children of your LinearLayout are not taking the full width offered.

I am boggled by the fact that you get different behavior in the ListView though.

Unfortunately I can't see the layout params for your LL children b/c they are hidden in styles!

hzardaryan commented 11 years ago

Some more info. I have changed the logging to show the measured width:

Log.d("DEBUG", "item " + i + " width= "+ list.getChildAt(i).getMeasuredWidth()); Log.d("DEBUG", "child " + i + " width= "+ ((ViewGroup) list.getChildAt(i)).getChildAt(0).getMeasuredWidth());

And here is the result:

02-12 00:47:12.210: D/DEBUG(23142): item 0 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 0 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 1 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 1 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 2 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 2 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 3 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 3 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 4 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 4 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 5 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 5 width= 304 02-12 00:47:12.210: D/DEBUG(23142): item 6 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 6 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 7 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 7 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 8 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 8 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 9 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 9 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 10 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 10 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 11 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 11 width= 254 02-12 00:47:12.210: D/DEBUG(23142): item 12 width= 600 02-12 00:47:12.210: D/DEBUG(23142): child 12 width= 600

This one seems to be more correct and it represents the visible state.

Regarding the layout params hidden in the styles. The LinearLayout that uses "@style/Item.DataLayout" has "fill_parent" width and weight equal to 1. All others have weight equal to 0, so it should take the whole space. The feeling is that weight is being ignored for some reasons...

bauerca commented 11 years ago

Okay, at this point I'll have to play with it. Could you fork this repo and modify the demo project in a separate branch to reproduce the above behavior? Thanks.

daneren2005 commented 11 years ago

I'm also getting similar behavior in https://github.com/daneren2005/Subsonic/blob/master/subsonic-android/res/layout/song_list_item.xml, although in my case I am using:

android:layout_width="0dip" android:layout_weight="1"

which makes the layout take up the remaining space in a standard ListView, but is only wrapping the original contents with dslv. I see in the demo that it is done like this:

android:layout_width="wrap_content" android:layout_weight="1"

But when I change my layout to do that as well it still is not taking up the entire screen.

daneren2005 commented 11 years ago

Ok so I found a solution, but I'm not exactly sure why it is working this way. The wrapper is a class which directly inherits LinearLayout. It then inflates from a linear layout xml file. Before I wasn't specifying the layout width or height in the main wrapper, just in the inflated layout. I wasn't doing this because it seemed unnecessary and worked just fine with the standard ListView. When using dslv it then appears to be changing the standard behavior of the wrapper class somehow (I'm not sure exactly what it is doing) so that it is just wrapping the contents instead of using filling the parent. Adding the following to the wrapper class in the constructor got rid of the problem and makes it take up the entire screen properly:

setLayoutParams(new AbsListView.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

hzardaryan commented 11 years ago

Hey daneran2005,

Thanks for the suggestion. The provided solution also worked for my case.

pikay commented 11 years ago

Hi there, i have the same problem as described above. The Views inside the list only take as much space as they would using wrap_content. My List child elements are LinearLayout (width: match_parent) and composed of different image and textviews (one of them has weight=1 and width=0).

Could you specify where to copy your workaround? I tried it inside the DragSortItemView.java , but it's already there?!

public DragSortItemView(Context context) {
    super(context);

    // always init with standard ListView layout params
    setLayoutParams(new AbsListView.LayoutParams(
            ViewGroup.LayoutParams.FILL_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT));

    //setClipChildren(true);
}
hzardaryan commented 11 years ago

Hello pikay,

The solution suggested by daneren2005 should be used in the class that inflates your layout. Thus, you will need to create some class if you still don't have it.

Something like this:

public class SomeView extends LinearLayout{

    public SomeView(Context context) {
           LayoutInflater li = (LayoutInflater) getContext().getSystemService(
               Context.LAYOUT_INFLATER_SERVICE);
           li.inflate(R.layout.some_view_layout, this, true);

          setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT));
     }

}

pikay commented 11 years ago

Thanks for the example, works like a charm now.

arantius commented 11 years ago

This is happening to me. Glad I took a look at the open issues before I reported one!

Based on the above comments, I added a v.setLayoutParams() immediately after v = mInflater.inflate(...) in my list adapter's getView() and things look like I expect again! (But I did MATCH_PARENT rather than FILL_PARENT.) Glad this worked because I didn't know, were I to create a SomeView() to do this, where to put it.

christarnowski commented 11 years ago

I can confirm that @arantius's solution works fine. For example (in adapter; item's top level layout is LinearLayout):

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    // ...

    if (convertView == null) {
        convertView = inflater.inflate(R.layout.list_item, null);
        convertView.setLayoutParams(new LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
        );

        //...
    }

    //...
}

@bauerca Can you make it so this workaround is not required? Or at least document this behavior?

cucko commented 11 years ago

yep, @arantius 's solution worked for me as well. thanks.

alexhilton commented 10 years ago

@arantius 's solution really works. Thanks.

Centril commented 10 years ago

Instead of @arantius solution you could pass in the parent as root but but attach it directly. You should rarely ever use null as root in a call to inflate. This fix should be slightly more performant.

convertView = LayoutInflater.from( getContext() ).inflate( id, parent, false );
j1c1m1b1 commented 10 years ago

@arantius solution rules!!!

Guihgo commented 8 years ago

Thanks @arantius 's solution and thanks @christarnowski for show how use...