WhatsAppがすべてのメッセージに表示される時間をどのように処理するのかと思っていました。
知らない人のために:
RelativeLayout
とtoLeftOf
を使用すると、1)が得られますが、2)ではなく、タイムビューの位置で前の行が「カットオフ」されます。 LinearLayout
を使用すると同じ動作になります。
そこで、テキストと時間との間に関係なく、FrameLayout
またはRelativeLayout
を使用しようとしました。
ただし、メッセージビューが大きい限りテキストが長い場合、両方のビューが重なります。メッセージに空白文字を入力すると、右側に時間がありません。
彼らは本当にこれのために何らかの種類のtext-wrapping-libを持っていますか、それともレイアウトのみで行うことは可能ですか?
要求されたスクリーンショットは次のとおりです。
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>
<?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>
txtMsgFrom.setText(Html.fromHtml(convertToHtml(txtMsgFrom.getText().toString()) + "         ")); // 10 spaces
@Hisham Muneerの答えはとても良い。
しかし、いくつかの問題があります。例えば:
この問題が必要な場合は、ソリューションを共有します。
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)要素を介してメジャーを決定するためです。
良い日をお過ごしください。あいさつ。
私は別の解決策を提案します
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つのパブリックメソッド
このレイアウトのテキストの行数を返します。
先行マージンのインデントおよび後続の空白を含む、指定された行の符号なし水平範囲を取得します。
以下のレイアウトとコードを使用して、目的の効果を実現できます。 ソースコードの要点
私が使用したのは、テキストの幅+時間レイアウトを取得し、これがコンテナレイアウトの幅を超えているかどうかを確認し、それに応じてコンテナの高さを調整することです。 FrameLayoutから拡張する必要があるのは、これが2つの子ビューのオーバーラップを許可するためです。
これは、英語ロケールで動作することがテストされています。提案や改善はいつでも歓迎です:)
同じソリューションを探している人を助けてくれることを願っています。
この種のレイアウトを実現する最も簡単な方法は、メッセージに十分な空白スペースを追加して、右側に時間をカバーしない十分なスペースがあることを確認することだと思います(他の簡単な方法でマージン/テキストの最後の行のパディング/配置のみ)次に、右下に揃えて相対的な時間を配置します
他の解決策をお勧めします。最大バブル幅と時間幅がわかっている場合は、ビューの配置方法を事前に計算できます。
レイアウト:
<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()
}
<?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>