Get child view size within onMeasure - android

I have listView with items created dynamicaly.Each item is my custom view and contains(from top to bottom) header,imageView and footer.I set item's height in the onMeasure method.I'd like imageView's height to be exactly
image_height=item_height-(footer_height+header_height)
I set footer/header height in the xml(constant value)
However when I try to get footer/header height it returns 0
#Override
public void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec,heightSpec);
int widthMode = MeasureSpec.getMode(widthSpec);
int widthSize = MeasureSpec.getSize(widthSpec);
int heightMode = MeasureSpec.getMode(heightSpec);
int heightSize = MeasureSpec.getSize(heightSpec);
//float ar=896f/768f;
int itemWidth=widthSize/IMGS_PER_ROW;
int calculated_width =widthSize;
int calculated_height = (int) (itemWidth*(1/IMG_ASPECT_RATIO));
if(!initialized) {
Image img=new Image(this.getContext());
int dy=((ImageView)this.findViewById(R.id.separator)).getHeight();//returns 0
Log.d("item",calculated_width+","+calculated_height+","+dy);
initialized=true;
}
this.setMeasuredDimension(calculated_width, calculated_height);
}
Item layout:
<?xml version="1.0" encoding="utf-8"?>
<com.rhyboo.net.listtest.ListItemView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:text="Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/title_text"
android:gravity="left"
android:textSize="24sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:id="#+id/items_container"
android:layout_marginBottom="30dp"
android:layout_marginTop="30dp">
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="30dp"
app:srcCompat="#color/colorAccent"
android:id="#+id/separator"
android:layout_gravity="bottom" />
</com.rhyboo.net.listtest.ListItemView>

Related

How to fill remaining space in layout without overdraw another elements?

I created a new small example to explain my new problem. In the example I created a RelativeLayout that fills the whole screen. And 3 children :
Custom view (PView) created to draw proportional view that must fill all free space by width in portrait mode and free space by height in landscape mode without overlay on top and bottom of FrameLayout (pink)
FrameLayout with id top that must show above middle (red)
FrameLayout with id bottom that must show below middle (yellow)
But now in landscape mode middle item overdraw top and bottom. Have anyone idieas how to fix it ?
layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/top"
android:layout_width="100dp"
android:layout_height="24dp"
android:layout_above="#+id/middle"
android:layout_centerHorizontal="true"
android:background="#FFFF0000" />
<test.com.heighttest.PView
android:id="#+id/middle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="#FFFF00FF" />
<FrameLayout
android:id="#+id/bottom"
android:layout_width="100dp"
android:layout_height="24dp"
android:layout_below="#+id/middle"
android:layout_centerHorizontal="true"
android:background="#FFFFFF00" />
</RelativeLayout>
PView.java
package test.com.heighttest;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
* Created by Anton Potekhin (Anton.Potekhin#gmail.com) on 26.09.16.
*/
public class PView extends RelativeLayout {
public static final int USE_FOR_SCALE_WIDTH = 0;
public static final int USE_FOR_SCALE_HEIGHT = 1;
public int getUseForScale() {
return useForScale;
}
public void setUseForScale(int useForScale) {
this.useForScale = useForScale;
}
private int useForScale = 0;
public float getAspectRatio() {
return aspectRatio;
}
public void setAspectRatio(float aspectRatio) {
this.aspectRatio = aspectRatio;
}
/**
* Aspect ratio to calculate size of view. Set 0 to not use aspect ratio (Works like RelativeLayout).
*/
private float aspectRatio = 0;
public PView(Context context) {
super(context);
}
public PView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Use aspect ratio only if it more than 0
if (aspectRatio > 0) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (useForScale == USE_FOR_SCALE_WIDTH) {
height = Math.round(width / aspectRatio);
} else {
width = Math.round(height * aspectRatio);
}
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
MainActivity.java
package test.com.heighttest;
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private PView middleView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
com.facebook.stetho.Stetho.initializeWithDefaults(this);
setContentView(R.layout.activity_main);
middleView = (PView) findViewById(R.id.middle);
middleView.setAspectRatio(1.2f);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
middleView.setUseForScale(PView.USE_FOR_SCALE_HEIGHT);
} else {
middleView.setUseForScale(PView.USE_FOR_SCALE_WIDTH);
}
}
}
Screenshot in portrait mode (Works correctly):
Screenshot in landscape mode (Works incorrect because middle overdraw top and bottom):
Screenshot what i want to get in landscape mode
Solution by #nidhi-pandya (with my fixes):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:id="#+id/top"
android:layout_width="100dp"
android:layout_height="24dp"
android:background="#FFFF0000" />
<test.com.heighttest.PView
android:id="#+id/middle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFFF00FF" />
<FrameLayout
android:id="#+id/bottom"
android:layout_width="100dp"
android:layout_height="24dp"
android:background="#FFFFFF00" />
</LinearLayout>
</RelativeLayout>
Add LinearLayout below Relativelayout and use its orientation to arrange your content vertically. Use alignparenttop, alignparentbottom and centerinparent attribute. It will give you the same output in both orientation.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:id="#+id/top"
android:layout_width="80dp"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="15dp"
android:background="#FFFF0000" />
<test.com.heighttest.PView
android:id="#+id/middle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="#FFFF00FF" />
<FrameLayout
android:id="#+id/bottom"
android:layout_width="80dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="15dp"
android:background="#FFFFFF00" />
</LinearLayout>
</RelativeLayout>

Strange Black Background after setMeasuredDimension in Custom ImageView

I have a library that allows for CircularImageView but I have a problem I can't fix.
I add it in my layout this way:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<com.mikhaellopez.circularimageview.CircularImageView
android:id="#+id/circularImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/test"
app:civ_border="true"
app:civ_border_color="#3f51b5"
app:civ_border_width="8dp"
app:civ_shadow="true"
app:civ_shadow_color="#3f51b5"
app:civ_shadow_radius="10" />
</LinearLayout>
<LinearLayout
android:layout_width="240dp"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<SeekBar-
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- ... -->
</LinearLayout>
On some picture I have a black background that appears in the view (On PNG images but ic_launcherfor example works well):
I can fix the problem when I change the onMesure method.
Before:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
int imageSize = (width < height) ? width : height;
setMeasuredDimension(imageSize, imageSize);
}
After:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
/*int imageSize = (width < height) ? width : height;
setMeasuredDimension(imageSize, imageSize);*/
setMeasuredDimension(width, height);
}
But with this modification my view isn't centered:
Could someone help me so to fix the black background or find a new way to center my view?

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:
If the message is very short, the text and time are in the same row.
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:
#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
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.
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**
txtMsgFrom.setText(Html.fromHtml(convertToHtml(txtMsgFrom.getText().toString()) + "         ")); // 10 spaces
Its easier with Unicode from here.
So with this you can archive the Unicode format
new TextView("Hello\u00A0world");
better than HTML string.
source: https://stackoverflow.com/a/6565049
Based on #Sinan Ergin answer but slightly improved:
less layouts
simplified gravity + layout_gravity usage
left + right layout files
FrameLayout instead of RelativeLayout
converted to Kotlin
no attrs.xml & style references
/**
* Layout that allows a [TextView] to flow around a [View].
*
* First child must be of type [TextView].
* Second child must be of type [View].
*/
class TextViewContainerFlowLayout #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
private val textView by lazy(NONE) { getChildAt(0) as TextView }
private val containerView by lazy(NONE) { getChildAt(1) }
private val viewPartMainLayoutParams by lazy(NONE) { textView.layoutParams as LayoutParams }
private val viewPartSlaveLayoutParams by lazy(NONE) { containerView.layoutParams as LayoutParams }
private var containerWidth = 0
private var containerHeight = 0
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
var widthSize = MeasureSpec.getSize(widthMeasureSpec)
if (widthSize <= 0) {
return
}
val availableWidth = widthSize - paddingLeft - paddingRight
val textViewWidth = textView.measuredWidth + viewPartMainLayoutParams.leftMargin + viewPartMainLayoutParams.rightMargin
val textViewHeight = textView.measuredHeight + viewPartMainLayoutParams.topMargin + viewPartMainLayoutParams.bottomMargin
containerWidth = containerView.measuredWidth + viewPartSlaveLayoutParams.leftMargin + viewPartSlaveLayoutParams.rightMargin
containerHeight = containerView.measuredHeight + viewPartSlaveLayoutParams.topMargin + viewPartSlaveLayoutParams.bottomMargin
val viewPartMainLineCount = textView.lineCount
val viewPartMainLastLineWidth = if (viewPartMainLineCount > 0) textView.layout.getLineWidth(viewPartMainLineCount - 1) else 0.0f
widthSize = paddingLeft + paddingRight
var heightSize = paddingTop + paddingBottom
if (viewPartMainLineCount > 1 && viewPartMainLastLineWidth + containerWidth < textView.measuredWidth) {
widthSize += textViewWidth
heightSize += textViewHeight
} else if (viewPartMainLineCount > 1 && viewPartMainLastLineWidth + containerWidth >= availableWidth) {
widthSize += textViewWidth
heightSize += textViewHeight + containerHeight
} else if (viewPartMainLineCount == 1 && textViewWidth + containerWidth >= availableWidth) {
widthSize += textView.measuredWidth
heightSize += textViewHeight + containerHeight
} else {
widthSize += textViewWidth + containerWidth
heightSize += textViewHeight
}
setMeasuredDimension(widthSize, heightSize)
super.onMeasure(
MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY)
)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
textView.layout(
paddingLeft,
paddingTop,
textView.width + paddingLeft,
textView.height + paddingTop
)
containerView.layout(
right - left - containerWidth - paddingRight,
bottom - top - paddingBottom - containerHeight,
right - left - paddingRight,
bottom - top - paddingBottom
)
}
}
view_chat_entry.xml
<my.ui.view.TextViewContainerFlowLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:gravity="left|center_vertical"
android:padding="12dp"
>
<my.ui.android.TextView
android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:gravity="left|top"
android:textIsSelectable="true"
tools:text="hjjfg"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="bottom"
>
<TextView
android:id="#+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:maxLines="1"
/>
<my.ui.view.ChatEntryStatusView
android:id="#+id/status"
android:layout_width="wrap_content"
android:layout_marginStart="4dp"
android:layout_height="wrap_content"
/>
</LinearLayout>
</my.ui.view.TextViewContainerFlowLayout>
view_chat_entry_status.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:layout_width="wrap_content"
tools:layout_height="wrap_content"
tools:parentTag="android.widget.FrameLayout"
>
<ImageView
android:contentDescription="#null"
android:id="#+id/statusImageView"
android:layout_width="15dp"
android:layout_height="15dp"
app:srcCompat="#drawable/ic_check_one"/>
</merge>
adapter_item_chat_left.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginEnd="64dp"
android:layout_marginTop="4dp"
android:gravity="left"
>
<include
layout="#layout/view_chat_entry"
android:id="#+id/chatEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
adapter_item_chat_right.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginStart="64dp"
android:layout_marginTop="4dp"
android:gravity="right"
>
<include
layout="#layout/view_chat_entry"
android:id="#+id/chatEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
End result (styling is done on the container with a drawable):
I propose another solution
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;
}
Two Public Methods
public abstract int getLineCount ()
Return the number of lines of text in this layout.
public int getLineWidth(int line)
Gets the unsigned horizontal extent of the specified line, including leading margin indent and trailing whitespace.
You can use the layout and code below to achieve the desired effect.
Source code gist
What I have used is get the width of the text + the time layout and check if this exceeds the container layout width, and adjust the height of the container accordingly. We have to extend from FrameLayout since this is the one which allows overlapping of two child views.
This is tested to be working on English locale. Suggestions and improvements are always welcome :)
Hope I've helped someone looking for the same solution.
I guess the easiest way to achieve this kind oflayout would be to add enough blank space in your message to be sure there is enough space on the right to not cover the time (I don't see any other easy way to have a margin/padding/positioning for the last line of your text only)
Then you just place the time in the relative as an align bottom right
layout_chat_left.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layoutChat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<RelativeLayout
android:id="#+id/message_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_toRightOf="#id/test_arrow"
android:background="#drawable/bg_msg_left"
android:paddingLeft="15dp"
android:paddingTop="5dp"
android:paddingRight="15dp"
android:paddingBottom="7dp"
tools:ignore="UselessParent">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:maxWidth="200dp"
android:text="demo Text"
android:textColor="#222"
android:textSize="17sp" />
<TextClock
android:id="#+id/msg_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/text"
android:layout_toEndOf="#+id/text"
android:text="1:30 P.M."
android:textColor="#888" />
<ImageView
android:id="#+id/is_Read_iv"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginBottom="2dp"
android:layout_marginLeft="2dp"
android:layout_alignBottom="#+id/text"
android:layout_toEndOf="#+id/msg_time"
android:src="#drawable/icon_tick"
android:tint="#color/BlueTint"/>
</RelativeLayout>
<ImageView
android:id="#+id/test_arrow"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="1dp"
android:layout_marginRight="-6dp"
android:background="#null"
android:scaleX="-1.5"
android:src="#drawable/v_bubble_corner_left" />
</RelativeLayout>
layout_chat_right.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layoutChat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<RelativeLayout
android:id="#+id/message_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_toLeftOf="#id/test_arrow"
android:background="#drawable/bg_msg_right"
android:paddingLeft="15dp"
android:paddingTop="5dp"
android:paddingRight="15dp"
android:paddingBottom="7dp"
tools:ignore="UselessParent">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:maxWidth="200dp"
android:text="demo Text"
android:textColor="#222"
android:textSize="17sp" />
<TextClock
android:id="#+id/msg_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/text"
android:layout_toEndOf="#+id/text"
android:text="1:30 P.M."
android:textColor="#888" />
<ImageView
android:id="#+id/is_Read_iv"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginBottom="2dp"
android:layout_marginLeft="2dp"
android:layout_alignBottom="#+id/text"
android:layout_toEndOf="#+id/msg_time"
android:src="#drawable/icon_tick"
android:tint="#color/BlueTint" />
</RelativeLayout>
<ImageView
android:id="#+id/test_arrow"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_marginLeft="-6dp"
android:layout_marginTop="1dp"
android:background="#null"
android:scaleX="1.5"
android:src="#drawable/v_bubble_corner_right" />
</RelativeLayout>
bg_msg_left.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_right" >-->
<solid android:color="#color/white" >
</solid>
<corners
android:topLeftRadius="0dp"
android:topRightRadius="5dp"
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp">
</corners>
</shape>
bg_msg_right.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_right" >-->
<solid android:color="#color/whatsapp_green" >
</solid>
<corners
android:topLeftRadius="5dp"
android:topRightRadius="0dp"
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp">
</corners>
</shape>
v_bubble_corner_left.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#color/white"
android:pathData="M8,5v14l11,-14z" />
</vector>
v_bubble_corner_right.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#color/whatsapp_green"
android:pathData="M8,5v14l11,-14z"/>
</vector>
And the CommentAdapter.java is
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.daimajia.androidanimations.library.Techniques;
import com.daimajia.androidanimations.library.YoYo;
import com.google.android.material.card.MaterialCardView;
import java.util.ArrayList;
import java.util.List;
public class CommentAdapter extends RecyclerView.Adapter<CommentAdapter.ViewHolder> {
private List<String> mComment;
private List<String> mTimeData;
private List<Integer> mIcon;
private List<Integer> mDirection;
private List<Integer> mRecordID;
private Context mContext;
private LayoutInflater mInflater;
private static final String TAG = "CommentAdapter";
private ItemLongClickListener mLongClickListener;
// data is passed into the constructor
CommentAdapter(Context context, List<String> dataComment, List<String> dataTimeData, List<Integer> dataDirection, List<Integer> dataRecordID) {
mContext = context;
this.mInflater = LayoutInflater.from( context );
this.mComment = dataComment;
this.mTimeData = dataTimeData;
this.mDirection = dataDirection;
this.mRecordID = dataRecordID;
}
// inflates the row layout from xml when needed
#NonNull
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == 1) {
view = mInflater.inflate( R.layout.layout_chat_left, parent, false );
} else {
view = mInflater.inflate( R.layout.layout_chat_right, parent, false );
}
return new ViewHolder( view );
}
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String mTitle = mComment.get( position );
holder.tvComment.setText( mTitle );
String mSubTitle = mTimeData.get( position );
holder.tvTime.setText( mSubTitle );
int maxWidth = mContext.getResources().getDisplayMetrics().widthPixels;
holder.layoutChat.getLayoutParams().width = maxWidth;
}
// total number of rows
#Override
public int getItemCount() {
return mComment.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
TextView tvComment;
TextView tvTime;
TextView tvSerial;
RelativeLayout layoutChat;
MaterialCardView cardView;
ViewHolder(View itemView) {
super( itemView );
tvComment = itemView.findViewById( R.id.text );
tvTime = itemView.findViewById( R.id.msg_time );
layoutChat = itemView.findViewById( R.id.layoutChat );
itemView.setOnLongClickListener( this );
}
#Override
public boolean onLongClick(View v) {
Log.d( TAG, "onLongClick: " + getAdapterPosition() );
if (mLongClickListener!=null)
mLongClickListener.onItemLongClick( v, mRecordID.get( getAdapterPosition() ) );
return true;
}
}
void setOnLongClickListener(ItemLongClickListener itemLongClickListener) {
this.mLongClickListener = itemLongClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemLongClickListener {
void onItemLongClick(View view, int position);
}
#Override
public int getItemViewType(int position) {
if (mDirection.get( position ) == 1)
return 1;
return 2;
}
}
Here are the screenshots, one from a live demo
<?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>
I suggest other solution. If you know max bubble width and time width, then you can precalculate how to place your views.
Layout:
<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>
Code:
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()
}
The previous answers didn't satisfy my needs as they were too complex and the scrolling on RecyclerView was way too slow! I could feel the stutter while scrolling. So, I modified #Rahul Shuklas answer to make it more efficient. I am sharing my result below. The code is self explanatory, I have added comments for more understandability.
class ChatBubbleLayout : FrameLayout {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
#TargetApi(21)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
doMeasure()
}
private fun doMeasure() {
val messageTextView = findViewById<TextView>(R.id.tv_message)
val dateTextView = findViewById<TextView>(R.id.tv_message_time)
// Message line count
val lineCount = messageTextView.lineCount
// Message padding
val messageTextViewPadding = messageTextView.paddingLeft + messageTextView.paddingRight
// First / Second last line of message
val lastLineStart = messageTextView.layout.getLineStart(lineCount - 1)
val lastLineEnd = messageTextView.layout.getLineEnd(lineCount - 1)
// Width of First / Second last line of message
var desiredWidth = Layout.getDesiredWidth(messageTextView.text.subSequence(lastLineStart,
lastLineEnd), messageTextView.paint).toInt()
var desiredHeight = measuredHeight
if (desiredWidth < minimumWidth && messageTextView.measuredWidth < minimumWidth) {
// Probably a small or single line message
desiredWidth = minimumWidth + messageTextViewPadding
} else {
// Probably a bit long or multiple line message
desiredWidth = messageTextView.measuredWidth + messageTextViewPadding
}
if(desiredHeight < minimumHeight) {
desiredHeight = minimumHeight
}
setMeasuredDimension(desiredWidth, desiredHeight)
}
}
My Layout XML file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<com.app.chat.ui.ChatBubbleLayout
android:id="#+id/chat_bubble_item_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/height_16dp"
android:background="#drawable/medium_green_rounded_corner"
android:minWidth="96dp"
android:minHeight="44dp">
<TextView
android:id="#+id/tv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|left"
android:autoLink="all"
android:linksClickable="true"
android:maxWidth="280dp"
android:paddingLeft="#dimen/margin_8dp"
android:paddingTop="#dimen/margin_8dp"
android:paddingRight="#dimen/margin_8dp"
android:paddingBottom="#dimen/margin_8dp"
android:text="#{chatMessageVM.iMessage.message}"
android:textColor="#color/white"
android:textColorLink="#color/white"
android:textIsSelectable="true"
android:textSize="#dimen/text_14sp"
tools:text="Nope" />
<TextView
android:id="#+id/tv_message_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|right|bottom"
android:layout_marginRight="#dimen/margin_4dp"
android:layout_marginBottom="#dimen/margin_2dp"
android:gravity="center_vertical"
android:text="#{chatMessageVM.iMessage.readableTimestamp}"
android:textColor="#color/gray_5"
android:textSize="#dimen/text_12sp"
tools:text="11:21 AM" />
</com.app.chat.ui.ChatBubbleLayout>
</LinearLayout>
I hope it helps future readers.
Here is my Layout file chat_row_right_1.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_toLeftOf="#+id/test_arrow"
android:id="#+id/message_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingBottom="7dp"
android:paddingTop="5dp"
android:paddingRight="15dp"
android:layout_marginTop="5dp"
android:maxWidth="200dp"
android:background="#drawable/layout_bg2_1"
tools:ignore="UselessParent">
<TextView
android:layout_marginEnd="10dp"
android:id="#+id/text"
android:text="demo Text"
android:textColor="#222"
android:textSize="17sp"
android:layout_width="wrap_content"
android:maxWidth="200dp"
android:layout_height="wrap_content" />
<TextClock
android:id="#+id/msg_time"
android:layout_toEndOf="#+id/text"
android:layout_alignBottom="#+id/text"
android:text="1:30 P.M."
android:textColor="#888"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="#+id/is_Read_iv"
android:layout_toEndOf="#+id/msg_time"
android:layout_alignBottom="#+id/text"
android:layout_width="wrap_content"
android:src="#drawable/ic_done_black_24dp"
android:layout_height="wrap_content" />
</RelativeLayout>
<ImageView
android:id="#+id/test_arrow"
android:layout_alignParentRight="true"
android:layout_width="20dp"
android:background="#null"
android:layout_marginTop="-2dp"
android:layout_marginLeft="-8dp"
android:layout_height="wrap_content"
android:src="#drawable/ic_play_arrow_black_24dp"/>
</RelativeLayout>
And here is ic_right_bubble.xml file in drawable folder
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#cfc"
android:pathData="M8,5v14l11,-14z"/>
</vector>
You will get exact same as WhatsApp See screenshot

Wrap_content doesn't work on RelativeLayout : Action bar custom View

I have created a custom layout for the Action Bar : which I add with this code on my Activity :
LayoutInflater inflater = (LayoutInflater)getSystemService (Context.LAYOUT_INFLATER_SERVICE);
View actionBarView = inflater.inflate (R.layout.main_actionbar, null);
getSupportActionBar ().setCustomView (actionBarView);
and the main_actionbar.xml layout :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:style="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white" >
<RelativeLayout
android:id="#+id/leftIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toLeftOf="#+id/empty"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:background="#drawable/uiclickable_gray_states"
android:layout_alignParentLeft="true" >
<ImageView
android:id="#+id/back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_marginRight="5dp"
android:src="#drawable/icon_back" />
<!-- The problem is here -->
<ImageView
android:id="#+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="#+id/back"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:src="#drawable/bg"
android:scaleType="fitXY"
android:adjustViewBounds="true" />
</RelativeLayout>
<View
android:id="#+id/empty"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#color/black_transparent"
android:layout_alignParentRight="true" />
</RelativeLayout>
My problem is that I would like the #+id/thumbnail to have it's height as match_parent and have it's width equals the height. means : height == match_parent AND width == height, so I can have a square Image.
it appears that this cannot be done by xml, so I created (with the help of some tutorials on the net) a subclass of ImageView and did this but without any positive results :
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
//int width = measureWidth (widthMeasureSpec);
int height = measureHeight (heightMeasureSpec);
setMeasuredDimension (height, height);
}
private int measureHeight (int measureSpecHeight) {
int result;
int specMode = MeasureSpec.getMode (measureSpecHeight);
int specSize = MeasureSpec.getSize (measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
// The child can be as large as it wants up to the specified size.
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = canvasSize;
}
return (result + 2);
}
Am I doing something wrong there ? Thank you.

I want to set max height in dialog fragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:minWidth="#dimen/dialog_min_width"
android:padding="#dimen/dialog_padding"
android:background="#drawable/dialog_background" >
<TextView android:id="#+id/base_dialog_title"
style="#style/DialogTitleTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="center" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="16dp" >
</FrameLayout>
<!-- *********************** HERE ************************* -->
<FrameLayout android:id="#+id/base_dialog_content"
android:background="#drawable/dialog_description_background"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
<!-- *********************** HERE ************************* -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="16dp" >
</FrameLayout>
<LinearLayout android:id="#+id/base_dialog_button_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="#+id/base_dialog_button_negative"
style="?android:attr/buttonStyleSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"/>
<Button
android:id="#+id/base_dialog_button_neutral"
style="?android:attr/buttonStyleSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"/>
<Button
android:id="#+id/base_dialog_button_positive"
style="?android:attr/buttonStyleSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
This is dialog fragment layout.
I add content view (TextView or ListView) into FrameLayout (#id/base_dialog_content)
When ListView has many Items, Dialog is full height in window.
I want to set maximum height dialog or content view (or list view)
Edit:
I solve the problem using OnLayoutChangeListener.
But I need same function in lower version (below HoneyComb)
In DialogFragment.onCreateView()
FrameLayout contentContainer = (FrameLayout) view.findViewById(R.id.base_dialog_content);
final View contentView = createContentView(inflater, contentContainer);
contentContainer.addView(contentView);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// TODO Check content view height and change height
} else {
view.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
int height = v.getHeight();
int contentHeight = contentView.getHeight();
int winHeight = ActivityUtil.getDisplayHeight(getSherlockActivity());
LogUtils.pe(height+" / "+winHeight+" / "+contentHeight);
int needHeight = height - winHeight*8/10;
if (needHeight > 0) {
contentView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, contentHeight-needHeight));
}
}
});
}
to control the maximum height you have to put the content view in one layout container first (Linear/Relative) let's say in my example I'll use linearlayout
to measure the screen height (I put it in Util.java ):
public static int getDisplayHeight(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int height = display.getHeight();
return height;
}
after that you create a container :
LinearLayout llContainerContent = new LinearLayout(context);
LayoutParams lpContainerContent = new LayoutParams(LayoutParams.FILL_PARENT, (Util.getDisplayHeight(context) * 8) /10);
llContainerContent.setLayoutParams(lpContainerContent);
after that you add the textview/listview to the container then you add the container to the framelayout. I hope my answer is clear enough for you but if you have another question please feel free to ask in the comment :)
Use this code for full screen height and width for the dialog
WindowManager window = (WindowManager)context
.getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
displayheight = display.getHeight();
displaywidth = display.getWidth();
dialog.getWindow().setLayout(displaywidth ,
displayheight );
what about using the onResume to get it done:
override fun onResume() {
super.onResume()
val width = (resources.displayMetrics.widthPixels * 0.95).toInt()
val height = (resources.displayMetrics.heightPixels * 0.60).toInt()
dialog?.window?.setLayout(width, height)
}

Categories

Resources