转载请注明原创出处,谢谢!
- GitHub: @Ricco

效果图
整体思路:
1、自定义viewGroup,控制子view显示的位置。
2、当没有达到maxLines时,判断文本加图片能不能显示下,如果显示下,放到后面,显示不下放到下一行。
3、当达到maxLines时,for循环最后一行每个文字的位置,计算显示followView的位置,让省略号紧跟文字。
缺点:
1、因为是采用覆盖的方式,所以需要给FollowTextLayout的第二个子view设置背景色,否则会把下面的文字显示出来。
2、布局必须是FollowTextLayout下面2个view,第一个必须是textview,第二个子view(我用的FrameLayout)也必须有2个子view(一个textTiew用来显示省略号,一个imageView用来显示图片)。
ps:注释应该算很详细吧
package com.example.myapplication;
import android.content.Context;
import android.support.annotation.Nullable;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* 图片跟随文字的Layout
*/
public class FollowTextLayout extends ViewGroup {
private static final int CHILD_COUNT = 2; // 必须只能有2个子view
public FollowTextLayout(Context context) {
super(context);
}
public FollowTextLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (getChildCount() == CHILD_COUNT && getChildAt(0) instanceof TextView) {
int width = r - l;
// TextView
TextView textView = (TextView) getChildAt(0);
int textViewWidth = textView.getMeasuredWidth();
int textViewHeight = textView.getMeasuredHeight();
// 跟随view
ViewGroup followView = (ViewGroup) getChildAt(1);
int followViewWidth = followView.getMeasuredWidth();
int followViewHeight = followView.getMeasuredHeight();
// 省略号,默认隐藏
View ellipsisView = followView.getChildAt(0);
int ellipsisViewWidth = ellipsisView.getMeasuredWidth();
ellipsisView.setVisibility(View.GONE);
// TextView正常layout
textView.layout(0, 0, textViewWidth, textViewHeight);
// 最后一行的宽度
int lastLineWidth = (int) textView.getLayout().getLineWidth(textView.getLineCount() - 1);
// 最后一行 + 跟随view > 宽度
if (lastLineWidth + followViewWidth > width) {
if (textView.getLineCount() < textView.getMaxLines()) {
// 没达到最大行数,但是放不下跟随view,把跟随view放到文本的下一行
followView.layout(0,
textViewHeight,
followViewWidth,
textViewHeight + followViewHeight);
} else {
// 显示省略号,跟随view固定到最后
showEllipsis(width,
textView,
textViewHeight,
followView, followViewWidth, ellipsisView, ellipsisViewWidth);
}
} else {
if (textView.getLineCount() <= textView.getMaxLines()) {
// 最后一行的宽度,上一行文本的高度,最后一行的宽度+跟随view的宽度,文本的高度
followView.layout(lastLineWidth,
textViewHeight / textView.getLineCount() * (textView.getLineCount() - 1),
followViewWidth + lastLineWidth,
textViewHeight);
} else {
// 显示省略号,跟随view固定到最后
showEllipsis(width,
textView,
textViewHeight,
followView, followViewWidth, ellipsisView, ellipsisViewWidth);
}
}
}
}
/**
* 显示省略号,并且将跟随view固定到最后
*/
private void showEllipsis(int width, TextView textView, int textViewHeight, ViewGroup followView, int followViewWidth, View ellipsisView, int ellipsisViewWidth) {
// 显示省略号,跟随view固定到最后
ellipsisView.setVisibility(View.VISIBLE);
Layout layout = textView.getLayout();
// 获取最后一行显示的内容
String lastLineStr = textView.getText().toString().substring(layout.getLineStart(textView.getMaxLines() - 1));
// 计算省略号开始的位置
float left = 0;
for (int i = 0; i < lastLineStr.length(); i++) {
float desiredWidth = Layout.getDesiredWidth(lastLineStr.substring(0, i), textView.getPaint());
if (desiredWidth + ellipsisViewWidth + followViewWidth > width) {
break;
}
left = desiredWidth;
}
// 跟随view显示到最后
followView.layout((int) left, textViewHeight / textView.getMaxLines() * (textView.getMaxLines() - 1), width, textViewHeight);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 必须是2个子控件,第一个必须是TextView
if (getChildCount() == CHILD_COUNT && getChildAt(0) instanceof TextView) {
// TextView
TextView textView = (TextView) getChildAt(0);
measureChild(textView, widthMeasureSpec, heightMeasureSpec);
int textViewWidth = textView.getMeasuredWidth();
int textViewHeight = textView.getMeasuredHeight();
// 跟随的view,followView
ViewGroup followView = (ViewGroup) getChildAt(1);
measureChild(followView, widthMeasureSpec, heightMeasureSpec);
int followWidth = followView.getMeasuredWidth();
int followHeight = followView.getMeasuredHeight();
// 跟随view,也 必须是2个子控件
if (followView.getChildCount() == CHILD_COUNT) {
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int destWidth;
int destHeight;
// 文字的宽度+跟随view的宽度超过最大宽度,1行显示不下
if (textViewWidth + followWidth > maxWidth) {
// 最后一行宽度
float lastLineWidth = textView.getLayout().getLineWidth(textView.getLineCount() - 1);
// 没有达到最后一行,但是最后一行也放不下跟随view
if (textView.getLineCount() != textView.getMaxLines() && lastLineWidth + followWidth > maxWidth) {
destWidth = maxWidth; // 最大的宽度
destHeight = textViewHeight + followHeight; // 文字的高度+跟随view的宽度
} else {
destWidth = maxWidth; // 最大的宽度
destHeight = textViewHeight; // 文字的高度
}
} else {
// 没有超过最大的宽度,1行显示的下
destHeight = textViewHeight; // 文字的高度
destWidth = textViewWidth + followWidth; // 文字的宽度+跟随view的宽度
}
setMeasuredDimension(destWidth, destHeight);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.myapplication.FollowTextLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="12313123123123"
android:textSize="14sp" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:text="..."
android:textSize="14sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="12dp"
android:layout_gravity="center_vertical|end"
android:adjustViewBounds="true"
android:contentDescription="@null"
android:src="@drawable/start3" />
</FrameLayout>
</com.example.myapplication.FollowTextLayout>
</LinearLayout>