Android 自定义groupviewViewGroup 怎么给他的子View增加属性?注意,是给【子View】增加属性

[摘要:自界说ViewGroup增加组开控件的子view时
肯定要重写
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widt]
自定义ViewGroup添加组合控件的子view时
一定要重写
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
并且在onMeasure中一定要调用measureChildren函数
否则组合控件内部的子view不会显示9178人阅读
移动开发_android(32)
本文翻译自《50 android hacks》依照惯例,先从一个例子说起。很简单,3张扑克牌叠在一起显示。这个布局效果该如何实现呢?有的同学该说了,这很简单啊,用RelativeLayout或FrameLayout,然后为每一个扑克牌设置margin就能实现了。ok,那就看一下通过这种方式是如何实现的。代码如下:&RelativeLayout xmlns:android=&/apk/res/android&
android:layout_width=&fill_parent&
android:layout_height=&fill_parent& &
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#FF0000& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:layout_marginLeft=&30dp&
android:layout_marginTop=&20dp&
android:background=&#00FF00& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:layout_marginLeft=&60dp&
android:layout_marginTop=&40dp&
android:background=&#0000FF& /&
&/RelativeLayout&效果图没错,通过这种方式是可以实现的。但是,不觉得这种方式有点low吗?!让我们用高级一点的方式去实现它,提升一下自己的逼格!定制ViewGroup之前,我们需要先理解几个概念。Android绘制视图的方式这里我不会涉及太多的细节,但是需要理解Android开发文档中的一段话:“绘制布局由两个遍历过程组成:测量过程和布局过程。测量过程由measure(int, int)方法完成,该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会向下层传递尺寸和规格。当measure方法遍历结束,每个视图都保存了各自的尺寸信息。第二个过程由layout(int,int,int,int)方法完成,该方法也是由上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。”简而言之,第一步是测量ViewGroup的宽度和高度,在onMeasure()方法中完成,ViewGroup遍历所有子视图计算出它的大小。第二步是根据第一步获取的尺寸去布局所有子视图,在onLayout()中完成。创建CascadeLayout终于到了定制ViewGroup的阶段了。假设我们已经定制了一个CascadeLayout的容器,我们会这样使用它。&FrameLayout xmlns:cascade=&/apk/res/com.manoel.custom&
&!-- 声明命名空间 --&
xmlns:android=&/apk/res/android&
android:layout_width=&fill_parent&
android:layout_height=&fill_parent& &
&com.manoel.view.CascadeLayout
android:layout_width=&fill_parent&
android:layout_height=&fill_parent&
&!-- 自定义属性 --&
cascade:horizontal_spacing=&30dp&
cascade:vertical_spacing=&20dp& &
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#FF0000& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#00FF00& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#0000FF& /&
&/com.manoel.view.CascadeLayout&
&/FrameLayout&首先,定义属性。在values文件夹下面创建attrs.xml,代码如下:&resources&
&declare-styleable name=&CascadeLayout&&
&attr name=&horizontal_spacing& format=&dimension& /&
&attr name=&vertical_spacing& format=&dimension& /&
&/declare-styleable&
&/resources&
同时,为了严谨一些,定义一些默认的垂直距离和水平距离,以防在布局中没有提供这些属性。在dimens.xml中添加如下代码:&resources&
&dimen name=&cascade_horizontal_spacing&&10dp&/dimen&
&dimen name=&cascade_vertical_spacing&&10dp&/dimen&
&/resources&准备工作已经做好了,接下来看一下CascadeLayout的源码,略微有点长,后面帮助大家分析一下。public class CascadeLayout extends ViewGroup {
private int mHorizontalS
private int mVerticalS
public CascadeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CascadeLayout);
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();
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getPaddingLeft();
int height = getPaddingTop();
int verticalS
final int count = getChildCount();
for (int i = 0; i & i++) {
verticalSpacing = mVerticalS
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
width = getPaddingLeft() + mHorizontalSpacing *
if (lp.verticalSpacing &= 0) {
verticalSpacing = lp.verticalS
width += child.getMeasuredWidth();
height += verticalS
width += getPaddingRight();
height += getChildAt(getChildCount() - 1).getMeasuredHeight()
+ getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i & i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
+ child.getMeasuredHeight());
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutP
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
public static class LayoutParams extends ViewGroup.LayoutParams {
public int verticalS
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
public LayoutParams(int w, int h) {
super(w, h);
首先,分析构造函数。public CascadeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CascadeLayout);
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();
}如果在布局中使用CasecadeLayout,系统就会调用这个构造函数,这个大家都应该知道的吧。这里不解释why,有兴趣的可以去看源码,重点看系统是如何解析xml布局的。构造函数很简单,就是通过布局文件中的属性,获取水平距离和垂直距离。然后,分析自定义LayoutParams。这个类的用途就是保存每个子视图的x,y轴位置。这里把它定义为静态内部类。ps:提到静态内部类,我又想起来关于多线程内存泄露的问题了,如果有时间再给大家解释一下多线程造成内存泄露的问题。public static class LayoutParams extends ViewGroup.LayoutParams {
public int verticalS
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
public LayoutParams(int w, int h) {
super(w, h);
}除此之外,还需要重写一些方法,checkLayoutParams()、generateDefaultLayoutParams()等,这个方法在不同ViewGroup之间往往是相同的。接下来,分析onMeasure()方法。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getPaddingLeft();
int height = getPaddingTop();
int verticalS
final int count = getChildCount();
for (int i = 0; i & i++) {
verticalSpacing = mVerticalS
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec); // 令每个子视图测量自身
LayoutParams lp = (LayoutParams) child.getLayoutParams();
width = getPaddingLeft() + mHorizontalSpacing *
// 保存每个子视图的x,y轴坐标
if (lp.verticalSpacing &= 0) {
verticalSpacing = lp.verticalS
width += child.getMeasuredWidth();
height += verticalS
width += getPaddingRight();
height += getChildAt(getChildCount() - 1).getMeasuredHeight()
+ getPaddingBottom();
// 使用计算所得的宽和高设置整个布局的测量尺寸
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
}最后,分析onLayout()方法。
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i & i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
+ child.getMeasuredHeight());
}逻辑很简单,用onMeasure()方法计算出的值为参数循环调用子View的layout()方法。为子视图添加自定义属性作为示例,下面将添加子视图重写垂直间距的方法。第一步是向attrs.xml中添加一个新的属性。
&declare-styleable name=&CascadeLayout_LayoutParams&&
&attr name=&layout_vertical_spacing& format=&dimension& /&
&/declare-styleable&这里的属性名是layout_vertical_spacing,因为该属性名前缀是layout_,同时,又不是View固有的属性,所以该属性会被添加到LayoutParams的属性表中。在CascadeLayout类的构造函数中读取这个新属性。public static class LayoutParams extends ViewGroup.LayoutParams {
public int verticalS
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CascadeLayout_LayoutParams);
verticalSpacing = a
.getDimensionPixelSize(
R.styleable.CascadeLayout_LayoutParams_layout_vertical_spacing,
} finally {
a.recycle();
public LayoutParams(int w, int h) {
super(w, h);
那怎么使用这个属性呢?so easy!
&com.manoel.view.CascadeLayout
android:layout_width=&fill_parent&
android:layout_height=&fill_parent&
cascade:horizontal_spacing=&30dp&
cascade:vertical_spacing=&20dp& &
&!-- 注意:这张“扑克牌”使用了layout_vertical_spacing属性 --&
android:layout_width=&100dp&
android:layout_height=&150dp&
cascade:layout_vertical_spacing=&90dp&
android:background=&#FF0000& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#00FF00& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#0000FF& /&
&/com.manoel.view.CascadeLayout&参考资料
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2488517次
积分:15375
积分:15375
排名:第606名
原创:127篇
转载:28篇
译文:60篇
评论:358条
文章:85篇
阅读:590086
(2)(3)(7)(2)(2)(11)(31)(31)(3)(8)(30)(11)(5)(1)(4)(1)(1)(1)(5)(1)(2)(53)}

我要回帖

更多关于 自定义viewgroup 刷新 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信