怎样自定义viewgroup layout让子元素有layout_weight属性?

如何自定义ViewGroup - 推酷
如何自定义ViewGroup
在开发中,有时会遇到一些需求,单单使用系统提供的布局和控件并不能够满足我们的开发,所以这个时候通常就可以自己定制布局(ViewGroup)和控件(View)了。我在这里,将会用一个最简单的例子,为大家解释一下自定义ViewGroup的基本流程,希望能够帮助到还不了解这个流程的朋友。
首先,我们想要实现的布局图如下:
就这样看起来十分简单,用系统提供的布局就可以实现了这个效果,根本不需要自定义ViewGroup来实现嘛!!!不过,如果你自己去尝试一下用系统的布局来做,你就会发现一些问题了。问题1是:要实现各个view错开的效果,就必须为他们设置不同的固定外边距参数,这样可能带来的问题就是:不同手机,显示效果可能会不一样,也就是适配问题!问题2是:如果想要加入的View多了,还需要自己计算每个View的外边距参数,很坑爹,再说,这里主要是讲解自定义ViewGroup的基本流程,所以,例子越简单,也就越好理解了!
首先,我们可以自定义自己ViewGroup想要的属性;这里我为ViewGroup定义了两个属性,horizontal_spacing(布局中的view的水平间距)和vertical_spacing(布局中的View的垂直间距);另外,还定义了一个布局参数的属性,layout_vertical_spacing,这个属性可以供我们自定义的ViewGroup中的View使用。
attrs.xml 的内容:
&?xml version=&1.0& encoding=&utf-8&?&
&resources&
&declare-styleable name=&MyCustomLayout&&
&attr name=&horizontal_spacing& format=&dimension& /&
&attr name=&vertical_spacing& format=&dimension& /&
&/declare-styleable&
&declare-styleable name=&MyCustomLayout_LayoutParams&&
&attr name=&layout_vertical_spacing& format=&dimension& /&
&/declare-styleable&
&/resources&
在dimens.xml中定义两个属性的默认值。
dimens.xml 的内容:
&?xml version=&1.0& encoding=&utf-8&?&
&resources&
&dimen name=&horizontal_spacing&&10dp&/dimen&
&dimen name=&vertical_spacing&&10dp&/dimen&
&/resources&
接着,最主要的步骤来了。先看看我们自定义的ViewGroup。
下面是源码:
package com.customlayout.
import android.content.C
import android.content.res.TypedA
import android.util.AttributeS
import android.view.V
import android.view.ViewG
public class MyCustomLayout extends ViewGroup {
private int mHorizontalS
private int mVerticalS
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomLayout);
mHorizontalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_horizontal_spacing,
getResources().getDimensionPixelSize(R.dimen.horizontal_spacing));
mVerticalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_vertical_spacing,
getResources().getDimensionPixelSize(R.dimen.vertical_spacing));
ta.recycle();
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
// TODO Auto-generated method stub
return p !=
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
// TODO Auto-generated method stub
return (LayoutParams)
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = getPaddingTop();
int verticalS
final int count = getChildCount();
for (int i = 0; i & i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
verticalSpacing = mVerticalS
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.verticalSpacing & 0) {
verticalSpacing += lp.verticalS
width = getPaddingLeft() + mHorizontalSpacing *
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());
public static class LayoutParams extends ViewGroup.LayoutParams {
public int verticalS
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray ta = c.obtainStyledAttributes(attrs, R.styleable.MyCustomLayout_LayoutParams);
verticalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_LayoutParams_layout_vertical_spacing, -1);
ta.recycle();
public LayoutParams(int w, int h) {
super(w, h);
首先,我们从MyCustomLayout.java 的构造方法说起。
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomLayout);
mHorizontalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_horizontal_spacing,
getResources().getDimensionPixelSize(R.dimen.horizontal_spacing));
mVerticalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_vertical_spacing,
getResources().getDimensionPixelSize(R.dimen.vertical_spacing));
ta.recycle();
这个构造方法有两个参数,其中attrs是我们布局的属性集合,有了这个参数,我们就可以获取到在布局文件xml中设置的相关属性了。
接着,讲onMeasure方法;顾名思义,这个方法是一个测量方法,它的作用是:遍历布局中的每一个View,对每一个View进行测量(调用measureChild方法),接着再为View设置LayoutParams,设置View的位置,即在屏幕上的x,y坐标,以便供onLayout方法中使用。这里的LayoutParams是我们重写的,其中int x和int y保存了布局中View的坐标位置。 注意:重写LayoutParams类时,必须要对ViewGroup中的checkLayoutParams(ViewGroup.LayoutParams p)、generateLayoutParams(ViewGroup.LayoutParams p)、generateLayoutParams(AttributeSet attrs)、generateDefaultLayoutParams()进行重写,否则将会出现异常!!!
接下来,讲onLayout方法;这个方法是对布局中的全部View进行位置部署。从方法体中可以看到,它通过一个遍历,对布局中的每一个View调用layout方法进行位置部署。
好了,在这里稍微总结下: 自定义ViewGroup流程中, 最主要是对onMeasure和onLayout两个方法进行重写。onMeasure通过遍历布局中的View,为每一个View测量了大小、计算布局参数等。onLayout则是通过遍历布局中的View,为每一个View进行位置布置。
在activity_main.xml 中使用我们自定义的ViewGroup:
&com.customlayout.mycustomlayout.MyCustomLayout
xmlns:android=&/apk/res/android&
xmlns:customLayout=&/apk/res-auto&
android:id=&@+id/layout&
android:layout_width=&fill_parent&
android:layout_height=&fill_parent&
android:background=&@android:color/white&
customLayout:horizontal_spacing=&65dp&
customLayout:vertical_spacing=&85dp&&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=�ff00& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=�ff& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=&#ff0000& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=�ff000& /&
android:layout_width=&100dp&
android:layout_height=&150dp&
android:background=�f0f0& /&
&/com.customlayout.mycustomlayout.MyCustomLayout&
MainActivity.java 的源码:
package com.customlayout.
import android.app.A
import android.graphics.RectF;
import android.os.B
import android.view.animation.AlphaA
import android.view.animation.A
import android.view.animation.AnimationS
import android.view.animation.BounceI
import android.view.animation.LayoutAnimationC
import android.view.animation.TranslateA
public class MainActivity extends Activity {
LayoutAnimationController layoutAnimationC
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
layout = (MyCustomLayout) findViewById(R.id.layout);
AnimationSet set = new AnimationSet(true);
AlphaAnimation a = new AlphaAnimation(0f, 1f);
a.setDuration(500);
TranslateAnimation t = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1f,
Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f);
t.setDuration(500);
set.addAnimation(t);
set.addAnimation(a);
layoutAnimationController = new LayoutAnimationController(set);
layout.setLayoutAnimation(layoutAnimationController);
最后,讲一下布局动画;在ViewGroup类中,有一个属性是LayoutAnimation,也就是所谓的布局动画;只要我们指定一个动画给这个属性,那么ViewGroup中的每一个在布局时都能够带有动画效果。在上面的onCreate()方法中,我指定了一个动画集合并设置给了我自定义好的MyCustomLayout。运行一下程序,就可以看到,MyCustomLayout中的每一个View将会按顺序且带着我指定的动画出现在MyCustomLayout中,效果很酷!
结合这个简单的例子,给大家简单介绍了一下自定义ViewGroup的方法;在我们使用到的其他复杂布局中,都是采用我上面介绍的方法进行实现的,只是他们的计算将会比上面的例子复杂好多,但基本原理就还是这些。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致自定义ViewGroup控件--自定义属性(面板思想)
时间: 22:41:17
&&&& 阅读:84
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&attrs.xml(第一步,在这里)
&?xml version=&1.0& encoding=&utf-8&?&
&resources&
&!-- R.styleable.TopBar_titleTextColor在R文件中是这样的形式 --&
&declare-styleable name=&TopBar&&
&!-- 文字 --&
&attr name=&leftText& format=&string& /&
&attr name=&rightText& format=&string& /&
&attr name=&titleText& format=&string& /&
&!-- 文字颜色 --&
&attr name=&leftTextColor& format=&color& /&
&attr name=&rightTextColor& format=&color& /&
&attr name=&titleTextColor& format=&color& /&
&!-- 文字大小 --&
&attr name=&titleTextSize& format=&dimension& /&
&!-- 背景色 --&
&attr name=&leftBackground& format=&reference|color& /&
&attr name=&rightBackground& format=&reference|color& /&
&/declare-styleable&
&/resources&
activity_main.xml(命名空间需要注意)
&RelativeLayout xmlns:android=&/apk/res/android&
xmlns:tools=&/tools&
xmlns:app=&/apk/res/com.example.custometopbarui&
android:layout_width=&match_parent&
android:layout_height=&match_parent&
tools:context=&com.sloop.topbar.MainActivity& &
&com.example.custometopbarui.TopBar
android:id=&@+id/topbar&
android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:minHeight=&48dp&
app:leftBackground=&@drawable/ic_launcher&
app:leftText=&左侧&
app:rightBackground=&@drawable/ic_launcher&
app:rightText=&右侧&
app:titleText=&自定义标题&
app:titleTextColor=&#ffffff&
app:titleTextSize=&8sp& &
&/com.example.custometopbarui.TopBar&
&/RelativeLayout&TopBar(注释很详细)
package com.example.
import android.content.C
import android.content.res.TypedA
import android.graphics.C
import android.graphics.drawable.D
import android.util.AttributeS
import android.view.G
import android.view.V
import android.view.ViewG
import android.widget.B
import android.widget.RelativeL
import android.widget.TextV
public class TopBar extends RelativeLayout {
* 定义三个控件,
private Button leftButton, rightB
private TextV
//左侧button属性
private int leftTextC
private Drawable leftB
private String leftT
//右侧button属性
private int rightTextC
private Drawable rightB
private String rightT
//title属性
private int titleTextC
private float titleTextS
private String titleT
//因为容器
private LayoutParams leftParams, rirhtParams, titleP
//定义接口对象
private TopBarClickL
* 点击事件***器接口
* @author Administrator
public interface TopBarClickListener {
public void leftclick();
public void rightclick();
//设置***器
public void setOnTopBarClickListener(TopBarClickListener listener){
this.listener =
* 对外暴露的方法
* @param visible
public void setLeftIsVisible(boolean visible){
if (visible) {
leftButton.setVisibility(View.VISIBLE);
leftButton.setVisibility(View.GONE);
public void setRightIsVisible(boolean visible){
if (visible) {
rightButton.setVisibility(View.VISIBLE);
rightButton.setVisibility(View.GONE);
* @param context
* @param attrs
public TopBar(Context context, AttributeSet attrs){
super(context, attrs);
//获取自定义属性和值的映射集合
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
//取出自定义属性 - 左侧
leftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, Color.BLACK);
leftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);
leftText = ta.getString(R.styleable.TopBar_leftText);
//取出自定义属性 - 右侧
rightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, Color.BLACK);
rightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);
rightText = ta.getString(R.styleable.TopBar_rightText);
//取出自定义属性 - 标题
titleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor, Color.BLACK);
titleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize, 12);
titleText = ta.getString(R.styleable.TopBar_titleText);
//回收TypedArray(避免浪费资源,避免因为缓存导致的错误)
ta.recycle();
* 创建控件对象
leftButton = new Button(context);
rightButton = new Button(context);
title = new TextView(context);
* 为各个控件设置属性 - 左侧
leftButton.setText(leftText);
leftButton.setTextColor(leftTextColor);
leftButton.setBackground(leftBackground);
//设置属性 - 右侧
rightButton.setText(rightText);
rightButton.setTextColor(rightTextColor);
rightButton.setBackground(rightBackground);
//设置属性 - 标题
title.setText(titleText);
title.setTextSize(titleTextSize);
title.setTextColor(titleTextColor);
title.setGravity(Gravity.CENTER);
//设置整体背景颜色
setBackgroundColor(0xfff59563);
* 为各个控件设置布局 - 左
//设置宽高
leftParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//添加规则
leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
leftParams.addRule(RelativeLayout.CENTER_VERTICAL, TRUE);
addView(leftButton, leftParams);//将按钮添加进布局中
//设置布局 - 右
rirhtParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rirhtParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
rirhtParams.addRule(RelativeLayout.CENTER_VERTICAL, TRUE);
addView(rightButton, rirhtParams);//将按钮添加进布局中
//设置布局 - 标题
titleParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
titleParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
addView(title, titleParams);//将按钮添加进布局中
* 设置***器
leftButton.setOnClickListener(new OnClickListener() {
public void onClick(View v){
//这里写一个接口回调方法,让外部去实现即可
listener.leftclick();
rightButton.setOnClickListener(new OnClickListener() {
public void onClick(View v){
listener.rightclick();
MainActivity
package com.example.
import com.example.custometopbarui.TopBar.TopBarClickL
import android.app.A
import android.os.B
import android.widget.T
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TopBar topBar = (TopBar) findViewById(R.id.topbar);
topBar.setLeftIsVisible(false);
topBar.setRightIsVisible(false);
topBar.setOnTopBarClickListener(new TopBarClickListener() {
public void rightclick(){
Toast.makeText(MainActivity.this, &Right Clicked&, Toast.LENGTH_SHORT).show();
public void leftclick(){
Toast.makeText(MainActivity.this, &Left Clicked&, Toast.LENGTH_SHORT).show();
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:&&&&&&&&&&&&&&&原文:http://blog.csdn.net/u/article/details/
教程昨日排行
&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!

参考资料

 

随机推荐