自定义分为自定义View与自定义布局:
-
需要掌握的技术:
- onMeasure 父子组件大小计算
- onLayout 组件排版
- onDraw
-
知识点:
- getMeasureWidth()与getMeasureHeight() 在View没有展示完全获取
- getWidth()与getHeight()在View展示后才能获取
- MeasureSpec.EXACTLY 等三种类型区别
- 通过getChildMeasureSpec将子view的宽高转为MeasureSpec
-
自定义布局用到前两个,我们以流式布局为例实现自定义布局:
实现:
思路:
1.onMeasure中计算子View 大小
2.右子View 大小计算出父View真是宽高
3.记录所有行的View 与高度
4.在Onlayout中排版
- 创建 LiuShiLayout 继承 ViewGroup
package com.myself.alamdding;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class LiuShiLayout extends ViewGroup {
int hWidth = 100; // 每一行的宽度间隔
int vHeight = 100; // 每一行的高度间隔
private List<List<View>> listListLine = new ArrayList<>();// 记录所有行的信息
private List<Integer> listHeight = new ArrayList<>();// 记录每一行的行高
public LiuShiLayout(Context context) {
super(context);
}
public LiuShiLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LiuShiLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public LiuShiLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private void initParams(){
listListLine = new ArrayList<>();// 记录所有行的信息
listHeight = new ArrayList<>();// 记录每一行的行高
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
initParams();
// 计算自己宽高
int lineWidth = 0; // 每一行的宽度
int lineHeight = 0;// 每一行的高度
int parentWith = 0;// 所有行一共宽度
int parentHeight = 0;// 所有行一共高度
List<View> lineViewArr = new ArrayList<>();// 每一行存储的view
int selfWidth = MeasureSpec.getSize(widthMeasureSpec);// 容器宽度
int selfHeight = MeasureSpec.getSize(heightMeasureSpec);// 容器高度
// 计算子view 大小
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
LayoutParams layoutParams = view.getLayoutParams();
int childMeasureSpecWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft+paddingRight, layoutParams.width);
int childMeasureSpecHeight = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, layoutParams.height);
view.measure(childMeasureSpecWidth,childMeasureSpecHeight);
// 获取子view 一行宽度和一行最大高度
int childWidth = view.getMeasuredWidth();
int childHeight = view.getMeasuredHeight();
if((lineWidth + childWidth + hWidth)> selfWidth||lineHeight==0){// 换行
listListLine.add(lineViewArr);// 记录每一行的view
listHeight.add(parentHeight);
// 换行之前统计一下宽高度
parentWith = Math.max(parentWith, lineWidth + hWidth);
parentHeight = parentHeight + lineHeight + vHeight;
lineWidth = 0;
lineHeight = 0;
lineViewArr = new ArrayList<>();
}
lineViewArr.add(view);
lineWidth = lineWidth + childWidth + hWidth;
lineHeight = Math.max(lineHeight,childHeight);
}
// 计算自己大小->根据子view
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// 如果父亲传过来的是确切的size就用自己,不是就用计算的
listListLine.add(lineViewArr);// 记录每一行的view
listHeight.add(parentHeight);
parentWith = Math.max(parentWith, lineWidth + hWidth);
parentHeight = parentHeight + lineHeight + vHeight;
int realWidth = (widthMode==MeasureSpec.EXACTLY)?selfWidth:parentWith;
int realHeight = (heightMode==MeasureSpec.EXACTLY)?selfHeight:parentHeight;
setMeasuredDimension(realWidth,realHeight);
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
int curLeft = getPaddingLeft();
int curTop = getPaddingTop();
for (int j = 0; j < listListLine.size(); j++) {
int lineHeight = listHeight.get(j);
List<View> viewList = listListLine.get(j);
for (int k = 0; k < viewList.size(); k++) {
View view = viewList.get(k);
int left = curLeft;
int top = curTop;
int right = left + view.getMeasuredWidth();
int bottom = top + view.getMeasuredHeight();
view.layout(left,top,right,bottom);
curLeft = right + hWidth;// 画完一个View,位置右移动画下一个
}
curLeft = getPaddingLeft();
curTop = curTop + lineHeight + vHeight;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
XML 实现:
<com.myself.alamdding.LiuShiLayout
android:layout_width="match_parent"
android:background="@color/colorPrimaryDark"
android:layout_marginTop="20dp"
android:layout_height="wrap_content">
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:text="北京"
android:background="@color/colorPrimary"/>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:text="石家庄"
android:background="@color/colorPrimary"/>
</com.myself.alamdding.LiuShiLayout>
网友评论