Search code examples
androidandroid-layoutandroid-linearlayoutlistviewitemandroid-relativelayout

How can I create a multiline, multicolumn list item layout?


I am creating a messaging app and am currently working on the inbox. I'm using a RecyclerView to display the list of conversations and would like each list item to look like this:

enter image description here

  1. Leftmost is the contact image at 90dp x 90dp
  2. The first row has two columns--the contact name and the date. The date should not be a fixed size as I currently have it, but fit at most DD/MM/YYYY (can be smaller in the case of something like "Sunday") and should be anchored to the right margin. The contact(s) should expand as necessary to fill any space up to the date.
  3. The second row contains as much text of the last message as will fit.

I was going to use layout_weight but that doesn't work in a RelativeLayout (and doesn't allow the contact names to elongate in the case of a shorter date) and LinearLayout doesn't let me use layout_toEndOf. I'm a newbie at Android development so I'm not sure if one of those is the "right" answer.

What's the proper way of accomplishing the layout I'm looking for?

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

    <RelativeLayout
        android:id="@+id/conversation_info"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="@dimen/activity_horizontal_margin">

        <RelativeLayout
            android:id="@+id/conversation_image_layout"
            android:layout_width="90dp"
            android:layout_height="90dp">

            <ImageView
                android:id="@+id/conversation_contact_image"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" />

        </RelativeLayout>    

        <TextView
            android:id="@+id/conversation_contact_name"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:gravity="center_vertical"
            android:textSize="@dimen/conversations_contact_font_size"
            android:layout_toEndOf="@id/conversation_image_layout" />    

        <TextView
            android:id="@+id/conversation_date"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@id/conversation_contact_name"
            android:gravity="right"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:textSize="@dimen/conversations_date_font_size" />    

        <TextView
            android:id="@+id/conversation_snippet"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/conversation_contact_name"
            android:textSize="@dimen/conversations_snippet_font_size"
            android:layout_toRightOf="@id/conversation_image_layout" />

    </RelativeLayout>    

    </LinearLayout>

This is what a list item currently looks like (I haven't loaded contact images yet). It's fine for the most part (though my layout code is probably incredibly bloated so I'd appreciate if anyone could point out ways to make it more concise) but notice how the date isn't on the same level as the contact name and wraps:

enter image description here


Solution

  • After it became clear to me that some of the layout parameters didn't mean what I thought they did, I spent some more time looking at my choices.

    It is not recommended to nest LinearLayouts/use layout_weight within a list item, as the number of views created increases rapidly as more items are added. I managed to minify my code and keep it in a single RelativeLayout with the following code:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/conversation_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/activity_horizontal_margin">
    
        <ImageView
            android:id="@+id/conversation_contact_image"
            android:layout_width="64dp"
            android:layout_height="64dp"/>
    
        <TextView
            android:id="@+id/conversation_contact_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/conversations_contact_font_size"
            android:layout_toEndOf="@id/conversation_contact_image"/>
    
        <TextView
            android:id="@+id/conversation_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignBaseline="@id/conversation_contact_name"
            android:textSize="@dimen/conversations_date_font_size"/>
    
        <TextView
            android:id="@+id/conversation_snippet"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/conversation_contact_name"
            android:textSize="@dimen/conversations_snippet_font_size"
            android:layout_toEndOf="@id/conversation_contact_image"/>
    
    </RelativeLayout>
    

    Notable changes:

    • No nested tags - It was unnecessary to group the first line in its own layout
    • Used layout_alignBaseline - @Vucko pointed out that I was using marginTop on the contact name but not the date. Even after removing it, the two were still misaligned. android:gravity had no effect and it turns out none of these actually affect the text inside the layout
    • Used layout_alignParentEnd to fix the date issue. I've realized that in my case, a layout component usually only needs to reference one other in order to properly align itself relative to the rest of the layout.