自定义控件FlowTextLayout
开发工具:Android Studio 4.0.2
文件名称:FlowTextLayout.java
package com.hl.maya.views;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.hl.maya.R;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义控件
*/
public class FlowTextLayout extends ViewGroup {
public static final String TAG = "FlowTextLayout";
private List<Line> mLines = new ArrayList<>();
private Line mCurrentLineCursor;
private int mHorizontalSpace = 20;
private int mVerticalSpace = 20;
private OnFlowTextItemClickListener mListener;
private List<String> mTexts = null;
public FlowTextLayout(Context context) {
super(context,null);
}
public FlowTextLayout(Context context, AttributeSet attrs) {
super(context, attrs,0);
}
public FlowTextLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 设置控件的样式
* @param texts
*/
public void setTextContents(List<String> texts){
this.mTexts = texts;
for (String text : texts) {
final TextView item = new TextView(getContext());
item.setText(text);
item.setBackground(getResources().getDrawable(R.drawable.shape_flow_text_bg));
item.setPadding(40,10,40,10);
//设置点击事件
item.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mListener != null){
mListener.onFlowTextItemClick(item.getText().toString());
}
}
});
item.setTextColor(Color.parseColor("#888585"));
item.setTextSize(18);
item.setGravity(Gravity.CENTER);
addView(item);
}
}
public void setSpace(int horizontalSpace,int verticalSpace){
this.mHorizontalSpace = horizontalSpace;
this.mVerticalSpace = verticalSpace;
}
/**
* 测量
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mLines.clear();
mCurrentLineCursor = null;
//获取宽度
int layoutWidth = MeasureSpec.getSize(widthMeasureSpec);
//计算最大宽度
int maxLineWidth = layoutWidth - getPaddingLeft() - getPaddingRight();
//先测量孩子
int count = getChildCount();
//如果子view 的数量为0 就不再需要继续测量
//if (count == 0) return;
for (int i=0;i<count;i++){
View view = getChildAt(i);
//如果子 View 不可见,停止这次view 测量
if(view.getVisibility() == View.GONE){
continue;
}
//测量孩子
measureChild(view,widthMeasureSpec,heightMeasureSpec);
//往Lines添加孩子
if(mCurrentLineCursor == null){
mCurrentLineCursor = new Line(maxLineWidth,mHorizontalSpace);
//添加到line中
mLines.add(mCurrentLineCursor);
//行中一个孩子都没有
mCurrentLineCursor.addView(view);
} else {
boolean canAdd = mCurrentLineCursor.canAdd(view);
if (canAdd) {
//可以添加
mCurrentLineCursor.addView(view);
} else {
//新建行
mCurrentLineCursor = new Line(maxLineWidth,mHorizontalSpace);
//添加到line中
mLines.add(mCurrentLineCursor);
//将view添加到line中
mCurrentLineCursor.addView(view);
}
}
}
//设置自己的宽度和高度
int measureWidth = layoutWidth;
float allHeight = 0;
for (int i = 0; i< mLines.size();i++) {
float mHeight = mLines.get(i).mHeight;
allHeight += mHeight;
//加间距
if (i != 0) {
allHeight += mVerticalSpace;
}
}
int measuredHeight = (int)(allHeight + getPaddingTop() + getPaddingBottom() + 0.5f);
setMeasuredDimension(measureWidth,measuredHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int paddingLeft = getPaddingLeft();
int offestTop = getPaddingTop();
for (int i=0;i<mLines.size();i++){
Line line = mLines.get(i);
//给行布局
line.layout(paddingLeft,offestTop);
offestTop += line.mHeight + mVerticalSpace;
}
}
/**
* 对外提供点击事件的接口
* @param listener
*/
public void setOnFlowTextItemClickListener(OnFlowTextItemClickListener listener){
this.mListener = listener;
}
/**
* 设置点击事件接口
*/
public interface OnFlowTextItemClickListener{
void onFlowTextItemClick(String text);
}
private class Line{
//属性
private List<View> mViews = new ArrayList<View>();
private float mMaxWidth;
private float mUsedWidth;
private float mHeight;
private float mMarginLeft;
private float mMarginRight;
private float mMarginTop;
private float mMarginBottom;
private float mHorizontalSpace;
public Line(int maxWidth,int horizontalSpace){
this.mMaxWidth = maxWidth;
this.mHorizontalSpace = horizontalSpace;
}
/**
* 添加view 记录属性的变化
* @param view
*/
public void addView(View view){
//加载view的方法
int size = mViews.size();
int viewWidth = view.getMeasuredWidth();
int viewHeight = view.getMeasuredHeight();
//计算高和宽
if (size == 0){
//还没有添加view
if(viewWidth > mMaxWidth){
mUsedWidth = mMaxWidth;
} else {
mUsedWidth = viewWidth;
}
mHeight = viewHeight;
} else {
//多个view的情况下
mUsedWidth += viewWidth + mHorizontalSpace;
mHeight = mHeight < viewHeight ? viewHeight : mHeight;
}
//将view记录到集合中
mViews.add(view);
}
/**
* 给孩子设置布局
* @param paddingLeft
* @param offestTop
*/
public void layout(int paddingLeft, int offestTop){
int currentLeft = paddingLeft;
int size = mViews.size();
//判断已经使用的额宽度是否小于最大的宽度
float extra = 0;
float widthAvg = 0;
if(mMaxWidth > mUsedWidth){
extra = mMaxWidth - mUsedWidth;
widthAvg = extra/size;
}
for (int i=0; i<size;i++) {
View view = mViews.get(i);
int viewWidth = view.getMeasuredWidth();
int viewHeight = view.getMeasuredHeight();
//判断是否有富余
if(widthAvg != 0){
//改变宽度
int newWidth = (int) (viewWidth + widthAvg + 0.5f);
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth,MeasureSpec.AT_MOST);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(viewHeight,MeasureSpec.EXACTLY);
view.measure(widthMeasureSpec,heightMeasureSpec);
viewWidth = view.getMeasuredWidth();
viewHeight = view.getMeasuredHeight();
}
//布局
int left = currentLeft;
int top = (int) (offestTop + (mHeight - viewHeight)/2 + 0.5f);
//int top = offsetTop;
int right = left + viewWidth;
int bottom = top + viewHeight;
view.layout(left,top,right,bottom);
currentLeft += viewWidth + mHorizontalSpace;
}
}
/**
* 用来判断是否可以将view 添加到line中
* @param view
* @return
*/
public boolean canAdd(View view){
//判断瑟吉欧否能添加view
int size = mViews.size();
if(size == 0){
return true;
}
int viewWidth = view.getMeasuredWidth();
//预计使用的宽度
float planWidth = mUsedWidth + mHorizontalSpace + viewWidth;
if(planWidth > mMaxWidth){
//不能添加到line中
return false;
} else {
return true;
}
}
}
}
控件背景 shape_flow_text_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android" >
<corners android:radius="20dp"/>
<stroke android:width="1dp" android:color="#888585"/>
</shape>
使用代码
布局文件中调用 activity.xml
<com.hl.maya.views.FlowTextLayout
android:id="@+id/ftl_search"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
private FlowTextLayout mSearchFlowText;
//初始化控件
mSearchFlowText = this.findViewById(R.id.ftl_search);
//设置数据
mSearchFlowText.setTextContents(hotwords);
//设置点击事件
//点击热词的时候执行的事件
mSearchFlowText.setOnFlowTextItemClickListener(new FlowTextLayout.OnFlowTextItemClickListener() {
@Override
public void onFlowTextItemClick(String text) {
Toast.makeText(SearchActivity.this,text,Toast.LENGTH_SHORT).show();
}
});