Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Whatsapp Message Layout - How to get time-view in the same row

I was wondering how WhatsApp handles the time shown in every message.

For those who don't know:

  1. If the message is very short, the text and time are in the same row.
  2. If the message is long, the time is in the bottom right corner - the text wrapped around it.

With a RelativeLayout and toLeftOf I would get 1) but not 2) as previous lines would be "cut off" at the position of the time view. Same behaviour If i use a LinearLayout.

So I tried to use a FrameLayout or a RelativeLayout without any connection between text and time.

However, if the text is as long as the message-view is big, both views would overlap. If I put blank characters to my message I wouldn't have the time on the right.

Do they really have some kind of text-wrapping-lib for this or is it possible to do only with layouts?

Here is the requested screenshot:

enter image description here

like image 960
Frame91 Avatar asked May 11 '15 13:05

Frame91


People also ask

Which layout is used by WhatsApp?

They use LinearLayout as FrameLayout.

How can I show date on WhatsApp Android?

How can I show dates in an app when I scroll the ListView like in WhatsApp? When I scroll the ListView, the date gets displayed over the list. If you still did not understand my question please open your WhatsApp, go to any group & start scrolling: you will see the date getting displayed for older texts.

How do I Auto Read WhatsApp messages?

Simply tap the Mark as read option and your friend will see their sent messages as read. Once you mark a message as read it will disappear from the notification center. However, you can still reply to messages from the notification.


2 Answers

@Hisham Muneer 's answer very good.

But there are some problems. For example:

  • If the TextView has 2 full lines (end to end), the text will intersect with datetime text layout. Finally, the views will look like onion effect.
  • The text line wraps can't works efficiently. You must control this lines and relocate the datetime view.

I'm going to share my solution, if you will need like this problem.

This is example screenshot Example screenshot

ImFlexboxLayout.java

    public class ImFlexboxLayout extends RelativeLayout {     private TextView viewPartMain;     private View viewPartSlave;      private TypedArray a;      private RelativeLayout.LayoutParams viewPartMainLayoutParams;     private int viewPartMainWidth;     private int viewPartMainHeight;      private RelativeLayout.LayoutParams viewPartSlaveLayoutParams;     private int viewPartSlaveWidth;     private int viewPartSlaveHeight;       public ImFlexboxLayout(Context context) {         super(context);     }      public ImFlexboxLayout(Context context, AttributeSet attrs) {         super(context, attrs);          a = context.obtainStyledAttributes(attrs, R.styleable.ImFlexboxLayout, 0, 0);     }      @Override     protected void onAttachedToWindow() {         super.onAttachedToWindow();          try {             viewPartMain = (TextView) this.findViewById(a.getResourceId(R.styleable.ImFlexboxLayout_viewPartMain, -1));             viewPartSlave = this.findViewById(a.getResourceId(R.styleable.ImFlexboxLayout_viewPartSlave, -1));         } catch (Exception e) {             e.printStackTrace();         }     }      @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(widthMeasureSpec, heightMeasureSpec);          int widthSize = MeasureSpec.getSize(widthMeasureSpec);         int heightSize = MeasureSpec.getSize(heightMeasureSpec);          if (viewPartMain == null || viewPartSlave == null || widthSize <= 0) {             return;         }          int availableWidth = widthSize - getPaddingLeft() - getPaddingRight();         int availableHeight = heightSize - getPaddingTop() - getPaddingBottom();          viewPartMainLayoutParams = (LayoutParams) viewPartMain.getLayoutParams();         viewPartMainWidth = viewPartMain.getMeasuredWidth() + viewPartMainLayoutParams.leftMargin + viewPartMainLayoutParams.rightMargin;         viewPartMainHeight = viewPartMain.getMeasuredHeight() + viewPartMainLayoutParams.topMargin + viewPartMainLayoutParams.bottomMargin;          viewPartSlaveLayoutParams = (LayoutParams) viewPartSlave.getLayoutParams();         viewPartSlaveWidth = viewPartSlave.getMeasuredWidth() + viewPartSlaveLayoutParams.leftMargin + viewPartSlaveLayoutParams.rightMargin;         viewPartSlaveHeight = viewPartSlave.getMeasuredHeight() + viewPartSlaveLayoutParams.topMargin + viewPartSlaveLayoutParams.bottomMargin;          int viewPartMainLineCount = viewPartMain.getLineCount();         float viewPartMainLastLineWitdh = viewPartMainLineCount > 0 ? viewPartMain.getLayout().getLineWidth(viewPartMainLineCount - 1) : 0;          widthSize = getPaddingLeft() + getPaddingRight();         heightSize = getPaddingTop() + getPaddingBottom();          if (viewPartMainLineCount > 1 && !(viewPartMainLastLineWitdh + viewPartSlaveWidth >= viewPartMain.getMeasuredWidth())) {             widthSize += viewPartMainWidth;             heightSize += viewPartMainHeight;         } else if (viewPartMainLineCount > 1 && (viewPartMainLastLineWitdh + viewPartSlaveWidth >= availableWidth)) {             widthSize += viewPartMainWidth;             heightSize += viewPartMainHeight + viewPartSlaveHeight;         } else if (viewPartMainLineCount == 1 && (viewPartMainWidth + viewPartSlaveWidth >= availableWidth)) {             widthSize += viewPartMain.getMeasuredWidth();             heightSize += viewPartMainHeight + viewPartSlaveHeight;         } else {             widthSize += viewPartMainWidth + viewPartSlaveWidth;             heightSize += viewPartMainHeight;         }          this.setMeasuredDimension(widthSize, heightSize);         super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));     }      @Override     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {         super.onLayout(changed, left, top, right, bottom);          if (viewPartMain == null || viewPartSlave == null) {             return;         }          viewPartMain.layout(                 getPaddingLeft(),                 getPaddingTop(),                 viewPartMain.getWidth() + getPaddingLeft(),                 viewPartMain.getHeight() + getPaddingTop());          viewPartSlave.layout(                 right - left - viewPartSlaveWidth - getPaddingRight(),                 bottom - top - getPaddingBottom() - viewPartSlaveHeight,                 right - left - getPaddingRight(),                 bottom - top - getPaddingBottom());     } } 

attrs.xml

<?xml version="1.0" encoding="utf-8"?> <resources>     <declare-styleable name="ImFlexboxLayout">         <attr name="viewPartMain" format="reference"></attr>         <attr name="viewPartSlave" format="reference"></attr>     </declare-styleable>  </resources> 

Example right ballon layout (balloon.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:baselineAligned="false"     android:gravity="center_vertical"     android:orientation="horizontal">      <LinearLayout         android:layout_width="0dp"         android:layout_height="wrap_content"         android:layout_gravity="right|center_vertical"         android:layout_weight="1"         android:gravity="right">          <tr.com.client.ImFlexboxLayout             android:id="@+id/msg_layout"             style="@style/BalloonMessageLayoutRight"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:layout_gravity="right|bottom"             android:gravity="left|center_vertical"             app:viewPartMain="@+id/chat_msg"             app:viewPartSlave="@+id/lytStatusContainer">              <TextView                 android:id="@+id/chat_msg"                 style="@style/BalloonMessageRightTextItem"                 android:layout_width="wrap_content"                 android:layout_gravity="right|bottom"                 android:focusableInTouchMode="false"                 android:gravity="left|top"                 android:text="hjjfg" />              <LinearLayout                 android:id="@+id/lytStatusContainer"                 android:layout_width="wrap_content"                 android:layout_height="wrap_content"                 android:layout_marginLeft="5dp"                 android:gravity="right"                 android:minWidth="60dp">                  <TextView                     android:id="@+id/date_view"                     style="@style/BallonMessageTimeText"                     android:layout_alignParentRight="true"                     android:layout_gravity="right|bottom"                     android:layout_marginRight="5dp"                     android:gravity="right"                     android:maxLines="1" />                  <include                     android:id="@+id/lytStatus"                     layout="@layout/layout_im_message_status"                     android:layout_width="wrap_content"                     android:layout_height="wrap_content"                     android:layout_gravity="bottom"                     android:layout_marginRight="5dp"                     android:minWidth="40dp" />              </LinearLayout>          </tr.com.client.ImFlexboxLayout>     </LinearLayout> </LinearLayout> 

You can modify layout xml and some sections related your scenario.

There are 2 important point: you must define in layout xml "viewPartMain", "viewPartSlave" attributes. Because the code will decide measure via your main(chat textview) and slave(datetime text view) elements.

I wish have good days. Greets.

like image 173
The Goat Avatar answered Sep 28 '22 09:09

The Goat


Adding HTML non-breaking spaces did the trick. Tested the code on most devices and working absolutely fine. Maybe whatsapp is also doing the same thing. Below is the chat code:

See images below to see it working.

XML Design:

<RelativeLayout     xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/rel_layout_left"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_below="@+id/txtDate"     android:visibility="visible"     android:orientation="vertical"    >      <TextView         android:id="@+id/lblMsgFrom"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:padding="5dp"         android:text="kfhdjbh"         android:textColor="@color/lblFromName"         android:textSize="12dp"         android:textStyle="italic"         android:visibility="gone" />      <ImageView         android:id="@+id/imageView"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_alignParentLeft="true"         android:layout_alignParentStart="true"         android:layout_below="@+id/lblMsgFrom"         android:layout_marginRight="-5dp"         android:src="@drawable/bubble_corner" />      <FrameLayout         android:orientation="horizontal"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_alignParentTop="true"         android:background="@drawable/bg_msg_from"         android:layout_toRightOf="@+id/imageView">          <TextView             android:id="@+id/txtTimeFrom"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:paddingRight="@dimen/d5"             android:text="Time"             android:textColor="@android:color/darker_gray"             android:layout_gravity="bottom|right"             android:padding="4dp"             android:textSize="10dp"             android:textStyle="italic"             android:layout_below="@+id/txtMsgFrom"             android:layout_alignRight="@+id/txtMsgFrom"             android:layout_alignEnd="@+id/txtMsgFrom" />         <TextView             android:id="@+id/txtMsgFrom"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:layout_alignTop="@+id/imageView"             android:layout_toEndOf="@+id/lblMsgFrom"             android:layout_toRightOf="@+id/imageView"             android:paddingLeft="10dp"             android:paddingRight="10dp"             android:paddingTop="5dp"             android:paddingBottom="5dp"             android:text="kdfjhgjfhf"             android:textColor="@color/black"             android:textSize="16dp"             android:layout_alignParentLeft="true"             android:layout_marginLeft="0dp"             android:layout_alignParentTop="true"             android:layout_marginTop="0dp"             android:layout_gravity="left|center_vertical" />     </FrameLayout>  </RelativeLayout> 

Code: bg_msg_from.xml

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"     android:shape="rectangle" >      <!-- view background color -->     <!--<solid android:color="@color/bg_msg_from" >-->     <solid android:color="@android:color/white" >     </solid>      <corners android:radius="@dimen/d5" >     </corners>  </shape> 

** File: bubble_corner.png**

Right Arrow Image enter image description here

enter image description hereenter image description hereenter image description here

txtMsgFrom.setText(Html.fromHtml(convertToHtml(txtMsgFrom.getText().toString()) + " &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;")); // 10 spaces 
like image 32
Hisham Muneer Avatar answered Sep 28 '22 11:09

Hisham Muneer