1,自定义View的分类
自定义View:没有现成的View,需要我们自己实现,就需要创建自定义View,自定义View一般继承View,SurfaceView或者其他View。
自定义ViewGroup:一般是利用现有的组件根据特定的方式组合成新的组件,实现需要的功能,一般继承自ViewGroup或者layout。
2,自定义View的绘制流程
onMeasure(),onlayout(),onDraw()
onMeasure()测量View的大小
onlayout()确定子View的布局
onDraw()实际进行绘制
自定义View主要实现onMeasure()和onDraw方法
自定义ViewGroup主要实现onMeasure和onlayout()
MeasureSpace是View的内部类,基本都是二进制运算,由于int是32位,用高两位表示mode,低30位表示size,MODE_SHIFT = 30的作用是移位,Mode的类型有三种:UPSPACEFILED,EXACTLY,AT_MOST。
UPSPACEFILED:不对view的大小做限制,系统中用
EXACTLY:父容器已经得到自己View确切的大小
AT_MOST:父容器指定一个大小,子View最大不能超过指定大小,eg:machParent,最大不能超过machParent
MeasureSpace和dp之间的转换
所有的View测量子View时最终都会调用
MeasureSpec.makeMeasureSpec(.......)
getMeasureWith和getWith
getMeasure在measure过程结束后就可以获取对应的值,通过setMeasureDimension方法进行设置
getWith在layout结束后才能获取到,通过视图右边的坐标减去左边的坐标计算出来
3,View的层级关系
4,Android中的两种坐标系 android中的屏幕坐标
视图坐标系
5,实现一个自定义空间
想要实现的效果
实现代码
package com.example.gshg;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class FlowLayout extends ViewGroup {
private static final String Tag = "FlowLayout";
private int mHorizontalSpacing = dp2px(16);
private int mVerticalSpacing = dp2px(16);
private List<List<View>> allLines; //记录所有的行一行一行的存储,用于layout方法
private List<Integer> lineHeights = new ArrayList<>(); //记录每一行的行高
//在代码中new FlowLayout()的时候调用
public FlowLayout(Context context) {
super(context);
}
//在xml中使用时调用
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
//设置主题时会调用这个构造方法
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//布局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int lineCount = allLines.size();
int curL = getPaddingLeft();
int curT = getPaddingTop();
for (int i = 0; i < lineCount; i++){
List<View> lineViews = allLines.get(i);
int height = 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();
curL = right + mHorizontalSpacing;
}
curL = getPaddingLeft();
curT = curT + height + mVerticalSpacing;
}
}
//度量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initMeasure();
//度量子View的大小
int childCount = getChildCount();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
List<View> lineViews = new ArrayList<>(); //保存所有的子view
int mLineWithUsed = 0; //记录已经使用的行宽
int mLineHeightUsed = 0;
int selfWith = MeasureSpec.getSize(widthMeasureSpec); //ViewGroup中父控件提供的宽度
int selfHeight = MeasureSpec.getSize(heightMeasureSpec); //viewGroup中父空间提供的宽度
int parentNeedWith = 0; // measure 过程中子View要求父View的宽
int parentNeedHeight = 0; // measure 过程中子View要求父View的高
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
LayoutParams childLp = childView.getLayoutParams();
int childWithMeasureSpace = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, childLp.width);
int childHeightMeasureSpace = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, childLp.height);
childView.measure(childWithMeasureSpace, childHeightMeasureSpace);
//View是分行layout的,所以要记录一行有那些View,方便布局layout
lineViews.add(childView);
//获取子View的宽和高
int childMeasuredWith = childView.getMeasuredWidth();
int childMeasureHeight = childView.getMeasuredHeight();
//计算每一行的宽高
mLineWithUsed = mLineWithUsed + childMeasuredWith + mHorizontalSpacing;
mLineHeightUsed = Math.max(mLineHeightUsed, childMeasureHeight);
//通过宽度确定是否需要换行,通过换行后没每行的行高来获取整个viewGroup的行高
if (childMeasuredWith + mLineWithUsed + mHorizontalSpacing > selfWith) {
//一旦换行后,我们就可以判断当前行需要的宽和高,因此可以记录下来
parentNeedWith = Math.max(parentNeedWith, mLineWithUsed + mHorizontalSpacing);
parentNeedHeight = parentNeedHeight + mLineHeightUsed + mVerticalSpacing;
lineViews = new ArrayList<>();
mLineWithUsed = 0;
mLineHeightUsed = 0;
}
}
//度量自己的大小
//根据子View的宽高,重新度量自己ViewGroup
//作为一个ViewGroup他也是一个View,它的宽高也需要根据它父亲给他提供的宽高
int withMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWith = withMode == MeasureSpec.EXACTLY ? selfWith : parentNeedWith;
int realHeight = heightMode == MeasureSpec.EXACTLY ? selfHeight: parentNeedHeight;
setMeasuredDimension(realWith, realHeight);
}
private static int dp2px(int dp){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}
private void initMeasure(){
lineHeights = new ArrayList<>();
allLines = new ArrayList<>();
}
}