自定义ViewGroup
来源:互联网 发布:北外网络教育作业答案 编辑:程序博客网 时间:2024/05/20 05:25
ViewGroup 运行的基本流程大致为:
1) 测量容器尺寸
重写 onMeasure()方法测量容器大小,和自定义组件有所区别的是,在测量容器大小之前,必须先调用 measureChildren()方法测量所有子组件的大小,不然结果永远为 0。
2) 确定每个子组件的位置
重写 onLayout()方法确定每个子组件的位置(这个其实挺麻烦,也是定义容器的难点部
分),在 onLayout()方法中,调用 View 的 layout()方法确定子组件的位置。
3) 绘制容器
重写 onDraw()方法,其实 ViewGroup 类并没有重写 onDraw()方法,除非有特别的要求,
自定义容器也很少去重写。比如 LinearLayout 重写了该方法用于绘制水平或垂直分割
条,而 FrameLayout 则是重写了 draw()方法,作用其实是一样的。
工作原理:
重写 onMeasure()方法:
重写 ViewGroup 的 onMeasure()方法时,必须先调用 measureChildren()方法测量子组件的尺寸,measureChildren()方法中,循环遍历每一个子组件,如果当前子组件的可见性不为 GONE 也就是没有隐藏则继续调用 measureChild(child,widthMeasureSpec,heightMeasureSpec)方法测量当前子组件 child 的大小,measureChild()方法结合父容器的 MeasureSpec、子组件的 Padding 和 LayoutParams 三个因素 利 用 getChildMeasureSpec() 计算出子组件的尺寸模式和尺寸大小(可以跟踪到getChildMeasureSpec()方法中查看),并调用子组件的 measure()方法进行尺寸测量。measure()方法调用了 onMeasure(widthMeasureSpec,heightMeasureSpec)方法,该方法正是我们重用的用来测量组件尺寸的方法,至此,测量组件尺寸的工作已掌握到开发人员手中。
接下来调用 onLayout()方法定位子组件,以确定子组件的位置和大小,在 onLayout()方法中,我们将调用子组件的 layout()方法,这里要一分为二,如果子组件是一个 View,定位流程到此结束,如果子组件又是一个容器呢?我们进入 layout()方法进行跟踪。如果子组件是一个容器,又会继续调用该容器的 onLayout()方法对孙组件进行定位,所以onLayout()方法也是一个递归的过程。
重写 onLayout()方法:
protected void onLayout(boolean changed,int l,int t,int r,int b),其中,参数 changed 判断是否有新的大小和位
置,l 表示 left,t 表示 top,r 表示 right,b 表示 bottom,后面的 4 个参数表示容器自己相对父容器的位置以及自身的大小,通常情况下,r-l 的值等同于方法 getMeasuredWidth()方法的返回值,b-t 的值等同于 getMeasuredHeight()方法的返回值。在 onLayout()方法中,需要调用 View 的 layout()方法用于定义子组件和子容器的位置,layout()方法的原理如下:
public void layout(int l,int t,int r,int b)
展示:
public class CustomViewGroup extends ViewGroup { private TextView textView; public CustomViewGroup(Context context) { super(context); } public CustomViewGroup(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void init(Context context){ textView = new TextView(context); textView.setText("自定义ViewGroup"); textView.setTextColor(Color.parseColor("#00a0e9")); textView.setBackgroundColor(Color.YELLOW);//TextView的背景 ViewGroup.LayoutParams lp = new LayoutParams(400,400);//textview的布局参数 addView(textView,lp); setBackgroundColor(Color.BLUE);//ViewGroup的背景颜色 } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { View childView = getChildAt(0); childView.layout(50,50,textView.getMeasuredWidth()+50,textView.getMeasuredHeight()+50); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec,heightMeasureSpec);//测量子View setMeasuredDimension(600,600);//测量ViewGroup的宽高 }}
结果显示:
public class CustomViewGroup2 extends ViewGroup { public CustomViewGroup2(Context context) { super(context); } public CustomViewGroup2(Context context, AttributeSet attrs) { this(context, attrs,0); } public CustomViewGroup2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //测量所有的子View measureChildren(widthMeasureSpec, heightMeasureSpec); //测量自己的宽高 int width = measureWidth(widthMeasureSpec); int height = measureHeight(heightMeasureSpec); setMeasuredDimension(width, height); } private int measureHeight(int widthMeasureSpec) { //父控件建议自己测量的值 int measureMode = MeasureSpec.getMode(widthMeasureSpec); int measureSize = MeasureSpec.getSize(widthMeasureSpec); int width = 0; if (measureMode == MeasureSpec.EXACTLY) { width = measureSize; } else if (measureMode == MeasureSpec.AT_MOST) { int aWidth = 0; int bWidth = 0; int cWidth = 0; int dWidth = 0; for (int i = 0; i < getChildCount(); i++) { if (i == 0) { aWidth = getChildAt(i).getMeasuredWidth();//左边View的宽度 } else if (i == 1) { bWidth = getChildAt(i).getMeasuredWidth(); } else if (i == 2) { cWidth = getChildAt(i).getMeasuredWidth(); } else if (i == 3) { dWidth = getChildAt(i).getMeasuredWidth(); } } width = Math.max(aWidth, bWidth) + Math.max(cWidth, dWidth);//取到宽度的最大值 } return width; } private int measureWidth(int heightMeasureSpec) { int measureMode = MeasureSpec.getMode(heightMeasureSpec); int measureSize = MeasureSpec.getSize(heightMeasureSpec); int height = 0; if (measureMode == MeasureSpec.EXACTLY) { height = measureSize; } else if (measureMode == MeasureSpec.AT_MOST) { int aHeight = 0; int bHeight = 0; int cHeight = 0; int dHeight = 0; for (int i = 0; i < getChildCount(); i++) { if (i == 0) { aHeight = getChildAt(i).getMeasuredHeight(); } else if (i == 1) { bHeight = getChildAt(i).getMeasuredHeight(); } else if (i == 2) { cHeight = getChildAt(i).getMeasuredHeight(); } else if (i == 3) { dHeight = getChildAt(i).getMeasuredHeight(); } } height = Math.max(aHeight, cHeight) + Math.max(bHeight, dHeight);//取到宽度的最大值 } return height; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View childView = getChildAt(i);//某一个子View if (i == 0) { childView.layout(0,0,childView.getMeasuredWidth(),childView.getMeasuredHeight()); } else if (i == 1) { childView.layout(getMeasuredWidth()-childView.getMeasuredWidth(),0,getMeasuredWidth(),childView.getMeasuredHeight()+0); } else if (i == 2) { childView.layout(0,getMeasuredHeight()-childView.getMeasuredHeight(),childView.getMeasuredWidth(),getMeasuredHeight()+0); } else if (i == 3) { childView.layout(getMeasuredWidth()-childView.getMeasuredWidth(),getMeasuredHeight()-childView.getMeasuredHeight(),getMeasuredWidth(),getMeasuredHeight()); } } }}
CustomViewGroup2的布局:
<com.example.user.myapplication4.CustomViewGroup2 android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="100dp" android:layout_height="100dp" android:background="@android:color/holo_blue_bright" /> <TextView android:layout_width="120dp" android:layout_height="120dp" android:background="@android:color/holo_blue_dark" /> <TextView android:layout_width="140dp" android:layout_height="150dp" android:background="@android:color/holo_red_dark" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:background="@android:color/holo_green_light" /></com.example.user.myapplication4.CustomViewGroup2>
结果显示:
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义viewGroup
- 自定义viewgroup
- 自定义viewgroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- 自定义ViewGroup
- java中volatile关键字的含义
- message系统
- Android startActivity中的Intent的各种FLAG
- GMAT数学满分前辈经验谈 攻克缺陷补足漏洞短板
- c++实验3-本月有几天?
- 自定义ViewGroup
- Android Genymotion 使用遇到的问题
- ajax请求的同步和异步操作
- 使用git提交项目到码云
- Gallery之BaseAdapter
- 2017浙理工校赛重现-G.最佳淘汰算法(优先队列模拟)
- WAL日志归档以及什么情况下会触发归档?
- poll函数详解及原理
- tfs自动编译msbuild参数