zoukankan      html  css  js  c++  java
  • 定义你自己ViewGroup

    尊重原创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 <br/>
     * 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<getChildCount();i++)
        {
          //依次遍历孩子节点
          View child=getChildAt(i);
          
         // MarginLayoutParams mlp=(MarginLayoutParams) child.getLayoutParams();
          LayoutParams lp=child.getLayoutParams();
          //计算左边和顶部的距离
          int left=horizontal_space*i;
          int top=vertical_space*i;
          
          child.layout(left,top,left+child.getMeasuredWidth(),top+child.getMeasuredHeight());
        }
      }
      
      /**
       * 改写生成LayoutParams的方法
       */
      @Override
      protected LayoutParams generateLayoutParams(LayoutParams p)
      {
        //return super.generateLayoutParams(p);
        //return new ViewGroup.MarginLayoutParams(p.width,p.height);
        return new ViewGroup.LayoutParams(p.width,p.height);
      }
      
      @Override
      public LayoutParams generateLayoutParams(AttributeSet attrs)
      {
        //return super.generateLayoutParams(attrs);
        //return new ViewGroup.MarginLayoutParams(this.getContext(), attrs);
        return new ViewGroup.LayoutParams(this.getContext(), attrs);
      }
      
    }

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

    <?

    xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CascadeLayout"> <attr name="horizontal_space" format="dimension"></attr> <attr name="vertical_space" format="dimension"></attr> </declare-styleable> </resources>


    在layout文件里使用CascadeLayout


    <com.myviewgroup.CascadeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:cascade ="http://schemas.android.com/apk/res/com.myviewgroup"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        cascade:horizontal_space="40dip"
        cascade:vertical_space="40dip"
        
       >
       
        <View 
            android:layout_width="200dip"
            android:layout_height="200dip"
            android:background="@color/view1"
            />
        
         <View 
            android:layout_width="200dip"
            android:layout_height="200dip"
            android:background="@color/view2"
            />
         
          <View 
            android:layout_width="200dip"
            android:layout_height="200dip"
            android:background="@color/view3"
            />
    </com.myviewgroup.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属性了。
    好了,先写到这里吧,有什么不懂的欢迎大家留言。

    。。


    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    jquery判断设备是否是手机
    jQuery -- touch事件之滑动判断(左右上下方向)
    sass制作雪碧图
    js时间字符串转为标准时间
    装箱和拆箱
    Dictionary泛型集合实现山寨版金山词霸
    泛型集合
    ArrayList集合与索引器及Hashtable
    黑马程序员--静态方法和静态类
    黑马程序员--多态练习(手机工厂)
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4688139.html
Copyright © 2011-2022 走看看