葫芦岛市建设局网站,做乡镇网站,嘉兴搜索引擎网站推广,我有域名怎么做网站目录 1、View的绘制流程 2、自定义ViewGroup构造函数的作用 3、onMeasure 方法 3.1、View的度量方式 3.2、onMeasure方法参数的介绍 3.3、自定义ViewGroup onMeasure 方法的实现 4、onLayout方法 5、onDraw方法 6、自定义View的生命周期 7、自定义流式布局的实现 扩展#xff… 目录 1、View的绘制流程 2、自定义ViewGroup构造函数的作用 3、onMeasure 方法 3.1、View的度量方式 3.2、onMeasure方法参数的介绍 3.3、自定义ViewGroup onMeasure 方法的实现 4、onLayout方法 5、onDraw方法 6、自定义View的生命周期 7、自定义流式布局的实现 扩展 1、View的绘制流程 从Activity启动开始了解View的绘制流程。 图1Activity启动的流程。 图2Activity启动的调用流程 图3显示了View与Window的逻辑结构。 图3中的ViewRootImpl是在图2中的Activity的attach中生成的是在Activity的onCreate生命周期之前ViewRootImpl负责处理View 的事件和逻辑。 图4ViewRootImpl的方法的调用流程 通过ViewRootImpl的performTraversals方法看出View的绘制流程 mearsure、layout、draw分别对应View及ViewGroup的onMeasure、onLayout和onDraw方法。 图5Activity在onCreate时使用setContentView 给Activity设置具体的ContentView。 2、自定义ViewGroup构造函数的作用 构造函数用于创建对象自定义View或ViewGroup会有三个不同用途的构造函数分别用于 new Object()创建对象、xml解析生成对象和自定义。 /**
* 通过 new FlowLayout() 代码创建
* param context
*/
public FlowLayout(Context context) {super(context);
}/**
* 通过xml解析创建
* param context
* param attrs
*/
public FlowLayout(Context context, AttributeSet attrs) {super(context, attrs);
}/**
* 自定义View 默认的属性以及自定义的属性
* param context
* param attrs
* param defStyleAttr
*/
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
} 3、onMeasure 方法 可参照LinearLayout或其他布局代码中onMeasure 的具体实现。 3.1、View的度量方式 View的层级结构如上图在measure时采用深度递归自上而下进行遍历在ViewGroup中触发子View的度量。 3.2、onMeasure方法参数的介绍 Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} onMeasure方法的参数有2个分别是widthMeasureSpec和heightMeasureSpec两个参数分别是 父布局根据父布局的宽高、父布局的padding、当前控件的layout_width或layout_height属性值、当前控件的margin属性值生成的包含width和height相关的mode以及宽高值。 以ViewGroup的width为例layout_width有三种数值match_parent、wrap_content和固定值。 当layout_width为match_parent时分配给的宽度是父布局的宽度。 当layout_width为wrap_content时因还未知具体需要多大的宽度所以分配的宽度是父布局的宽度。 当layout_width为固定值时分配的宽度是固定值。 综上度量最终计算的宽高只会比参数中给定的宽高小或者相等。 3.3、自定义ViewGroup onMeasure 方法的实现 在自定义ViewGroup时需要实现onMeasure方法否则layout_width和layout_height属性无效该ViewGroup的宽高为父控件的宽高。 在重写onMeasure方法时该方法需要实现的功能如下 度量子View深度递归因为ViewGroup不是叶子节点一定要度量子View的宽高 根据子View度量后的宽高计算ViewGroup的宽高 注意 在度量子View的宽高时要考虑ViewGroup的padding和子View的margin值。 在计算ViewGroup的宽高时要使用子View度量后的宽高ViewGroup分配给子View的宽高可能会大需要子View执行onMeasure方法后确定自己的宽高 在计算ViewGroup的宽高时要加入ViewGroup的padding值。 4、onLayout方法 在onLayout方法中使用相对父控件的相对布局而不是相对Android屏幕坐标系的绝对布局在实现onLayout时只考虑与父控件的关系即可最终在View绘制时会将相对位置转换为绝对位置此工作是由WMS来实现的。 注意 在自定义ViewGroup的 onLayout中需考虑ViewGroup的padding和子View的margin值 在自定义View中不用实现onLayout。 5、onDraw方法 ViewGroup的作用在于布局子View如果需要边框和背景的绘制在onDraw中实现如不需要可不实现。 6、自定义View的生命周期 View的度量、布局和绘制会有多次但是View的初始化只有一次。 7、自定义流式布局的实现 package com.liluj.androidui.custom;import static com.scwang.smartrefresh.layout.util.SmartUtil.dp2px;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.liluj.androidui.utils.UnitConvertUtils;
import java.util.ArrayList;
import java.util.List;/**
* 自定义流式布局
*/
public class FlowLayout extends ViewGroup {//水平间隔private int horizontalSpace dp2px(20);//垂直间隔private int veticalSpace dp2px(20);private ListListView lineViewList; //记录每行的Viewprivate ListInteger heightList; //记录每列的高度。/*** 通过 new FlowLayout() 代码创建* param context*/public FlowLayout(Context context) {super(context);}/*** 通过xml解析创建* param context* param attrs*/public FlowLayout(Context context, AttributeSet attrs) {super(context, attrs);}/*** 自定义View 默认的属性以及自定义的属性* param context* param attrs* param defStyleAttr*/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 initMeasure() {/*** 此两个参数是为了给Layout布局用的界面不只绘制一次会重新绘制所以在每次执行onMeasure时要重新初始化而不是在创建FlowLayout时初始化*/lineViewList new ArrayList();heightList new ArrayList();}/*** 依据子View的宽高 确定自己的宽高* 注意* 1、margin值在父控件中处理* 1.1 当前ViewGroup的margin值在父控件已处理* 1.2 在度量和布局子View时要考虑margin值否则子View的margin属性无用此例子没处理子View的margin值* 2、padding值包含在ViewGroup以及View的宽高* 2.1 在ViewGroup和View的 onMeasure、onLayout、onDraw时要考虑自身的padding值* param widthMeasureSpec horizontal space requirements as imposed by the parent.* The requirements are encoded with* {link android.view.View.MeasureSpec}.* param heightMeasureSpec vertical space requirements as imposed by the parent.* The requirements are encoded with* {link android.view.View.MeasureSpec}.**/Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);initMeasure();int count getChildCount();ListView lineView new ArrayList();int lineUsedWidth 0; //当前行已使用的宽度int lineHeight 0; //当前行的高度int mWidth 0; //view的宽度int mHeight 0; //view的高度/*** 父控件根据自己的宽高以及当前View的LayoutParam属性(宽、高、margin)分配给当前View的宽高固定值就是固定值* match_parent和wrap_content为父控件的宽度*/int maxWidth MeasureSpec.getSize(widthMeasureSpec);//子View可使用的最大宽度需减去ViewGroup的paddingLeft和paddingRightint childrenCanUseWidth maxWidth - getPaddingLeft()-getPaddingRight();int maxHeight MeasureSpec.getSize(heightMeasureSpec);/*** 度量子View并计算ViewGroup属性为wrap_content时实际的宽高*/for(int i 0;icount;i){View view getChildAt(i);/*** 根据当前View的MeasureSpec属性、padding值和子View的LayoutParams值(android:layout_width和android:layout_height的属性值)来给子View分配空间*/int measureSpecWidth getChildMeasureSpec(widthMeasureSpec,getPaddingLeft()getPaddingRight(),view.getLayoutParams().width);int measureSpecHeight getChildMeasureSpec(heightMeasureSpec,getPaddingTop()getPaddingBottom(),view.getLayoutParams().height);/*** 触发子View度量自己的空间子View度量自己的大小后校正子View的大小*/view.measure(measureSpecWidth,measureSpecHeight);int measuredWidth view.getMeasuredWidth();int measuredHeight view.getMeasuredHeight();//换行if(lineUsedWidth measuredWidth childrenCanUseWidth){lineViewList.add(lineView);heightList.add(lineHeight);mWidth Math.max(mWidth,lineUsedWidth);mHeight mHeight lineHeight veticalSpace;lineUsedWidth 0;lineHeight 0;lineView new ArrayList();}lineView.add(view);lineUsedWidth lineUsedWidth measuredWidth horizontalSpace;lineHeight Math.max(lineHeight, measuredHeight);/*** 添加上最后一行的宽高*/if(i count -1){lineViewList.add(lineView);heightList.add(lineHeight);mWidth Math.max(mWidth,lineUsedWidth);mHeight mHeight lineHeight;}}//ViewGroup的宽高加上padding值mWidth mWidthgetPaddingLeft()getPaddingRight();mHeight mHeightgetPaddingTop()getPaddingBottom();/*** 获取当前View 宽高的mode*/int widthMode MeasureSpec.getMode(widthMeasureSpec);int heightMode MeasureSpec.getMode(heightMeasureSpec);/*** 当View的宽高为固定值时使用固定值此处固定值只会比按照wrap_content计算出来的值大不会小与父控件分配给当前View的宽高值有关。* 可参见ViewGroup的getChildMeasureSpec方法*/int realWidth (widthMode MeasureSpec.EXACTLY) ? maxWidth:mWidth;int realHeight (heightMode MeasureSpec.EXACTLY) ? maxHeight:mHeight;setMeasuredDimension(realWidth,realHeight);}/*** 布局子View布局子View时使用相对坐标相对父View进行布局而不是绝对坐标Window坐标系* param changed This is a new size or position for this view* param l Left position, relative to parent* param t Top position, relative to parent* param r Right position, relative to parent* param b Bottom position, relative to parent*/Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int curL getPaddingLeft();int curT getPaddingTop();for(int i 0;i lineViewList.size();i){ListView lineView lineViewList.get(i);for(int j 0;jlineView.size();j){View view lineView.get(j);int left curL;int top curT;int right curL view.getMeasuredWidth();int bottom curT view.getMeasuredHeight();view.layout(left,top,right,bottom);curL right horizontalSpace;}curL getPaddingLeft();curT curT heightList.get(i)veticalSpace;}}
} 效果图 扩展 将xml解析为UI 例如LinearLayout标签解析为LinearLayout 对象layout_widthlayout_height、layout_weight会生成对应的LayoutParam。 MeasureSpec用于表示View以及ViewGroup的宽高 xml解析采用反射实现。