android 开发技巧(3)--创建定制的 ViewGroup
来源:互联网 发布:网络大专报名条件 编辑:程序博客网 时间:2024/06/06 19:36
在项目中,有的时候需要用到自定义的ViewGroup
一、绘制过程
绘制布局由两个遍历过程组成:测量过程和布局过程。测
量过程由 measure(int, int) 方法完成,该方法从上到下遍历视图
树。在递归遍历过程中,每个视图都会向下层传递尺寸和规格。当
measure 方法遍历结束,每个视图都保存了各自的尺寸信息。第二
个过程由 layout(int, int, int,int) 方法完成,该方法也是由上而下遍
历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所
有子视图的位置信息。”
第一步是测量 ViewGroup 的宽度和高度,在 onMeasure() 方法中完成这
步操作。在该方法中, ViewGroup 通过遍历所有子视图计算出它
的大小。在 onLayout() 方法中完成,在该方法中,
ViewGroup 利用上一步计算出的测量信息,布局所有子视图。
二、ViewGroup和LayoutParams
每个ViewGroup需要指定一个LayoutParams,用于确定支持childView支持哪些属性,比如LinearLayout指定LinearLayout.LayoutParams等在编写 onMeasure() 方法之前,先创建自定义 LayoutParams类,该类用于保存每个子视图的 x、 y 轴位置。把 LayoutParams 定义为 的内部类
三、View的3种测量模式
EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;
四、自定义ViewGroup需要实现的方法
用新定义的 ViewGroup中的LayoutParams 类,还需要重
写 CascadeLayout 类中的其他一些方法。这些方法是 checkLayoutParams()、 generateDefaultLayoutParams()、 generateLayoutPar
ams(AttributeSetattrs) 和 generateLayoutParams(ViewGroup.
LayoutParams p)。这些方法的代码在不同 ViewGroup 之间往往是
相同的。
效果图
主界面java代码就不贴了,只是引用xml而已
主界面布局activity_hack03.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cascade="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="boerpower.com.android50hacks.Hack03Activity"> <boerpower.com.android50hacks.CascadeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" cascade:horizontal_spacing="30dp" cascade:vertical_spacing="20dp"> <View android:layout_width="100dp" android:layout_height="150dp" android:background="#FF0000" /> <View android:layout_width="100dp" android:layout_height="150dp" android:background="#00FF00" /> <View android:layout_width="100dp" android:layout_height="150dp" android:background="#0000FF" /> </boerpower.com.android50hacks.CascadeLayout></RelativeLayout>
自定义ViewGroup类:CascadeLayout.java
public class CascadeLayout extends ViewGroup { private int mHorizontalSpacing; private int mVerticalSpacing; public CascadeLayout(Context context, AttributeSet attrs) {//当通过XML文件创建该视图的实例时会调用构造函数 super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CascadeLayout);//mHorizontalSpacing和mVerticalSpacing由自定义属性中获取,如果其值未指定,就使用默认值 try { mHorizontalSpacing = a.getDimensionPixelSize( R.styleable.CascadeLayout_horizontal_spacing, getResources().getDimensionPixelSize( R.dimen.cascade_horizontal_spacing)); mVerticalSpacing = a.getDimensionPixelSize( R.styleable.CascadeLayout_vertical_spacing, getResources() .getDimensionPixelSize(R.dimen.cascade_vertical_spacing)); } finally { a.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //使用宽和高计算布局的最终大小以及子视图的x与y轴位置 int width = getPaddingLeft(); int height = getPaddingTop(); int verticalSpacing; final int count = getChildCount(); for (int i = 0; i < count; i++) { verticalSpacing = mVerticalSpacing; View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec);//令每个子视图测量自身 LayoutParams lp = (LayoutParams) child.getLayoutParams(); width = getPaddingLeft() + mHorizontalSpacing * i; //在 LayoutParams中保存每个子视图的 x 和 y 坐标 lp.x = width; lp.y = height; if (lp.verticalSpacing >= 0) { verticalSpacing = lp.verticalSpacing; } width += child.getMeasuredWidth(); height += verticalSpacing; } width += getPaddingRight(); height += getChildAt(getChildCount() - 1).getMeasuredHeight() + getPaddingBottom();//在 LayoutParams中保存每个子视图的 x 和 y 坐标使用计算所得的宽和高设置整个布局的 测 量尺寸 setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight()); } } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; } @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p.width, p.height); } public static class LayoutParams extends ViewGroup.LayoutParams { int x; int y; public int verticalSpacing; public LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CascadeLayout_LayoutParams); try { verticalSpacing = a .getDimensionPixelSize( R.styleable.CascadeLayout_LayoutParams_layout_vertical_spacing, -1); } finally { a.recycle(); } } public LayoutParams(int w, int h) { super(w, h); } }}
定义那些定制的属性,在res/values 目录下创建一个属性文件 attrs.xml
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="CascadeLayout"> <attr name="horizontal_spacing" format="dimension" /> <attr name="vertical_spacing" format="dimension" /> </declare-styleable> <declare-styleable name="CascadeLayout_LayoutParams"> <attr name="layout_vertical_spacing" format="dimension" /> </declare-styleable></resources>
同时还需要指定水平间距和垂直间距的默认值,以便在未指定这些值时使用。把这些默认值保存在 dimens.xml 文件中,该文件同样位于 res/values 文件夹下。 dimens.xml 文件的内容如下:
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <dimen name="cascade_horizontal_spacing">10dp</dimen> <dimen name="cascade_vertical_spacing">10dp</dimen></resources>
参考资料
Android 手把手教您自定ViewGroup
Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
- android 开发技巧(3)--创建定制的 ViewGroup
- 创建定制的ViewGroup
- Hack3 - 创建定制的ViewGroup
- 【Android 开发技巧】如何自定义ViewGroup
- 动开发] 【Android 开发技巧】如何自定义ViewGroup
- 50个Android开发技巧(03 自定义ViewGroup)
- Android开发,自定义ViewGroup的神器,ViewDragHelper
- android 系统定制的小技巧
- android 系统定制的小技巧
- android 系统定制的小技巧
- android 系统定制的小技巧
- android 系统定制的小技巧
- Android开发,自定义ViewGroup
- Android开发技巧——定制仿微信图片裁剪控件
- Android开发技巧——定制仿微信图片裁剪控件
- Android应用开发原理之从ViewGroup源码分析ViewGroup的事件分发机制
- 【Android】快速开发偷懒必备,一句话搞定所有ViewGroup的Adapter . 支持自定义ViewGroup
- Android开发:定制Activity的标题栏(Titlebar)
- 332. Reconstruct Itinerary
- 《Android源码设计模式》读书笔记 (13) 第13章 备忘录模式
- h3c 三层交换机snmp 团体名称配置
- HDU 1284 钱币兑换问题(母函数)
- 【Android】安卓学习笔记之广播(一)动态注册监听网络变化
- android 开发技巧(3)--创建定制的 ViewGroup
- Android质量压缩和尺寸压缩
- 备忘
- React图片上传组件设计
- ModeDriven 和Preparable拦截器 及其源码解析
- Android 开发文档doc下载 ,怎么找到它
- Android之解决太大太多图片造成的oom
- 2sum问题和3sum问题
- centos7 上安装jdk tomcat oracle