web-dev-qa-db-ja.com

Whatsappメッセージレイアウト-同じ行でタイムビューを取得する方法

WhatsAppがすべてのメッセージに表示される時間をどのように処理するのかと思っていました。

知らない人のために:

  1. メッセージが非常に短い場合、テキストと時間は同じ行にあります。
  2. メッセージが長い場合、時間は右下隅にあります-テキストがその周りに折り返されています。

RelativeLayouttoLeftOfを使用すると、1)が得られますが、2)ではなく、タイムビューの位置で前の行が「カットオフ」されます。 LinearLayoutを使用すると同じ動作になります。

そこで、テキストと時間との間に関係なく、FrameLayoutまたはRelativeLayoutを使用しようとしました。

ただし、メッセージビューが大きい限りテキストが長い場合、両方のビューが重なります。メッセージに空白文字を入力すると、右側に時間がありません。

彼らは本当にこれのために何らかの種類のtext-wrapping-libを持っていますか、それともレイアウトのみで行うことは可能ですか?

要求されたスクリーンショットは次のとおりです。

enter image description here

39
Frame91

HTMLの改行なしスペースを追加することでうまくいきました。ほとんどのデバイスでコードをテストし、正常に動作しました。たぶん、whatsappも同じことをしています。チャットコードは次のとおりです。

以下の画像を参照して、動作を確認してください。

XMLデザイン:

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

コード: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>

**ファイル:bubble_corner.png **

Right Arrow Imageenter 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
32
Hisham Muneer

@Hisham Muneerの答えはとても良い。

しかし、いくつかの問題があります。例えば:

  • TextViewに2行(エンドツーエンド)がある場合、テキストはdatetimeテキストレイアウトと交差します。最後に、ビューはタマネギ効果のように見えます。
  • テキスト行の折り返しは効率的に機能しません。この行を制御し、日時ビューを再配置する必要があります。

この問題が必要な場合は、ソリューションを共有します。

これはスクリーンショットの例です 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>

右バルーンレイアウトの例(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>

レイアウトXMLおよびシナリオに関連するいくつかのセクションを変更できます。

2つの重要なポイントがあります:レイアウトxmlで定義する必要があります "viewPartMain" "viewPartSlave"属性。コードは、main(chat textview)およびslave(datetime text view)要素を介してメジャーを決定するためです。

良い日をお過ごしください。あいさつ。

26
Sinan Ergin

here のUnicodeを使用すると簡単です。

これで、Unicode形式をアーカイブできます

 new TextView("Hello\u00A0world");

hTML文字列よりも優れています。

ソース: https://stackoverflow.com/a/6565049

4
Kelvin Wong

私は別の解決策を提案します

public static final String TAG = "MainActivity";
    private TextView mText;
    private RelativeLayout relativeLayout;
    private Boolean mFirstTime = true;
    private static final int WIDH_HOUR = 382;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        final int width = getScreensWidh();

        mText = (TextView) findViewById(R.id.activity_main_text);
        relativeLayout = (RelativeLayout) findViewById(R.id.activity_main_relative);

        mText.setText("aaaaa dfsafsa afdsfa fdsafas adfas fdasf adfsa dsa aaaa dfsafsa afdsfa fdsafas adfas fdasf adfsa");

        ViewTreeObserver vto = mText.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (mFirstTime) {
                    Layout layout = mText.getLayout();
                    int lines = layout.getLineCount();

                    int offset = layout.layout.getLineWidth(lines - 1);
                    int freeSpace = width - offset;

                    TextView hour = new TextView(MainActivity.this);
                    hour.setText("12:20");
                    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
                    params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                    if (freeSpace > WIDH_HOUR) {
                        params.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.activity_main_text);
                    } else {
                        params.addRule(RelativeLayout.BELOW, R.id.activity_main_text);
                    }
                    hour.setLayoutParams(params);
                    relativeLayout.addView(hour);
                    Log.d(TAG, String.valueOf(freeSpace));
                    mFirstTime = false;
                }

            }
        });


    }

    public int getScreensWidh() {
        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        return size.x;

    }

2つのパブリックメソッド

このレイアウトのテキストの行数を返します。

先行マージンのインデントおよび後続の空白を含む、指定された行の符号なし水平範囲を取得します。

2
Cabezas

以下のレイアウトとコードを使用して、目的の効果を実現できます。 ソースコードの要点

私が使用したのは、テキストの幅+時間レイアウトを取得し、これがコンテナレイアウトの幅を超えているかどうかを確認し、それに応じてコンテナの高さを調整することです。 FrameLayoutから拡張する必要があるのは、これが2つの子ビューのオーバーラップを許可するためです。

これは、英語ロケールで動作することがテストされています。提案や改善はいつでも歓迎です:)

同じソリューションを探している人を助けてくれることを願っています。

2
Rahul Shukla

この種のレイアウトを実現する最も簡単な方法は、メッセージに十分な空白スペースを追加して、右側に時間をカバーしない十分なスペースがあることを確認することだと思います(他の簡単な方法でマージン/テキストの最後の行のパディング/配置のみ)次に、右下に揃えて相対的な時間を配置します

1
Xavier Falempin

他の解決策をお勧めします。最大バブル幅と時間幅がわかっている場合は、ビューの配置方法を事前に計算できます。

レイアウト:

<RelativeLayout
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content">

    <TextView
        Android:id="@+id/text"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:textSize="12sp"
        tools:text="This is text"/>

    <TextView
        Android:id="@+id/time"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:textSize="10sp"
        tools:text="10:10"/>
</RelativeLayout>

コード:

fun setTextAndTime(textView: TextView, timeView: TextView, text: String, time: String) {
    // screen width - offset from bubble
    val maxWidth: Int = Resources.getSystem().displayMetrics.widthPixels - context.resources.getDimensionPixelSize(R.dimen.bubble_offset)
    val timeWidth: Int = getTextWidth(time, 10f)

    textView.text = text
    timeView.text = time

    textView.measure(makeMeasureSpec(maxWidth, EXACTLY), makeMeasureSpec(0, UNSPECIFIED))
    val offset = textView.layout.getLineWidth(textView.layout.lineCount - 1)
    val freeSpace = maxWidth - offset

    val moveTimestampBelow = freeSpace < timeWidth
    val multilineContent = textView.layout.lineCount > 1

    val params = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT)
    when {
        moveTimestampBelow -> params.apply {
            addRule(RelativeLayout.BELOW, textView.id)
            addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        }
        multilineContent -> params.apply {
            params.addRule(RelativeLayout.ALIGN_BOTTOM, textView.id)
            addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        }
        else -> params.apply {
            params.addRule(RelativeLayout.ALIGN_BOTTOM, textView.id)
            addRule(RelativeLayout.END_OF, textView.id)
        }
    }
    timeView.layoutParams = params
}

private fun getTextWidth(text: String, textSizeSp: Float): Int {
    val textPaint = Paint()
    val pxSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSizeSp, context.resources.displayMetrics)
    textPaint.textSize = pxSize
    textPaint.style = Paint.Style.FILL
    val result = Rect()
    textPaint.getTextBounds(text, 0, text.length, result)
    return result.width()
}
0
northerngirl
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/rel_layout_left"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:background="@drawable/bubble1"
    Android:orientation="vertical">

    <TextView
        Android:id="@+id/lblMsgFrom"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Person Name or Id"           
        Android:visibility="gone" />   

    <TextView
        Android:id="@+id/lblMessage_text"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:paddingBottom="5dp"
        Android:paddingLeft="10dp"
        Android:paddingRight="10dp"
        Android:paddingTop="5dp"
        Android:text="Sample \n Sample2 Sample2 Sample2 Sample2 Sample2 Sample2 Sample2 Sample2 Sample2 \n Sample2"
        Android:textSize="16dp" />

    <TextView
        Android:id="@+id/lblMessage_Time"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_alignEnd="@+id/lblMessage_text"
        Android:layout_alignRight="@+id/lblMessage_text"
        Android:layout_below="@+id/lblMessage_text"
        Android:text="04:50 Am"
        Android:textColor="@Android:color/darker_gray"
        Android:textSize="10dp"
        Android:textStyle="italic" />    

</RelativeLayout>
0
lucky