《Android开发艺术探索》第三章重点笔记

                                                                               第三章   View的事件体系
           
   ##1、获得view的宽高坐标关系:
         width = right - left
         height= bottom - top
        
         Left = getLeft();
         Right = getRight();
         Top = getTop();
         Bottom = getBottom();

        getX/getY 返回的是相当于当前View左上角的x和y坐标,getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标。

   ##2、View事件的滑动三种方式
         第一种通过View本身提供的scrollTo/ScrollBy方法

  public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }


  public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

            第二种通过动画给View施加平移效果实现滑动

 ObjectAnimator.ofFloat(testButton,"translationX",0,100).setDuration(100).start();

            第三种通过改变View的LayoutParams使得view重新布局实现滑动

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) testButton.getLayoutParams();
layoutParams.width +=100;
layoutParams.leftMargin +=100;
testButton.requestLayout();
//或者testButton.setLayoutParams(layoutParams);

                 1、使用scrollTo和scrollBy方法只能改变view内容的位置而不能改变view在布局中的位置。 scrollBy是基于当前位置的相对滑动,而scrollTo是基于所传参数的绝对滑动。通过View的getScrollX和getScrollY方法可以得到滑动的距离。

                 2、使用动画 使用动画来移动view主要是操作view的translationX和translationY属性,既可以使用传统的view动画,也可以使用属性动画,使用后者需要考虑兼容性问题,如果要兼容Android3.0一下版本系统的话推荐使用nineoldandroids。使用动画还存在一个交互问题:在android3.0以前的系统上,view动画和属性动画,新位置均无法触发点击事件,同时,老位置仍然可以触发单击事件。从3.0开始,属性动画的单击事件触发位置为移动后的位置,view动画仍然在原位置。

                 3、改变布局参数 通过改变LayoutParams的方式去实现View的滑动是一种灵活的方法。

                 4、各种滑动方式的对比
                            scrollTo/scrollBy:操作简单,适合对View内容的滑动
                            动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果
                            改变布局参数:操作稍微复杂,适用于有交互的View
          

   ##3、View的事件分发机制

                public boolean dispatchTouchEvent(MotionEvent ev)

                如果事件能够传递给当前的View会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
                
                public boolean onInterceptTouchEvent(MotionEvent event)

              用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
                
                public boolean onTouchEvent(MotionEvent event)
                在dispatchTouchEvent方法中调用,用来处理点击事件,返回表示是否消耗当前的事件,如果不消耗,则同一个事件序列中,当前View无法再次接受到事件。
                
                这三个方法的关系可以用如下伪代码表示:
                public boolean dispatchTouchEvent(MotionEvent event)
                {
                    boolean consume = false;
                    if(onInterceptTouchEvent(ev))
                    {
                        consume = onTouchEvent(ev);
                    }
                    else
                    {
                        consume = child.dispatchTouchEvent(ev);
                    }
                    return consume;
                }
                
                         

     ##4、View滑动冲突
                  一、滑动冲突场景
                               1、外部左右滑动,内部上下滑动   (viewpager和listview的嵌套滑动)
                               2、内外层两层同时上下滑动或者左右滑动
                               3、内层有场景1滑动,外层场景2滑动,外层与中层的滑动方向一致,而中层与内层的滑动方向不一致
                
                  二、滑动冲突的处理规则

                             场景1的处理规则:
                            1、当用户左右滑动时,让外部的View拦截点击事件,当用户上下滑动时,让内部的View拦截点击事件;
                            2、判断用户的滑动方向,如果用户手指滑动的水平距离大于垂直距离,则左右滑动,反之,上下滑动;还可以根据角度、速度差来做判断;
                            
                            场景2的处理规则:
                            当处于某种状态需要外部View响应用户的滑动,而处于另一种状态时则需要内部View响应用户的滑动,可以根据业务的需求得出相应的处理规则。
                            
                            场景3的处理规则:场景1的处理规则和场景2的处理规则一起用。

                   三、滑动冲突解决方法            

                        针对场景一有两种解决滑动冲突的方法:外部拦截法和内部拦截法
                       外部拦截法是指点击事件都先经过父容器的onInterceptTouchEvent方法拦截处理,如果父容器需要此事件就拦截,否则就不拦截        伪代码如下:
                          public  boolean onInterceptTouchEvent (MotionEvent event) {
                              boolean  intercepted = false ;
                              int x = (int) event.getX();
                              int y = (int) event.getY();
                             switch(event.getAction()){
                               case MotionEvent.ACTION_DOWN:  {
                                         intercepted = false;
                                         break;              
                                     }
                               case MotionEvent.ACTION_MOVE:  {
                                       if (父容器需要处理当前事件){
                                         intercepted = true;
                                        } else{
                                           intercepted = false;
                                         }       
                                       break;              
                                     }
                
                               case MotionEvent.ACTION_UP:  {
                                         intercepted = false;
                                         break;              
                                     }
                        
                              default:
                              break;   
                            }
                            mLastXIntercept = x;
                            mLastYIntercept = Y;
                            return intercepted;
                          }
                
                
                   内部拦截法指父容器不拦截任何事件,需要配合requestDisallowInterceptTouchEvent方法工作,重写子元素的dispathTouchEvent方法:
                     
                         public  boolean dispatchtTouchEvent (MotionEvent event) {
                                         int x = (int) event.getX();
                                          int y = (int) event.getY();
                                         switch (event.getAction() ) {
                                              case MotionEvent.ACTION_DOWN:  {
                                                   parent.requestDisallowInterceptTouchEvent(true);
                                                    break;             
                                                          }
                                             case MotionEvent.ACTION_MOVE:  {
                                                     int  deltaX = x - mLastX;
                                                     int  deltaY = y - mLastY;                
                                                     if (父容器需要处理当前事件){
                                                        parent.requestDisallowInterceptTouchEvent(false);
                                                        }       
                                                       break;              
                                                      }                                 
                                              case MotionEvent.ACTION_UP:  {
                                                         break;              
                                                      }  
                                                  default:
                                                  break;   
                                                }
                                                mLastX = x;
                                                mLastY= Y;
                                                return super.dispatchTouchEvent(event);
                                              }

针对场景二有两种解决滑动冲突的方法:外部拦截法和内部拦截法
   
假设需求:用一个竖直的ScrollView嵌套一个ListView做例子,当ListView滑到顶部了,并且继续向下滑就让ScrollView拦截掉;当ListView滑到底部了,并且继续向下滑,就让ScrollView拦截掉,其余时候都交给ListView自身处理事件

公共部分就是判断  滑动是否到达listView的顶部或者底部

   public boolean isBottom(final ListView listView) {
        boolean result=false;
        if (listView.getLastVisiblePosition() == (listView.getCount() - 1)) {
            final View bottomChildView = listView.getChildAt(listView.getLastVisiblePosition() - listView.getFirstVisiblePosition());
            result= (listView.getHeight()>=bottomChildView.getBottom());
        };
        return  result;
    }

    public boolean isTop(final ListView listView) {
        boolean result=false;
        if(listView.getFirstVisiblePosition()==0){
            final View topChildView = listView.getChildAt(0);
            result=topChildView.getTop()==0;
        }
        return result ;
    }


第一种    :外部拦截法   重写ScrollView的onInterceptTouchEvent方法

    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int y = (int) event.getY();
        int removeY = 0; //移动Y的距离
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN: {
                removeY = y;
                //ScrollView在Down方法时需要初始化一些参数
                intercepted = super.onInterceptTouchEvent(event);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if(isTop(mListView) && y>removeY){
                    intercepted = true;
                    break;
                }
                else if(isBottom(mListView) && y<removeY){
                    intercepted = true;
                    break;
                }
                intercepted = false;
                break;
            }
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }

        return intercepted;
    }
第二种  :内部拦截法   重写ListView的dispatchTouchEvent方法
 public boolean dispatchTouchEvent(MotionEvent event) {
        int y = (int) event.getY();
        int removeY = 0; //移动Y的距离
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                removeY = y;
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if(isTop(mListView) && y>removeY){
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
                }
                else if(isBottom(mListView) && y<removeY){
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }
        return super.dispatchTouchEvent(event);
   }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值