赞
踩
一,自定义view理论:
自定义view三部曲:onMeasure() onLayout() onDraw()
自定义view可以粗略的分为两类,1,自定义View,一般继承自View,主要实现onMeasure()与onDraw()方法,2,继承ViewGroup,主要实现onMeasure()与onLayout()方法
自定义View的生命周期:构造函数(view的初始化)onMeasure()(测量view的大小) onChangeSize()(确定view的大小)onLayout()(确定子view的布局,包含子view的使用)onDraw()(实际绘制View)
什么时MeasureSpec?
MeasureSpec 是View的内部类,基本都是二进制运算,由于int是32位的,用高两位表示mode,低30位表示size,MODE_SHIFT = 30的作用是位移,是父容器给你的度量之后的大小的确定
分为三种情况:1,UNSPECIFIED:不对view大小做限制,基本在系统中使用
2,EXACTLY:父容器已经得到自己view的大小
AT_MOST:父容器指定一个大小,孩子不可超过这个值,如:matchParent,最大不能超过父容器
getMeasureWidth()是在measure()过程结束后就可以获取对应的至,是通过setMeasuredDimension()来进行设置的,
getWidth()在layout()过程结束之后才能获取到,是通过视图右边的坐标减去左边的坐标计算出来的,
二自定义View实战---流式布局实现
- package com.example.viewdemo.view;
-
- import android.content.Context;
- 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 mHorSpacing = dp2px(16);
- private int mVerSpacing = dp2px(8);
-
- //用来记录所用行的view与每一行的行高,用于layout布局使用
- private List<List<View>> allLineViews ;
- List<Integer> lineHeights ;
- public FlowLayout(Context context) {
- super(context);
- }
-
- //反射
- public FlowLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- //主题style
- public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- /**
- * 初始化总记录,
- */
- private void initMeasureParams(){
- lineHeights = new ArrayList<>();
- allLineViews = new ArrayList<>();
- }
-
- //度量
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- /**
- * 可能会多次调用,在此初始化
- */
- initMeasureParams();
- //度量孩子大小
- int childCunt = getChildCount();
-
- // 父布局的padding
- int paddingLeft = getPaddingLeft();
- int paddingRight = getPaddingRight();
- int paddingTop = getPaddingTop();
- int paddingBottom = getPaddingBottom();
-
- //用来保存一行中所有的子View
- List<View> lineViews = new ArrayList<>();
- //记录这行已经使用多宽的size;
- int lineWidthUsed = 0;
- //一行的高度
- int lineHeigth = 0;
-
- //这是viewGroup解析的父控件给的宽高
- int selfWidth = MeasureSpec.getSize(widthMeasureSpec);
- int selfHeigth = MeasureSpec.getSize(heightMeasureSpec);
-
- //measure的过程中,子view要求的父ViewGroup 的宽高即,子view累计的宽高,本控件需要的宽高
- int parentNeededWidth = 0;
- int parentNeededHeight = 0;
-
- for (int i=0;i<childCunt;i++){
- View childView = getChildAt(i);
- //获取子控件的布局参数,layoutParams为布局参数
- LayoutParams childLp = childView.getLayoutParams();
-
- //将布局参数转变为具体的size大小 传参分别是 父布局widthMeasureSpec 父布局padding,及子布局宽高
- int childWidthMusSpc = getChildMeasureSpec(widthMeasureSpec,paddingLeft+paddingRight,childLp.width);
- int childHeightMusSpc = getChildMeasureSpec(widthMeasureSpec,paddingTop+paddingBottom,childLp.height);
- //给子view设置度量
- childView.measure(childWidthMusSpc,childHeightMusSpc);
-
- //获取子布局的宽高
- int childMeasureWidth = childView.getMeasuredWidth();
- int childMeasureHeight = childView.getMeasuredHeight();
-
- /**
- * 通过狂赌来判断是否换行,通过换行后每行的行高来获取整个view Group的行高
- * 因为一旦换行说明这一行的高度就确定了
- */
- if (childMeasureWidth+lineWidthUsed+mHorSpacing>selfWidth){
-
- //记录每一行的view与行高,用于layout布局
- allLineViews.add(lineViews);
- lineHeights.add(lineHeigth);
- //总行高就等于之前行高+本次子行高+行间距
- parentNeededHeight = parentNeededWidth+lineHeigth+mVerSpacing;
- //总行宽就等于本次之前行宽与本次行宽+子view的列间距对比最大值
- parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed+mHorSpacing);
-
- //换行后清空行记录的view 及本行的宽高,进行记录下一行的宽高
- lineViews = new ArrayList<>();
- lineWidthUsed = 0;
- lineHeigth = 0;
- }
-
- //view 是分行的layout,所以要记录每一行的view,这样可以方便layout布局
- lineViews.add(childView);
- //记录每一行的宽和高,每一行都会有自己的宽高
- lineWidthUsed = lineWidthUsed+childMeasureWidth+mHorSpacing;
- //因为子view的高度不确定,所以行高度自己的高度与子view的高度取最大值
- lineHeigth = Math.max(lineHeigth,childMeasureHeight);
- }
-
- /**
- * 因为不确定布局文件给到的宽高方式(例:)
- * 要根据不同的情况,需要度量自己的大小
- */
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int realWidth = (widthMode==MeasureSpec.EXACTLY)?selfWidth:parentNeededWidth;
- int realHeigth = (heightMode==MeasureSpec.EXACTLY)?selfHeigth:parentNeededHeight;
-
- //度量自己的宽高
- setMeasuredDimension(realWidth,realHeigth);
- }
-
- //布局
- @Override
- protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
-
- int lineCount = allLineViews.size();
- int curL = getPaddingLeft();
- int curT = getPaddingTop();
-
- for (int j=0;j<lineCount;j++){
- int lineHeight = lineHeights.get(i);
- List<View> lineViews = allLineViews.get(j);
- for (int k=0;k<lineViews.size();k++){
- View view = lineViews.get(i);
- int left = curL;
- int top = curT;
- /**
- * 因为getWidth()需要执行完onLayout()才会有值
- * 所以不能用下面的等式
- */
- // int right = left+view.getWidth();
- // int bottom = top+view.getHeight();
- /**
- * 正确的方式
- */
- int right = left+view.getMeasuredWidth();
- int bottom = top+view.getMeasuredHeight();
- view.layout(left,top,right,bottom);
- // 上面代码实现第一个子view的布局,本行后续布局需要重置left
- curL = right+mHorSpacing;
- }
- //单行布局完成后需要将left重置到左边
- curL = getPaddingLeft();
- //top的长度需要重新赋值 原本距上+行高+行间距
- curT = curT+lineHeight+mVerSpacing;
-
- }
-
-
- }
- public int dp2px(int pxValue) {
- return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, pxValue, getResources().getDisplayMetrics());
-
- }
- }
源码下载地址:https://download.csdn.net/download/feng_zhongsha/16636492
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。