Search code examples
androidlistviewgravitylayout-gravity

android: how to change property of layout_gravity of a row_item in a listview


I'm trying to implement a chat bubbles that can appear to the left or the right. I read a lot of answers already published here but i didn't manage to get any of them working for me, maybe i'm missing something.

if i change the layout_gravity of the linearLayout in the row_item xml from left to right manually and then look at the graphical layout i can see that it works as i wanted. the problem is that i'm unable to control this property. as i understand the problem is connect to the fact that the LayoutParams that exist are of the listview.LayoutParams, and if i try to get the LayoutParams to an listview.LayoutParams object it works. but that is not what i'm trying to achieve.

here's the code:

the activity xml

<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="MergeRootFrame" >

<ListView
    android:id="@+id/listViewMessages"
    android:layout_width="match_parent"
    android:layout_height="0dp" 
    android:layout_weight="0.98" >

</ListView>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.02"
    android:background="#D3D3D3"
    android:orientation="horizontal" >

    <EditText
        android:id="@+id/editTextMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@+id/imageButtonSend"
        android:ems="10"
        android:hint="@string/textFieldMessage"
        android:textSize="14sp"
        android:visibility="visible" >

        <requestFocus />
    </EditText>

    <ImageButton
        android:id="@+id/imageButtonSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="14dp"
        android:background="?android:attr/selectableItemBackground"
        android:contentDescription="@string/imageButtonSendNow_contentDescription"
        android:src="@drawable/ic_action_send_now" 
        android:onClick="onClick" />

</RelativeLayout>

</LinearLayout>

the row_item xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/singleMessageContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:orientation="horizontal"
android:padding="6dp" >

    <TextView
        android:id="@+id/textViewMessage"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:layout_weight="0.85"
        android:layout_marginBottom="2dp"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="2dp"
        android:gravity="center_vertical"
        android:text="Message"
        android:textSize="16sp"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/TextViewTime"
        android:layout_width="wrap_content"
        android:layout_height="14dp"
        android:layout_weight="0.1"
        android:layout_gravity="bottom"
        android:layout_marginLeft="22dp"
        android:gravity="center_vertical"
        android:text="Time"
        android:textSize="12sp"
        tools:ignore="HardcodedText" />

    <ImageView
        android:id="@+id/imageViewSuccess"
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:layout_weight="0.05"
        android:layout_gravity="bottom"
        android:contentDescription="successIcon"
        android:src="@drawable/ic_action_accept"
        tools:ignore="HardcodedText" />

  </LinearLayout>

the adapter code:

public class ConversationAdapter extends ArrayAdapter<Message> {

private final Context context;

static class ViewHolder {
    public TextView message;
    public TextView time;
    public ImageView success;
    public LinearLayout singleMessageContainer;

}

public ConversationAdapter(Context context, ArrayList<Message> values) {
    super(context, R.layout.conversation_list_item, values);
    this.context = context;

}

  @Override
public View getView(int position, View convertView, ViewGroup parent) {
      View rowView = convertView;
        // reuse views
        if (rowView == null) {
          LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          rowView = inflater.inflate(R.layout.conversation_list_item, parent,false);
          // configure view holder
          ViewHolder viewHolder = new ViewHolder();
          viewHolder.message = (TextView) rowView.findViewById(R.id.textViewMessage);
          viewHolder.time = (TextView) rowView.findViewById(R.id.TextViewTime);
          viewHolder.success = (ImageView) rowView.findViewById(R.id.imageViewSuccess);
          viewHolder.singleMessageContainer = (LinearLayout)     rowView.findViewById(R.id.singleMessageContainer);

          rowView.setTag(viewHolder);
        }

        // fill data
        ViewHolder holder = (ViewHolder) rowView.getTag();

        holder.message.setText(getItem(position).getMessage());
        holder.time.setText(getItem(position).getUniversalTimeString());
        holder.success.setImageResource(R.drawable.ic_action_accept);

        boolean isMine = (getItem(position).getSender() == 0);

        holder.singleMessageContainer.setBackgroundResource(
                isMine ? R.drawable.bubble_yellow : R.drawable.bubble_green);

        // This part doesn't work
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) 
                holder.singleMessageContainer.getLayoutParams();
        params.gravity=(isMine ? Gravity.LEFT : Gravity.RIGHT);

        holder.singleMessageContainer.setLayoutParams(params);

        return rowView; 
  }

}

Solution

  • Solution

    I went on reading about gravity and layout_gravity and encountered a more detail information that helped me to solve this problem. In few answers to questions over here, there is a solution to use warpper layout but the important things to notice weren't written.

    first thing to know is that is you set linear layout as horizontal you will not be able to use the gravity property as left or right only top or bottom so the outer container has to be vertical and the innner warpper has to be horizontal for displaying the message and other data one after the other in a line.

    and then you can use the method setGravity(Gravity.LEFT) or setGravity(Gravity.RIGHT) of the outer container to properly align the inner warpper to the left or the right. you need to pay attention also to the properties of fill_parent and wrap_content

    here the updated code: the row_item xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/singleMessageContainer"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="left"
    android:orientation="vertical"
    android:padding="6dp" >
    
    <LinearLayout
         android:id="@+id/warpper"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="horizontal" >
    
        <TextView
            android:id="@+id/textViewMessage"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginBottom="2dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:layout_marginTop="2dp"
            android:text="Message"
            android:textSize="14sp"
            tools:ignore="HardcodedText" />
    
        <TextView
            android:id="@+id/TextViewTime"
            android:layout_width="wrap_content"
            android:layout_height="14dp"
            android:layout_gravity="bottom"
            android:text="Time"
            android:textSize="12sp"
            tools:ignore="HardcodedText" />
    
        <ImageView
            android:id="@+id/imageViewSuccess"
            android:layout_width="wrap_content"
            android:layout_height="12dp"
            android:layout_gravity="bottom"
            android:contentDescription="successIcon"
            android:src="@drawable/ic_action_accept"
            tools:ignore="HardcodedText" />
    
        </LinearLayout>
    
    </LinearLayout>
    

    and the updated adpater code:

    public class ConversationAdapter extends ArrayAdapter<Message> {
    
    private final Context context;
    
    static class ViewHolder {
        public TextView message;
        public TextView time;
        public ImageView success;
        public LinearLayout singleMessageContainer;
        // Added
        public LinearLayout warpper;
    
    }
    
    public ConversationAdapter(Context context, ArrayList<Message> values) {
        super(context, R.layout.conversation_list_item, values);
        this.context = context;
    
    }
    
      @Override
    public View getView(int position, View convertView, ViewGroup parent) {
          View rowView = convertView;
            // reuse views
            if (rowView == null) {
              LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
              rowView = inflater.inflate(R.layout.conversation_list_item, parent,false);
              // configure view holder
              ViewHolder viewHolder = new ViewHolder();
              viewHolder.message = (TextView) rowView.findViewById(R.id.textViewMessage);
              viewHolder.time = (TextView) rowView.findViewById(R.id.TextViewTime);
              viewHolder.success = (ImageView) rowView.findViewById(R.id.imageViewSuccess);
              viewHolder.singleMessageContainer = (LinearLayout) rowView.findViewById(R.id.singleMessageContainer);
              // Added
              viewHolder.warpper = (LinearLayout) rowView.findViewById(R.id.warpper);
    
              rowView.setTag(viewHolder);
            }
    
            // fill data
            ViewHolder holder = (ViewHolder) rowView.getTag();
    
            holder.message.setText(getItem(position).getMessage());
            holder.time.setText(getItem(position).getUniversalTimeString());
            holder.success.setImageResource(R.drawable.ic_action_accept);
    
            boolean isMine = (getItem(position).getSender() == 0);
    
            // Changed to warpper
            holder.warpper.setBackgroundResource(
                    isMine ? R.drawable.bubble_yellow : R.drawable.bubble_green);
            // Added
            holder.singleMessageContainer.setGravity(isMine ? Gravity.LEFT : Gravity.RIGHT);
    
    
            return rowView; 
      }
    
    }
    

    And it should work. only thing, is that you may see a warnning in the xml about one of the linear layout is useless, but it wont work without it.

    Hope it will help you.