常用的四种标准自定义View方法

转载于 http://blog.csdn.net/makeyourchance/article/details/51884861

(二)继承ViewGroup派生特殊的Layout 
这是真真实实的造”轮子“呀!需要你自己写view的OnMeasure()跟OnLayout()过程的逻辑,如果想写一个listView+Scrollview的变异Layout,那你还要处理滑动冲突的问题,我原来一直不明白,为什么我一定要懂事件分发机制(内部是一个树形结构),它有什么用?我现在知道了,那就是几乎所有的自定义相关的View或ViewGroup它在写处理的逻辑的时候的基础就是事件分发,比如:自定义Draw(),它内部有一个dispatchDraw()方法,一看名字是不是非常熟悉,哈哈~没错,跟Event的分发很类似,然后,我又想到——-为什么我要学习《离散数学》了?如果当时老师告诉我学它是干嘛用的,我一定好好学习,学渣表示已经还给老师了~好了,我们废话不多说,走起! 
我们先来看自定义ViewGroup的OnMeasure()方法(为什么先从它开始,因为这个渣渣很容易出错,而且很关键); 
思路: 
以前我们自定义View的时候,我只需要考虑它自己的测量就可以了,现在ViewGroup中放了很多个View ,你说怎么测量?当然是把它(即遍历)出来,然后测出宽高,然后求和,就是我要画的总宽高了呀!当然,这里有个假设必须成立—–那就是子View的宽高均相等;结合具体情况具体使用呀~比如:我要定义一个类似水平的LinearLayout ,这时候我的高就等于第一个子View的高,而宽则等于所以子View的总和,why ?因为你看手机图片的时候是不是左右滑动,而不是上下滑动,对否~上代码:

  创建CoustomViewGroup.java  
 /**
     * view测量原理:
     *      主要是MeasureSpace代表一个int 32位的值,高俩位分别为spaceMode(即测量模式),spaceSize(即测量大小)
     *      那3中测试模式这里就赘述了,自己搜一下,主要针对说下宽高属性为wrap_content的情况,加入任一属性为wrap_content时,
     *      高(宽)需要在onMeasure()方法中做特殊处理,不复杂,就是给一个默认值比如:200dp ,我在网上看到很多人都喜欢使用这个数字,
     *      不知道为什么?若二者都为wrap_content ,简单那就在OnMeasure()中都做处理给个默认值呗,就这么简单!
     *
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //定义俩个保存子Viw的测量宽跟高的变量measureWidth和measureHeight
        int measureChildWidth = 0;
        int measureChildheight = 0;
        //获取子view的个数
        final int childCount = getChildCount();
        //测量ziView的宽高;
        measure(widthMeasureSpec , heightMeasureSpec);


        //下来就是套路了,确定测量的模式跟大小

        int widthSpaceMode = MeasureSpec.getMode(widthMeasureSpec);
        int widSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);

        //下来就是写自己的逻辑判断了,这里必须感谢下大牛----coder任玉刚 ,解决了我许久的困惑,
        // 比如:为什么自定义view时宽高设置为warp_content时,不重写OnMeasure()方法,View的效果等同于math_parent?

        //先来判断下有没有子元素,没有就不用测了直接置0
        if (childCount == 0){
            setMeasuredDimension(0,0);
        }else if ( (widthSpaceMode == MeasureSpec.AT_MOST) && (heightSpaceMode == MeasureSpec.AT_MOST) ){

            //还记得我们自定义View的时候的处理规范吗------setMeasuredDimension(200 , 200);

            //获取第一个子View的对象
            final View childView = getChildAt(0);
            measureChildWidth = childView.getMeasuredWidth() * childCount;
            measureChildheight = childView.getMeasuredHeight() * childCount;
            setMeasuredDimension(measureChildWidth , measureChildheight);

        }else if ( widthSpaceMode == MeasureSpec.AT_MOST ){

            //当宽属性为wrap_content时,需要所有子View的宽之和(记得我们是水平的呀)
            final View childView = getChildAt(0);
            measureChildWidth = childView.getMeasuredWidth() * childCount;
            setMeasuredDimension(measureChildWidth , heightSpaceSize);

        }else  if ( heightMeasureSpec == MeasureSpec.AT_MOST ){

            //注意:当高属性为wrap_content时,仅仅需要任一子View的高即可(记得我们是左右滑动的,高是不变得)
            final View childView = getChildAt(0);
            measureChildheight = childView.getMeasuredHeight() ;
            setMeasuredDimension(widSpaceSize , measureChildheight);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

代码中的注释已经很完整了,这里就不在解释代码了~~我们继续,

NPC “任教主”的温馨提示: 
上面的OnMeasure()方法有俩点不规范:

NO .1 当没有子元素的时候,不应该直接把宽高置为0 ,而应该根据LayoutParams中的宽高来做相应的处理; 
No.2 在测量CoustomViewGroup的时候没有考虑它的padding 跟子View的margin会影响到CoustomViewGroup的宽高,why ?因为不管是自己的padding或者是子View的margin占用的都是CoustomViewGroup的空间;

小伙伴们,可以自己实现下,我们主要学习流程就不走细节了,咱们继续,来看看自定义ViewGroup的OnLayout()的过程,走起!

/**
     * onLayout()过程主要用于确定view在ViewGroup中的摆放位置,通过确定View的L , T,R ,B四个坐标点;
     * @param b
     * @param i
     * @param i1
     * @param i2
     * @param i3
     */
    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {

        int childLeft = 0;
        final int childCount = getChildCount();
        //mChildrenSize = childCount;// 这里没搞懂为什么要把childCount赋值给mChildrenSize ?

        //接下来就是你熟悉的套路了,遍历每个子View并获取它们的位置, 从左向右
        for ( int n = 0 ; n < childCount ; n++){
            final  View childView = getChildAt(n);
            //View可见
            if ( (childView.getVisibility()) != View.GONE){
                    final int childWidth = childView.getMeasuredWidth();
                    mChildWidth = childWidth;
                    childView.layout(childLeft , 0 , childLeft + childWidth , childView.getMeasuredHeight());
                    childLeft += childWidth;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

同样任教主温馨提示时间到:

No.2 在测量CoustomViewGroup的时候没有考虑它的padding 跟子View的margin会影响到CoustomViewGroup的宽高,why ?因为不管是自己的padding或者是子View的margin占用的都是CoustomViewGroup的空间;

至于怎么处理让它变得规范,即具有处理padding及margin 的能力,就看法宝 吧~下面咱们继续分析:

我们都知道有View的监听方法有一个叫OnTechEvent()的,它里面有3个Action 分别为Action_Down , Action_Move ,ACtion_Up ,那么,我们从左向右滑动图片的时候,必然会涉及到这3个“怪”,我们要做的就是加入自己的逻辑判断规则,来劝化他们,怎么劝化呢?

我们可以这样: 
1)手指落下后会触发—-Down , 滑动会触发—-Move ,手指收起会触发—-Up ; 
2) 1怪跟3怪不足为虑,主要是2怪,它会产生一个滑动事件,我想让它把这个事件交给onTechEvent()的来处理,好那就重写onTechEvent()然而并没有什么卵用,这就涉及到一个事件分发的问题,至于事件分发的机制,网上多如牛毛,自己搜下吧~我们切入正题,事件分发机制遵循—–谁拦截谁负责到底的原则,当然,前提是onTechEvent()返回True (表示这个事件我来处理了) ,onInterceptTouchEvent()返回Treue(表示事件被截断,不在传递); 那么方法就是重写ViewGroup的onInterceptTouchEvent()方法,加入自己的逻辑判断;

好了,咱们明天继续~~你可以试着来实现一下!

你可能感兴趣的:(android)