Android自定义ViewGroup的实现方法
在android中提供了常见的几种viewgroup的实现,包括linearlayout、relativeayout、framelayout等。这些viewgroup可以满足我们一般的开发需求,但是对于界面要求复杂的,这几个布局就显得捉襟见肘了。所以自定义的viewgroup在我们接触过的应用中比比皆是。
要想实现一个自定义的viewgroup,第一步是学会自定义属性,这些自定义的属性将让我们配置布局文件的时候更加的灵活。自定义属性是在value目录下声明一个attrs.xml文件。
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="cascadeviewgroup"> <attr name="verticalspacing" format="dimension"/> <attr name="horizontalspacing" format="dimension"/> </declare-styleable> <declare-styleable name="cascadeviewgroup_layoutparams"> <attr name="layout_paddingleft" format="dimension"/> <attr name="layout_paddintop" format="dimension"/> </declare-styleable> </resources>
在这里我们声明了两个自定义属性集,cascadeviewgroup中的属性是针对我们自定义的cascadeviewgroup组件设置的,也就是可以在布局文件中<cascadeviewgroup>标签中可以使用的属性。另外一个cascadeviewgroup_layoutparams则是针对于cascadeviewgroup中的子view设置的属性。
在编写代码前,我们还设置了一个默认的宽度和高度供cascadelayout使用。这两个属性在dimens.xml定义。
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="default_horizontal_spacing">10dp</dimen> <dimen name="default_vertical_spacing">10dp</dimen> </resources>
下面开始编写自定义的组件cascadelayout了。
package com.app.customviewmotion; import android.content.context; import android.content.res.typedarray; import android.util.attributeset; import android.view.view; import android.view.viewgroup; /** * created by charles on 2015/8/13. */ public class cascadeviewgroup extends viewgroup { //自定义布局中设置的宽度和高度 private int mhoriztonalspacing; private int mverticalspacing; public cascadeviewgroup(context context) { this(context, null); } public cascadeviewgroup(context context, attributeset attrs) { this(context, attrs, 0); } public cascadeviewgroup(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); typedarray a = context.obtainstyledattributes(attrs, r.styleable.cascadeviewgroup); try { //获取设置的宽度 mhoriztonalspacing = a.getdimensionpixelsize(r.styleable.cascadeviewgroup_horizontalspacing, this.getresources().getdimensionpixelsize(r.dimen.default_horizontal_spacing)); //获取设置的高度 mverticalspacing = a.getdimensionpixelsize(r.styleable.cascadeviewgroup_verticalspacing, this.getresources().getdimensionpixelsize(r.dimen.default_vertical_spacing)); } catch (exception e) { e.printstacktrace(); } finally { a.recycle(); } } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { final int count = this.getchildcount(); int width = this.getpaddingleft(); int height = this.getpaddingtop(); for (int i = 0; i < count; i++) { final view currentview = this.getchildat(i); this.measurechild(currentview, widthmeasurespec, heightmeasurespec); cascadeviewgroup.layoutparams lp = (cascadeviewgroup.layoutparams) currentview.getlayoutparams(); if(lp.msettingpaddingleft != 0){ width +=lp.msettingpaddingleft; } if(lp.msettingpaddingtop != 0){ height +=lp.msettingpaddingtop; } lp.x = width; lp.y = height; width += mhoriztonalspacing; height += mverticalspacing; } width +=getchildat(this.getchildcount() - 1).getmeasuredwidth() + this.getpaddingright(); height += getchildat(this.getchildcount() - 1).getmeasuredheight() + this.getpaddingbottom(); this.setmeasureddimension(resolvesize(width, widthmeasurespec), resolvesize(height, heightmeasurespec)); } @override protected void onlayout(boolean b, int l, int i1, int i2, int i3) { final int count = this.getchildcount(); for (int i = 0; i < count; i++) { final view currentview = this.getchildat(i); cascadeviewgroup.layoutparams lp = (cascadeviewgroup.layoutparams) currentview.getlayoutparams(); currentview.layout(lp.x, lp.y, lp.x + currentview.getmeasuredwidth(), lp.y + currentview.getmeasuredheight()); } } public static class layoutparams extends viewgroup.layoutparams { int x; int y; int msettingpaddingleft; int msettingpaddingtop; public layoutparams(context c, attributeset attrs) { super(c, attrs); typedarray a = c.obtainstyledattributes(attrs, r.styleable.cascadeviewgroup_layoutparams); msettingpaddingleft = a.getdimensionpixelsize(r.styleable.cascadeviewgroup_layoutparams_layout_paddingleft, 0); msettingpaddingtop = a.getdimensionpixelsize(r.styleable.cascadeviewgroup_layoutparams_layout_paddintop, 0); a.recycle(); } public layoutparams(int width, int height) { super(width, height); } public layoutparams(viewgroup.layoutparams source) { super(source); } } @override protected viewgroup.layoutparams generatedefaultlayoutparams() { return new layoutparams(layoutparams.wrap_content, layoutparams.wrap_content); } @override protected viewgroup.layoutparams generatelayoutparams(viewgroup.layoutparams p) { return new layoutparams(p); } @override public viewgroup.layoutparams generatelayoutparams(attributeset attrs) { return new layoutparams(this.getcontext(), attrs); } }
代码稍微优点长,但是结构还是很清晰的。
1)构造方法中或者xml文件中配置属性的值。通过typedarray中的方法获取我们在layout布局中设置的属性,并且将他们保存在成员变量中。
2)构造自定义的内部类layoutparams。构造这个内部类,可以方便我们在测量我们的子view的时候保存他们的属性值,以便在layout阶段布局。
3)generatelayoutparams()、generatedefaultparams()等方法。在这些方法中返回我们自定义的layoutparams。至于为什么要重写这些方法,可以查看viewgroup类的addview()方法就很清楚了。
4)measure阶段。在measure阶段,我们会测量自己的大小,同时也要测量子view的大小,并且将子view的信息保存在layoutparams中。
5)layout阶段。根据各个子view的信息,布局他们的位置。
最后加上布局文件。
<?xml version="1.0" encoding="utf-8"?> <!--添加自定义属性给viewgroup--> <!--新添加的命名空间的后缀必须保持和.xml中声明的包名一致--> <com.app.customviewmotion.cascadeviewgroup xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ts="http://schemas.android.com/apk/res/com.app.customviewmotion" android:layout_width="match_parent" android:layout_height="match_parent" ts:horizontalspacing="15dp" ts:verticalspacing="15dp"> <textview android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text1" android:background="#668b8b"/> <textview android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text2" android:background="#ffdab9"/> <textview android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text3" android:background="#43cd80"/> <!--这个子view中添加自定义子view属性--> <textview android:layout_width="100dp" android:layout_height="100dp" android:gravity="center" android:text="text4" ts:layout_paddingleft="100dp" ts:layout_paddintop="100dp" android:background="#00ced1"/> </com.app.customviewmotion.cascadeviewgroup>
实现的效果如下:
以上就是的全部内容,希望能给大家一个参考,也希望大家多多支持。
上一篇: java TreeMap源码解析详解
下一篇: ibatis学习之搭建Java项目