title: Android Implementing Chat Bubble in ListView
body: <p>I am in the process of writing a chat client on Android but I am having some issues with putting a chat bubble in my client.  My chat screen consists of a <code>ListView</code> with a textbox and a Send button at bottom.  For an outgoing message, the texts are left aligned in the <code>ListView</code> row. For an incoming message, the texts are right aligned in the <code>ListView</code> row. However, the chat bubble does not resize to the length of the incoming message texts.  This issue does not happen for the left aligned outgoing message.</p>
  
  <p>Here the screen shot is below.</p>
  
  <p><a href="https://i.stack.imgur.com/QGJm8.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/QGJm8.png" alt="Image"></a>
  </p>
  
  <p>The chat message texts are stored in a database and are displayed in the <code>ListView</code> via a cursor adapter.  The alignment of the chat texts are determined on-the-fly in the Java source code in <code>MessageAdapter</code>.  Both chat bubbles are done using nine patch image of Android.  </p>
  
  <p>Below is my chat activity layout, the <code>ListView</code> with issue is <code>messageHistoryList</code>:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical"
    android:padding="10dip" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"&gt;
  
      &lt;ListView
        android:id="@+id/messageHistoryList"
        android:layout_width="wrap_content" 
        android:layout_height="0px" 
        android:layout_weight="1"/&gt;
  
      &lt;LinearLayout 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" &gt;
  
        &lt;EditText
          android:id="@+id/message"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:gravity="top"
          android:layout_weight="1"/&gt;
  
        &lt;Button 
            android:id="@+id/sendMessageButton"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="4"
            android:text="Send"/&gt;
  
      &lt;/LinearLayout&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>ListView row layout:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"&gt;
  
      &lt;RelativeLayout
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:id="@+id/userAndMessage"&gt;
  
          &lt;TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:id="@+id/textUser"
              android:textStyle="bold"
              android:textColor="@color/blue"/&gt;
  
          &lt;TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:id="@+id/textMessage"
              android:textColor="@color/blue"
              android:textStyle="bold"/&gt;
  
      &lt;/RelativeLayout&gt;
  
      &lt;TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:id="@+id/textTime"
          android:textColor="@color/blue"/&gt;
  
  &lt;/RelativeLayout&gt;
  </code></pre>
  
  <p>Message adapter:</p>
  
  <pre><code>public class MessageAdapter extends SimpleCursorAdapter {
  
      static final String[] FROM = { ChatHistoryManager.C_TIME };
      static final int[] TO = { R.id.textTime };
  
      static final int MESSAGE_INCOMING_DIR = 1;
  
      private String incomingMessageUserName;
      private String selfUserName;
  
      public MessageAdapter(Context context, Cursor cursor) {
          super(context, R.layout.message_list_item, cursor, FROM, TO);
      }
  
      @Override
      public void bindView(View row, Context context, Cursor cursor) {
          super.bindView(row, context, cursor);
  
          int messageDir = cursor.getInt(cursor.getColumnIndex(ChatHistoryManager.C_DIR));
          if(messageDir == MESSAGE_INCOMING_DIR) {
  
              RelativeLayout.LayoutParams userNameAndChatMessageParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              userNameAndChatMessageParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
  
              RelativeLayout.LayoutParams userNameParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              userNameParams.addRule(RelativeLayout.LEFT_OF, R.id.textMessage);
  
              RelativeLayout.LayoutParams chatMessageParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              chatMessageParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, R.id.textUser);
  
              RelativeLayout.LayoutParams timeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              timeParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.userAndMessage);
              timeParams.addRule(RelativeLayout.BELOW, R.id.userAndMessage);
  
              row.setBackgroundResource(R.color.grey);
  
              // Set the chat message
              String chatMessage = cursor.getString(cursor.getColumnIndex(ChatHistoryManager.C_TEXT));
              TextView textMessage = (TextView) row.findViewById(R.id.textMessage);
              textMessage.setText(chatMessage.trim());
              textMessage.setLayoutParams(chatMessageParams);
  
              // Format the time stamp of the message
              long timestamp = cursor.getLong(cursor.getColumnIndex(ChatHistoryManager.C_TIME));
              TextView textTime = (TextView) row.findViewById(R.id.textTime);
              String readableTimeStamp = (String) DateUtils.getRelativeTimeSpanString(timestamp);
              textTime.setText(readableTimeStamp.trim());
              textTime.setLayoutParams(timeParams);
  
              // Format the message owner and the message
              TextView textUser = (TextView) row.findViewById(R.id.textUser);
              textUser.setText(incomingMessageUserName + ": ");
              textUser.setLayoutParams(userNameParams);
              row.setBackgroundResource(R.drawable.incoming_chat_bubble);
          }
          else {
              RelativeLayout.LayoutParams userNameAndChatMessageParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              userNameAndChatMessageParams.addRule(RelativeLayout.RIGHT_OF, R.id.userImage);
  
              RelativeLayout.LayoutParams userImageParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              userImageParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
  
              RelativeLayout.LayoutParams userNameParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              userNameParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.userAndMessage);
  
              RelativeLayout.LayoutParams chatMessageParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              chatMessageParams.addRule(RelativeLayout.RIGHT_OF, R.id.textUser);
  
              RelativeLayout.LayoutParams timeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
              timeParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.userAndMessage);
              timeParams.addRule(RelativeLayout.BELOW, R.id.userAndMessage);
  
              // Set the chat message
              String chatMessage = cursor.getString(cursor.getColumnIndex(ChatHistoryManager.C_TEXT));
              TextView textMessage = (TextView) row.findViewById(R.id.textMessage);
              textMessage.setText(chatMessage);
              textMessage.setLayoutParams(chatMessageParams);
  
              // Format the time stamp of the message
              long timestamp = cursor.getLong(cursor.getColumnIndex(ChatHistoryManager.C_TIME));
              TextView textTime = (TextView) row.findViewById(R.id.textTime);
              textTime.setText(DateUtils.getRelativeTimeSpanString(timestamp));
              textTime.setLayoutParams(timeParams);
  
              // Format the message owner and the message
              TextView textUser = (TextView) row.findViewById(R.id.textUser);
              textUser.setText(selfUserName + ": ");
              textUser.setLayoutParams(userNameParams);
              row.setBackgroundResource(R.drawable.outgoing_chat_bubble);
          }
      }
  
      public void setIncomingMessageUserName(String inputUserName) {
          this.incomingMessageUserName = inputUserName;
      }
  
      public void setSelfUserName(String inputUserName) {
          this.selfUserName = inputUserName;
      }
  }
  </code></pre>