看了大锯哥自定义View中的FlowLayout的视频
FlowLayout中的代码
package com.example.customview.customview.flow;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.InputFilter;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.customview.R;
import com.example.customview.customview.utils.SizeUtils;
import java.util.ArrayList;
import java.util.List;
public class FlowLayout extends ViewGroup {
public static final int DEFAULT_LINE = -1;
//后需要转单位,目前是px,不适配
//默认水平间距
public static final int DEFAULT_HORIZONTAL_MARGIN = SizeUtils.dip2px(5f);//将5dp转成px格式
//默认垂直间距
public static final int DEFAULT_VERTICAL_MARGIN = SizeUtils.dip2px(5f);
public static final int DEFAULT_TEXT_LENGTH = -1;
public static final int DEFAULT_Border_RADIUS = SizeUtils.dip2px(5f);
private static final String TAG = "FlowLayout";
private int mMaxLines;
//水平间距
private int mHorizontalMargin;
private float mVerticalMargin;
private int mTextMaxLength;
private int mTextColor;
private int mBorderColor;
private float mBorderRadius;
private List<String> mData = new ArrayList<>();
private OnItemClickListener mItemClickListener = null;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取R.styleable.FlowLayout对应的属性
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
mMaxLines = a.getInt(R.styleable.FlowLayout_maxLines, DEFAULT_LINE);
if(mMaxLines!=-1&&mMaxLines<1) {
throw new IllegalArgumentException("mMaxLine can not less then 1.");
}
mHorizontalMargin = (int)a.getDimension(R.styleable.FlowLayout_itemHorizontalMargin, DEFAULT_HORIZONTAL_MARGIN);
mVerticalMargin = a.getDimension(R.styleable.FlowLayout_itemVerticalMargin, DEFAULT_VERTICAL_MARGIN);
mTextMaxLength = a.getInt(R.styleable.FlowLayout_textMaxLength, DEFAULT_TEXT_LENGTH);
if(mTextMaxLength!=DEFAULT_TEXT_LENGTH&&mTextMaxLength<0){
Log.d(TAG, "FlowLayout: mTextMaxLength!=DEFAULT_TEXT_LENGTH&&mTextMaxLength<0"+"经来了");
throw new IllegalArgumentException("text length must be max than 0");
}
mTextColor = a.getColor(R.styleable.FlowLayout_textColor, getResources().getColor(R.color.text_grey));
mBorderColor = a.getColor(R.styleable.FlowLayout_borderColor, getResources().getColor(R.color.text_grey));
mBorderRadius = a.getDimension(R.styleable.FlowLayout_borderRadius, DEFAULT_Border_RADIUS);
//回收,释放资源
a.recycle();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "onLayout:getMeasuredWidth "+getMeasuredWidth());
View firstChild = getChildAt(0);
Log.d(TAG, "onLayout:firstChild "+firstChild);
int currentLeft = (int) mHorizontalMargin + getPaddingLeft();
int currentTop = (int) mVerticalMargin+getPaddingTop();
int currentRight = (int) mHorizontalMargin + getPaddingLeft();
int currentBottom =firstChild.getMeasuredHeight()+(int) mVerticalMargin+getPaddingTop();
for(List<View> line:mLines){
for(View view:line){
//布局每一行
int width = view.getMeasuredWidth();
currentRight+=width;
//判断最右边的边界
Log.d(TAG, "onLayout: getMeasuredWidth"+getMeasuredWidth());
//因为我们默认第一个搜索记录是添加进来的,并没有进行CanBeAdd判断,所以可能在第一次的时候如果
//长度过长右边界为出界,所有进行判断
if(currentRight > getMeasuredWidth()-mHorizontalMargin-getPaddingLeft()){
Log.d(TAG, "onLayout: "+"经来了");
currentRight=getMeasuredWidth()-mHorizontalMargin-getPaddingLeft();
}
Log.d(TAG, "onLayout: "+"出来了");
view.layout(currentLeft,currentTop,currentRight,currentBottom);
currentLeft=currentRight+(int) mHorizontalMargin;
currentRight+=(int) mHorizontalMargin;
}
currentLeft=(int) mHorizontalMargin+getPaddingLeft();
currentRight=(int) mHorizontalMargin+getPaddingRight();
currentBottom+=firstChild.getMeasuredHeight()+(int) mVerticalMargin;
currentTop+=firstChild.getMeasuredHeight()+(int) mVerticalMargin;
}
}
private List<List<View>> mLines = new ArrayList<>();
/**
* 这两个值来自于父控件,包含模式和值
* 在本例子当中就来源于LinerLayout
* int类型=====>4字节=====>4*8 bit=32位
* 将高位的两位做成模式,后面的做成值
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int mode = MeasureSpec.getMode(widthMeasureSpec);
int mode1 = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//mode取决于父控件给的widthMeasureSpec,heightMeasureSpec
//当然我们也可以不遵守,自己去设置值和模式
Log.d(TAG, "mode===>" + mode);
Log.d(TAG, "mode1====>" + mode1);
Log.d(TAG, "widthSize===>" + widthSize);
Log.d(TAG, "heightSize==>" + heightSize);
Log.d(TAG, "AT_MOST===>" + MeasureSpec.AT_MOST);
Log.d(TAG, "EXACTLY===>" + MeasureSpec.EXACTLY);
Log.d(TAG, "UNSPECIFIED===>" + MeasureSpec.UNSPECIFIED);
Log.d(TAG, "getMeasuredWidth: "+getMeasuredWidth());//获取FlowLayout布局的宽
Log.d(TAG, "getMeasuredHeight: "+getMeasuredHeight());
Log.d(TAG, "=====================");
int parentWidthSize = MeasureSpec.getSize(widthMeasureSpec);
int parentHeightSize = MeasureSpec.getSize(heightMeasureSpec);
int childCount = getChildCount();
if (childCount == 0) {
//如果孩子的个数为0,不用再浪费资源往下走,直接return
return;
}
//先清空
mLines.clear();
//添加默认行
List<View> line = new ArrayList<>();
mLines.add(line);
for (int i = 0; i < childCount; i++) {
//获取所有孩子
View child = getChildAt(i);
if (child.getVisibility() != VISIBLE) {
continue;
}
measureChild(child,widthMeasureSpec,heightMeasureSpec);
Log.d(TAG, "child.getMeasuredWidth()---->" + child.getMeasuredWidth());
Log.d(TAG, "child.getMeasuredHeight()---->" + child.getMeasuredHeight());
//测量子view后就可以将其进行添加和判断了
if (line.size() == 0) {
//可以添加
line.add(child);
} else {
//判断是否可以添加到当前行
boolean canBeAdd = checkChildCanBeAdd(line, child, parentWidthSize);
if(!canBeAdd){
//设置最大行数,如果超过最大行数直接跳出循环,不进行添加
if(mMaxLines!=-1&&mLines.size()>=mMaxLines){
break;
}
line=new ArrayList<>();
mLines.add(line);
}
line.add(child);
}
}
//根据尺寸计算所有行高
View child = getChildAt(0);
int childHeight = child.getMeasuredHeight();
int parentHeightTargetSize = ((int) mVerticalMargin + childHeight) * mLines.size()+getPaddingTop()+getPaddingBottom();
//Log.d(TAG, "onMeasure: mLines.size()--->"+mLines.size());mLines.size()--->1
//测量自己(设置FlowLayout布局的宽度和高度)
//parentWidthSize我们遵守xml布局设置的machParent,但是parentHeightTargetSize我们没有遵守
setMeasuredDimension(parentWidthSize, parentHeightTargetSize);
}
private boolean checkChildCanBeAdd(List<View> line, View child, int parentWidthSize) {
//获取孩子的宽度
int measuredWidth = child.getMeasuredWidth();
Log.d(TAG, "checkChildCanBeAdd: measuredWidth--->" + measuredWidth);
int totalWidth = (int) getPaddingLeft();
//原来没加上之前
for (View view : line) {
Log.d(TAG, "view.getMeasuredWidth()--->" + view.getMeasuredWidth());
totalWidth += view.getMeasuredWidth() + mHorizontalMargin;
}
//加上当前的
totalWidth += measuredWidth + mHorizontalMargin + getPaddingRight();
//如果超出限制宽度,则不可以继续添加
//否则可以添加
return totalWidth <= parentWidthSize;
}
public void setTextList(List<String> data) {
this.mData.clear();
this.mData.addAll(data);
//根据数据创建子view,并且添加进来
setupChildren();
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mItemClickListener = listener;
}
public interface OnItemClickListener {
void onItemClickListener(View v, String text);
}
private void setupChildren() {
//清空原来的内容
this.removeAllViews();
//添加子view进来
for (String datum : mData) {
//TextView textView = new TextView(getContext());
TextView textView = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.item_flow_text, this, false);
if(mTextMaxLength!=DEFAULT_TEXT_LENGTH){
//设置TextView的最长内容长度
textView.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mTextMaxLength)});
}
textView.setText(datum);
//设置TextView的相关属性,边距,颜色,border之类的...
//匿名内部类,可以使用final,保证至始至终都是一个变量
final String tempData = datum;
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClickListener(v, tempData);
}
}
});
addView(textView);
}
}
//暴露出去给别人设置
public int getMaxLines() {
return mMaxLines;
}
public void setMaxLines(int maxLines) {
mMaxLines = maxLines;
}
public float getHorizontalMargin() {
return mHorizontalMargin;
}
public void setHorizontalMargin(int horizontalMargin) {
mHorizontalMargin = SizeUtils.dip2px(horizontalMargin);
}
public float getVerticalMargin() {
return mVerticalMargin;
}
public void setVerticalMargin(float verticalMargin) {
mVerticalMargin = verticalMargin;
}
public int getTextMaxLength() {
return mTextMaxLength;
}
public void setTextMaxLength(int textMaxLength) {
mTextMaxLength = textMaxLength;
}
public int getTextColor() {
return mTextColor;
}
public void setTextColor(int textColor) {
mTextColor = textColor;
}
public int getBorderColor() {
return mBorderColor;
}
public void setBorderColor(int borderColor) {
mBorderColor = borderColor;
}
public float getBorderRadius() {
return mBorderRadius;
}
public void setBorderRadius(float borderRadius) {
mBorderRadius = borderRadius;
}
}
MainActivity代码
package com.example.customview;
import androidx.appcompat.app.AppCompatActivity;
import com.example.androidflowlayoutlibrary.MyFlowLayout;
import com.example.customview.customview.flow.FlowLayout;
import com.example.customview.customview.utils.SizeUtils;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FlowLayout flowLayout = findViewById(R.id.flow_layout);
List<String> data=new ArrayList<>();
data.add("春夏秋冬超帅装大声的等等等等等等等ad等等我就等等等等可能");
data.add("显示器");
data.add("鼠标");
data.add("iPad");
data.add("air addaccchhhhassbbbbcioadjsaidjasdsdsadasdAAAKkdjsdkjakdsdsddsada");
data.add("mac pro");
data.add("耳机");
data.add("春夏秋冬超帅装大声的等等等等等春夏秋冬超帅装春夏秋冬超帅装春夏秋冬超帅装是啊");
data.add("春夏秋冬超帅装");
data.add("春夏秋冬超帅装");
data.add("春夏秋冬超帅装");
data.add("男鞋");
data.add("女装");
flowLayout.setTextList(data);
flowLayout.setMaxLines(100);
}
}

问题是当我添加的数据很长的时候TextView不是设置了android:singleLine="true"android:ellipsize="end"吗?这个时候如果过长后面正常应该有三个省略号,但是测试的时候有的时候是三个省略号,两个,也有时候没有,不知道是啥原因,怎么固定后面过长就是三个省略号?请知道的大佬帮忙一下
自定义 超过一行 直接拼... 因为那是textview自动给你计算的 你有时候是字母xxsas有时候文字 占的像素不一样 这需求估计得自定义view了