Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

android - 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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

@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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...