Question

I want to have a Listview in which the rows change their layout based on a variable that is inside the object I want to display.

So I wrote a custom ArrayAdapter and overrode the getView() method like this:

public class ChatAdapter extends ArrayAdapter<Nachricht> {

    public ChatAdapter(Context context, ArrayList<Nachricht> users) {
        super(context, R.layout.message_layout_standard, users);
    }

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

        // SimpleDateFormat to change the view of the date
        SimpleDateFormat dformat = new SimpleDateFormat("dd.MM.yyyy   HH:mm");

        // Get the data item for this position
        Nachricht nachricht = getItem(position);

        // Check if an existing view is being reused, otherwise inflate the view
        if (convertView == null) {
            if(nachricht.SENDER_ID.equals(DataService.options.ownUser.USER_ID)){
                convertView = LayoutInflater.from(getContext()).inflate(R.layout.message_layout_own, null);
            }else{
                convertView = LayoutInflater.from(getContext()).inflate(R.layout.message_layout_other, null);
            }
        }

        // Lookup view for data population
        TextView chatdate = (TextView) convertView.findViewById(R.id.date_chat);
        TextView chatsender = (TextView) convertView.findViewById(R.id.sender_chat);
        TextView chatmessage = (TextView) convertView.findViewById(R.id.message_chat);

        if(!nachricht.SENDER_ID.equals(DataService.options.ownUser.USER_ID)){

            // Set the sender of the message
            chatsender.setText("" + DataService.freunde.get(DataService.freunde.indexOf(new Freund(nachricht.SENDER_ID, ""))).name);
            // Set the Message
            chatmessage.setText(nachricht.TEXT);

            //Set the Date/Clock of the Message
            chatdate.setText(dformat.format(nachricht.DATE));

        }else{
            // Set the sender of the message
            chatsender.setText("Du");
            // Set the Message
            chatmessage.setText(nachricht.TEXT);
            chatdate.setText(dformat.format(nachricht.DATE));

        }

        // Return the completed view to render on screen
        return convertView;

    }
}

The xml for my views looks like this. Only the margins and colors are different:

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

<!--
    Dies ist das Layout zu der Java-Class 'ChatAdapter'
    Es erstellt die Chat-Nachrichten in der ListView
    der einzelnen Chats.
-->


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_width="wrap_content">


        <TextView
            android:id="@+id/sender_chat"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="left"
            android:height="20sp"
            android:text="sender_chat"
            android:textColor="#000000"
            android:textSize="11sp"
            android:textStyle="bold" />

        <ImageView
            android:id="@+id/background_chat"
            android:scaleType="fitXY"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="5dp"
            android:layout_marginRight="70dp"
            android:layout_below="@+id/sender_chat"
            android:layout_above="@+id/date_chat"
            android:src="@drawable/message_own" />

        <TextView
            android:textColor="#000000"
            android:id="@+id/message_chat"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="left"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="7dp"
            android:layout_marginBottom="7dp"
            android:layout_marginRight="70dp"
            android:layout_below="@+id/sender_chat"

            android:text="message_chat"
            android:textSize="15sp" />



        <TextView
            android:textColor="#000000"
            android:layout_below="@+id/message_chat"
            android:id="@+id/date_chat"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:height="20dp"
            android:text="date_chat"
            android:textSize="11sp"/>

</RelativeLayout>

And the fragment layout where the list is placed:

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

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Senden"
        android:id="@+id/button"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/nachrichtChatText"
        android:layout_alignTop="@+id/button"
        android:layout_toLeftOf="@+id/button"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/talkView"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_above="@+id/button"
        android:layout_below="@+id/button2"
        android:cacheColorHint="@android:color/transparent"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Chatmitglieder"
        android:id="@+id/chatTeilnehmer"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_toLeftOf="@+id/button2"
        android:layout_above="@+id/talkView" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" + "
        android:id="@+id/buttonChangeChat"
        android:layout_alignParentTop="true"
        android:layout_alignRight="@+id/button"
        android:layout_alignEnd="@+id/button" />
</RelativeLayout>

It works fine until I have two views with different layouts in my list. Then the list seems to switch back and forth between the layouts every few seconds.

Picture for the curious: http://imgur.com/5KbXKu2

I tried to only use one layout and change the properties of it but when I scrolled too fast I had some strange display errors. Problably due to the recycling of the listview.

Next thing I'd try is using different row classes for each item but maybe any of you has an idea how to get this to work?

Was it helpful?

Solution

For your different types I'd choose to change properties not related to the LayoutParams, for example use setPadding or a view to the left whose visibility you toggle between gone/invisible or gone/visible. I like the setPadding approach, seems to me it will be the quickest and cleanest thing.

I bet the mess is somehow related to changing Layout Parameters for every getView(): It should work, but you would need to call requestLayout inside getView everytime and it can adversely affect performance. I'm no expert at this, but it's even probable that the time you win for using a convertView is lost when you force the measure and layout phase again and again for every view (something that in the case of a quick fling is happening tens of times continuously)

From http://developer.android.com/training/custom-views/optimizing-view.html: Another very expensive operation is traversing layouts. Any time a view calls requestLayout(), the Android UI system needs to traverse the entire view hierarchy to find out how big each view needs to be. If it finds conflicting measurements, it may need to traverse the hierarchy multiple times. UI designers sometimes create deep hierarchies of nested ViewGroup objects in order to get the UI to behave properly. These deep view hierarchies cause performance problems. Make your view hierarchies as shallow as possible.

Besides the setPadding solution, if you are adding more types in the future (ie. a file attach/voice/images...) it's not a bad idea to implement the ViewTypeCount, etc... as @Picpik suggests. This way, in convertView you will get a view to reuse of the same type as the new view and you can safely use the different layouts approach.

OTHER TIPS

You have to override getViewTypeCount() and getItemViewType() in a BaseAdapter to specify that you have two different types of views.

Here's an example.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top