前言:
自定义View与自定义ViewGroup的区别:
- 自定义View:在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View,SurfaceView或其他的View。这个是控件。
- 自定义ViewGroup:一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自ViewGroup或各种Layout。这个是组件。
自定义View的绘制流程图如下:
下面来实现流式布局,定义一个类FlowLayout继承自ViewGroup,重写里面的相关方法,实现流式布局的效果。
public class FlowLayout extends ViewGroup {
//每个item横向间距
private int mHorizontalSpacing = dp2px(16);
//每个item竖向间距
private int mVerticalSpacing = dp2px(8);
//记录所有的行,一行一行的存储,用于Layout
private List<List<View>> allLines;
//记录每一行的行高,用于Layout
List<Integer> lineHeights = new ArrayList<>();
//new FlowLayout(传入上下文)
public FlowLayout(Context context) {
super(context);
}
//在xml中使用 在xml转化为java代码的时候,通过反射调用有两个参数的构造函数
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
//代码中有不同的主题style,调用有三个参数的构造函数
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
//进行集合的初始化
private void initMeasureParams() {
if (allLines != null) {
allLines.clear();
} else {
allLines = new ArrayList<>();
}
if (lineHeights != null) {
lineHeights.clear();
} else {
lineHeights = new ArrayList<>();
}
}
//度量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//每次进入onMeasure()方法中 都需要将全局变量重新初始化
initMeasureParams();
//度量子View的宽度和高度
int childCount = getChildCount();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
//ViewGroup解析的宽度
int selfWidth = MeasureSpec.getSize(widthMeasureSpec);
//ViewGroup解析的高度
int selfHeight = MeasureSpec.getSize(heightMeasureSpec);
//保存一行中的所有View
List<View> lineViews = new ArrayList<>();
//记录这一行已经使用了多宽的size
int lineWidthUsed = 0;
//一行的行高
int lineHeight = 0;
//measure过程中,子View要求的父ViewGroup的宽
int parentNeededWidth = 0;
//measure过程中,子View要求的父ViewGroup的高
int parentNeededHeight = 0;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
LayoutParams childLP = childView.getLayoutParams();
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, childLP.width);
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingBottom + paddingTop, childLP.height);
childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
//获取子View的宽高
int childMeasuredWidth = childView.getMeasuredWidth();
int childMeasuredHeight = childView.getMeasuredHeight();
//通过宽度来判断是否需要换行,通过换行后的每行的行高来获取
//整个ViewGroup的行高
if (childMeasuredWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
allLines.add(lineViews);
lineHeights.add(lineHeight);
//一旦换行,我们就可以判断当前列需要的宽和高了,所以此时需要记录下来
parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing;
parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing);
lineViews = new ArrayList<>();
lineWidthUsed = 0;
lineHeight = 0;
}
//view是分行layout的,所以要记录每一行有哪些View,这样可以方便布局
lineViews.add(childView);
//每行都会有自己的宽和高
lineWidthUsed = lineWidthUsed + childMeasuredWidth + mHorizontalSpacing;
lineHeight = Math.max(lineHeight, childMeasuredHeight);
}
//根据子View的度量结果,来重新度量自己的ViewGroup
//作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;
int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
//度量自己的宽度和高度
setMeasuredDimension(realWidth, realHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//获取Layout中的行数
int lineCount = allLines.size();
int curL = getPaddingLeft();
int curT = getPaddingTop();
for (int i = 0; i < lineCount; i++) {
List<View> lineViews = allLines.get(i);
int lineHeight = lineHeights.get(i);
for (int j = 0; j < lineViews.size(); j++) {
View view = lineViews.get(j);
int left = curL;
int top = curT;
int right = left + view.getMeasuredWidth();
int bottom = top + view.getMeasuredHeight();
view.layout(left, top, right, bottom);
curL = right + mHorizontalSpacing;
}
curL = getPaddingLeft();
curT = lineHeight + curT + mVerticalSpacing;
}
}
//dp转为px
public static int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}
}
之后我们在xml中,进行引用,就可以了。
<?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=".ViewGroupActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="搜索历史"
android:textColor="@color/black"
android:textSize="17sp" />
<com.example.animationtest.view.FlowLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="水果味孕妇奶粉"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童洗衣机"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="洗衣机全自动"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="手机"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童汽车可坐人111111111111111111"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="抽真空收纳袋"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童滑板车"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="稳定器 电容"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童洗衣机"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="衣服"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="运动鞋"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="手表"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="水果味孕妇奶粉"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童洗衣机"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="洗衣机全自动"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="手机"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="抽真空收纳袋"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童滑板车"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="稳定器 电容"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童洗衣机"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="衣服"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="运动鞋"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="手表"
android:textColor="@color/black"
android:textSize="16sp" />
</com.example.animationtest.view.FlowLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="搜索发现"
android:textColor="@color/black"
android:textSize="17sp" />
<com.example.animationtest.view.FlowLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="水果味孕妇奶粉"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童洗衣机"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="洗衣机全自动"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="手机"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童汽车可坐人111111111111111111"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="抽真空收纳袋"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童滑板车"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="稳定器 电容"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="儿童洗衣机"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="衣服"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="运动鞋"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tv_back_color"
android:text="手表"
android:textColor="@color/black"
android:textSize="16sp" />
</com.example.animationtest.view.FlowLayout>
</LinearLayout>
运行后,效果如下:
|