尊重原创http://blog.csdn.net/yuanzeyao/article/details/40264433

好久都没有写文章了,现在利用周末的时间对一些知识进行总结,便于加深理解,今天我就来学习一下如何实现自定义ViewGroup

在Android中比较常用的布局LinearLayout,FrameLayout,RelativeLayout。。。这些布局都是继承自ViewGroup,通过这些布局,我们几乎可以实现Android中所有的界面开发,但是对于一些比较常见并且比较复杂的布局,使用这些基本布局开发的话,就会花大量的时间在一些重复的工作上,那么我们能不能模仿这些基本布局,根据自己的需求来实现一些自己的布局,以后需要的时候直接拿来用呢?当然是可以的。


类似扑克牌的布局


在斗地主的游戏中,我们会经常遇到类似这样的布局,我就将这个布局叫级联布局吧,这个布局使用RelativeLayout和margin等熟悉是可以做出来的,但是不是很方便,我们今天就这种需求为背景讲解一下自定义ViewGroup

在学习自定义布局前,读者最好先了解一下Android的布局好似怎么绘画出来的,我推荐大家去了解一下:都是官网的一些文章
1、http://developer.android.com/guide/topics/ui/how-android-draws.html
2、http://developer.android.com/reference/android/view/ViewGroup.html
3、http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html


通过以上几篇文章,我们需要了解一下知识:
1、绘制布局是由两个过程组成:测量过程和布局过程,测量过程使用measure(int,int)方法完成,遍历完成后,所有的View的大小都确定了,布局过程使用layout方法完成,就是通过View的大小,决定View放置在上面地方,不过通过源码中measure和layout方法都是final类型的,所以我们是无法改写的,之所以定义成final的,就是避免开发者破坏了布局的绘画流程,不过测量和布局的细节我们可以通过改写onMeasure和onLayout实现。


2、ViewGroup
在前面我就说过,Android中所有的布局都是继承自ViewGroup,ViewGroup就是一个View的容器,我们可以再里面放置任务的View,至于如何放置,我们可以通过onMeasure和onLayout来定义,onMeasure在measure调用,onLayout是在layout中调用的


3、ViewGroup.LayoutParams


这个类主要是View用来告诉他的父容器它想怎么显示,如宽、高、居中等等。ViewGroup.LayoutParams里面最重要的两个参数就是width,height。如果你想使用margin属性,那么必须使用ViewGroup.MarginLayoutParams这个类,这个类继承自ViewGroup.LayoutParams。添加了对margin属性的支持,如果你想加入更多的属性,可以自己定义一个LayoutParams类加入你需要的属性,事实上LinearLayout等布局都是继承自ViewGroup.MarginLayoutParams,并加入了自己需要的属性。




实现自己的布局CascadeLayout.java


/** * 自定义布局,用来实现扑克牌效果 * com.myviewgroup.CascadeLayout * @author yuanzeyao 
* create at 2014年10月19日 下午4:15:42 */public class CascadeLayout extends ViewGroup{ /** * 水平偏移距离 */ private int horizontal_space; /** * 垂直偏移距离 */ private int vertical_space; public CascadeLayout(Context context) { super(context); } public CascadeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initAttribute(context, attrs); } public CascadeLayout(Context context, AttributeSet attrs) { super(context, attrs); initAttribute(context, attrs); } /** * 根据xml文件中的属性对horizontal_space,vertical_space来赋值 * @param context * @param attrs */ private void initAttribute(Context context,AttributeSet attrs) { TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.CascadeLayout); horizontal_space=a.getDimensionPixelSize(R.styleable.CascadeLayout_horizontal_space,this.getResources().getDimensionPixelSize(R.dimen.cascade_horizontal_spacing)); vertical_space=a.getDimensionPixelSize(R.styleable.CascadeLayout_vertical_space, this.getResources().getDimensionPixelSize(R.dimen.cascade_vertical_spacing)); a.recycle(); } /** * onMeasure在measure中调用,参数分别是CascadeLayout的宽度和高度 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //使用measureChildren对所有的孩子进行测量,你也可以使用for循环,然后掉哦弄个measurechild函数进行测量 measureChildren(widthMeasureSpec, heightMeasureSpec); //将宽度和高度赋值为CascadeLayout的measureWidth,measureHeight变量 setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } /** * 在该函数中添加如何布置各个View的逻辑 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for(int i=0;i

在CascadeLayout中,我们定义了两个属性,horizontal_space,vertical_space,这两个属性用来记录该布局中每个子View之前的垂直距离和水平距离,他们都是在构造函数中初始化。由于我们添加了属性,所以在res/values中定义attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>                        

在layout文件中使用CascadeLayout


                           

运行效果如下



自定义自己的LayoutParams

在CascadeLayout中,我们使用的是ViewGroup.LayoutParams类,但是这个类仅仅有width和height属性,如果我们想加入其他属性,那么我们需要自己定义LayoutParams类
下面我们自定义自己的LayoutParams类,这个类继承ViewGroup.MarginLayoutParams,所以我自定义的LayoutPrams具有margin的属性



在CascadeLayout中定义如下LayoutParams类


public static class LayoutParams extends ViewGroup.MarginLayoutParams  {    /**     * 定义在垂直方向的偏移距离,可以覆盖CascadeLayout中的vertical_space属性     */    private int layout_vertical_spacing;    public LayoutParams(Context c, AttributeSet attrs)    {      super(c, attrs);      TypedArray a = c.obtainStyledAttributes(attrs,          R.styleable.CascadeLayout_LayoutParams);      try {        layout_vertical_spacing = a            .getDimensionPixelSize(                R.styleable.CascadeLayout_LayoutParams_layout_vertical_spacing,                -1);      } finally {        a.recycle();      }    }    public LayoutParams(int width, int height)    {      super(width, height);    }   }
该LayoutParams就支持了layout_vertical_spaceing属性了。
好了,先写到这里吧,有什么不懂的欢迎大家留言。。。



更多相关文章

  1. android中textview常见属性设置
  2. Android(安卓)View系统源码分析(十)—— View.setVisibility(int v
  3. 解析Android中使用自定义字体的实现方法
  4. Android(安卓)Flutter:Dart语言(布局篇)
  5. [Android]AndroidBucket增加碎片SubLayout功能及AISubLayout的注
  6. android 入门 001 (界面布局)
  7. android CheckBox RadioButton 照片和文字的间距问题
  8. Android手机应用开发(三) | Intent、Bundle的使用以及RecyclerView
  9. Android———Layout:LinearLayout

随机推荐

  1. android绕过设备锁(device lock)
  2. android 中imageview 与diallog综合应用
  3. Android(安卓)监听网络变化然后刷新页面
  4. You must supply a layout_width attribu
  5. android通过google api获取天气信息示例
  6. C# 实现 类似Android的Toast
  7. Android(安卓)Jetpack Compose 之 Text
  8. Android(安卓)Stagefright MPEG4Extracto
  9. Android(安卓)简单的网络变化监听器
  10. Android(安卓)Studio多个Module依赖同一