zoukankan      html  css  js  c++  java
  • Android——滑动事件冲突解决

    android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件。

    android系统中的每个View的子类都具有下面三个与TouchEvent处理密切相关的方法:

    (1)public boolean dispatchTouchEvent(MotionEvent ev)这个方法用来分发TouchEvent

    (2)public boolean onInterceptTouchEvent(MotionEvent ev)这个方法用来拦截TouchEvent

    (3)public boolean onTouchEvent(MotionEvent ev)这个方法用来处理TouchEvent

    当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层View的dispatchTouchEvent,然后由dispatchTouchEvent方法进行分发。

    如果dispatchTouchEvent返回true,则交给这个view的onTouchEvent处理;如果返回false,则交给这个view的interceptTouchEvent方法来决定是否要拦截这个事件。

    如果interceptTouchEvent返回true,也就是拦截了,则交给它的onTouchEvent来处理,如果interceptTouchEvent返回false,则传递给子view,由子view的dispatchTouchEvent再开始这个事件分发。

    如果事件传递到某一层的子view的onTouchEvent上了,这个方法返回了false,则表明该onTouchEvent未处理完毕,那么这个事件会从这个view往上传递,都是onTouchEvent来接收。而如果传递到最上面的onTouchEvent也返回false的话,这个事件就好消失,而且接收不到下一次事件。

    如果onTouchEvent返回true,则事件处理完毕,不会传递。

    让子view先处理的方法是重写父view的onInterceptTouchEvent事件并返回false

    1 public boolean onInterceptTouchEvent(MotionEvent ev){
    2     return false;
    3 }

    转自:http://blog.csdn.net/spt110/article/details/7919870

    转自: http://blog.csdn.net/a992036795/article/details/51735501

    一、前言 
    Android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。 
    滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。 
    所以我就写了4个例子来学习如何解决滑动冲突的,这四个例子分别为: 外部拦截法解决横竖冲突、外部拦截法解决同向冲突、内部拦截法解决横竖冲突、内部拦截法解决同向冲突。 
    先上效果图: 
    这里写图片描述

    二、实战 
    1、外部拦截法,解决横竖冲突 
    思路是,重写父控件的onInterceptTouchEvent方法,然后根据具体的需求,来决定父控件是否拦截事件。如果拦截返回返回true,不拦截返回false。关于为什么返回true就代表拦截事件。可以参考我的上一篇博客:http://blog.csdn.net/a992036795/article/details/51698023 。 如果父控件拦截了事件,则在父控件的onTouchEvent进行相应的事件处理。 
    我的这个例子,是一个横向滑动的ViewGroup里面包含了3个竖向滑动的ListView。下面我附上代码: 
    HorizontalEx.java

      1 /**
      2  * Created by blueberry on 2016/6/20.
      3  *
      4  * 解决交错的滑动冲突
      5  *
      6  * 外部拦截法
      7  */
      8 public class HorizontalEx extends ViewGroup {
      9 
     10     private static final String TAG = "HorizontalEx";
     11 
     12     private boolean isFirstTouch = true;
     13     private int childIndex;
     14     private int childCount;
     15     private int lastXIntercept, lastYIntercept, lastX, lastY;
     16 
     17     private Scroller mScroller;
     18     private VelocityTracker mVelocityTracker;
     19 
     20     public HorizontalEx(Context context) {
     21         super(context);
     22         init();
     23     }
     24 
     25     public HorizontalEx(Context context, AttributeSet attrs) {
     26         super(context, attrs);
     27         init();
     28     }
     29 
     30     public HorizontalEx(Context context, AttributeSet attrs, int defStyleAttr) {
     31         super(context, attrs, defStyleAttr);
     32         init();
     33     }
     34 
     35     private void init() {
     36         mScroller = new Scroller(getContext());
     37         mVelocityTracker = VelocityTracker.obtain();
     38     }
     39 
     40     @Override
     41     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     42         int width = MeasureSpec.getSize(widthMeasureSpec);
     43         int height = MeasureSpec.getSize(heightMeasureSpec);
     44         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     45         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     46 
     47         childCount = getChildCount();
     48         measureChildren(widthMeasureSpec, heightMeasureSpec);
     49 
     50         if (childCount == 0) {
     51             setMeasuredDimension(0, 0);
     52         } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
     53             width = childCount * getChildAt(0).getMeasuredWidth();
     54             height = getChildAt(0).getMeasuredHeight();
     55             setMeasuredDimension(width, height);
     56         } else if (widthMode == MeasureSpec.AT_MOST) {
     57             width = childCount * getChildAt(0).getMeasuredWidth();
     58             setMeasuredDimension(width, height);
     59         } else {
     60             height = getChildAt(0).getMeasuredHeight();
     61             setMeasuredDimension(width, height);
     62         }
     63     }
     64 
     65     @Override
     66     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     67         int left = 0;
     68         for (int i = 0; i < getChildCount(); i++) {
     69             final View child = getChildAt(i);
     70             child.layout(left + l, t, r + left, b);
     71             left += child.getMeasuredWidth();
     72         }
     73     }
     74 
     75     /**
     76      * 拦截事件
     77      * @param ev
     78      * @return
     79      */
     80     @Override
     81     public boolean onInterceptTouchEvent(MotionEvent ev) {
     82         boolean intercepted = false;
     83         int x = (int) ev.getX();
     84         int y = (int) ev.getY();
     85 
     86         switch (ev.getAction()) {
     87             /*如果拦截了Down事件,则子类不会拿到这个事件序列*/
     88             case MotionEvent.ACTION_DOWN:
     89                 lastXIntercept = x;
     90                 lastYIntercept = y;
     91                 intercepted = false;
     92                 if (!mScroller.isFinished()) {
     93                     mScroller.abortAnimation();
     94                     intercepted = true;
     95                 }
     96                 break;
     97             case MotionEvent.ACTION_MOVE:
     98                 final int deltaX = x - lastXIntercept;
     99                 final int deltaY = y - lastYIntercept;
    100                 /*根据条件判断是否拦截该事件*/
    101                 if (Math.abs(deltaX) > Math.abs(deltaY)) {
    102                     intercepted = true;
    103                 } else {
    104                     intercepted = false;
    105                 }
    106                 break;
    107             case MotionEvent.ACTION_UP:
    108                 intercepted = false;
    109                 break;
    110 
    111         }
    112         lastXIntercept = x;
    113         lastYIntercept = y;
    114         return intercepted;
    115     }
    116 
    117 
    118     @Override
    119     public boolean onTouchEvent(MotionEvent event) {
    120         int x = (int) event.getX();
    121         int y = (int) event.getY();
    122         mVelocityTracker.addMovement(event);
    123         ViewConfiguration configuration = ViewConfiguration.get(getContext());
    124         switch (event.getAction()) {
    125             case MotionEvent.ACTION_DOWN:
    126                 if (!mScroller.isFinished()) {
    127                     mScroller.abortAnimation();
    128                 }
    129                 break;
    130             case MotionEvent.ACTION_MOVE:
    131                 /*因为这里父控件拿不到Down事件,所以使用一个布尔值,
    132                     当事件第一次来到父控件时,对lastX,lastY赋值*/
    133                 if (isFirstTouch) {
    134                     lastX = x;
    135                     lastY = y;
    136                     isFirstTouch = false;
    137                 }
    138                 final int deltaX = x - lastX;
    139                 scrollBy(-deltaX, 0);
    140                 break;
    141             case MotionEvent.ACTION_UP:
    142                 int scrollX = getScrollX();
    143                 final int childWidth = getChildAt(0).getWidth();
    144                 mVelocityTracker.computeCurrentVelocity(1000, configuration.getScaledMaximumFlingVelocity());
    145                 float xVelocity = mVelocityTracker.getXVelocity();
    146                 if (Math.abs(xVelocity) > configuration.getScaledMinimumFlingVelocity()) {
    147                     childIndex = xVelocity < 0 ? childIndex + 1 : childIndex - 1;
    148                 } else {
    149                     childIndex = (scrollX + childWidth / 2) / childWidth;
    150                 }
    151                 childIndex = Math.min(getChildCount() - 1, Math.max(childIndex, 0));
    152                 smoothScrollBy(childIndex * childWidth - scrollX, 0);
    153                 mVelocityTracker.clear();
    154                 isFirstTouch = true;
    155                 break;
    156         }
    157 
    158         lastX = x;
    159         lastY = y;
    160         return true;
    161     }
    162 
    163     void smoothScrollBy(int dx, int dy) {
    164         mScroller.startScroll(getScrollX(), getScrollY(), dx, dy, 500);
    165         invalidate();
    166     }
    167 
    168     @Override
    169     public void computeScroll() {
    170         if (mScroller.computeScrollOffset()) {
    171             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    172             invalidate();
    173         }
    174     }
    175 
    176     @Override
    177     protected void onDetachedFromWindow() {
    178         super.onDetachedFromWindow();
    179         mVelocityTracker.recycle();
    180     }
    181 }

    调用代码:

     1 @Override
     2     public void showOutHVData(List<String> data1, List<String> data2, List<String> data3) {
     3         ListView listView1 = new ListView(getContext());
     4         ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data1);
     5         listView1.setAdapter(adapter1);
     6 
     7         ListView listView2 = new ListView(getContext());
     8         ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data2);
     9         listView2.setAdapter(adapter2);
    10 
    11         ListView listView3 = new ListView(getContext());
    12         ArrayAdapter<String> adapter3 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data3);
    13         listView3.setAdapter(adapter3);
    14 
    15         ViewGroup.LayoutParams params
    16                 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    17                 ViewGroup.LayoutParams.MATCH_PARENT);
    18 
    19         mHorizontalEx.addView(listView1, params);
    20         mHorizontalEx.addView(listView2, params);
    21         mHorizontalEx.addView(listView3, params);
    22     }

    其实外部拦截的主要思想都在于对onInterceptTouchEvent的重写。

     1 @Override
     2     public boolean onInterceptTouchEvent(MotionEvent ev) {
     3         boolean intercepted = false;
     4         int x = (int) ev.getX();
     5         int y = (int) ev.getY();
     6 
     7         switch (ev.getAction()) {
     8             /*如果拦截了Down事件,则子类不会拿到这个事件序列*/
     9             case MotionEvent.ACTION_DOWN:
    10                 lastXIntercept = x;
    11                 lastYIntercept = y;
    12                 intercepted = false;
    13                 if (!mScroller.isFinished()) {
    14                     mScroller.abortAnimation();
    15                     intercepted = true;
    16                 }
    17                 break;
    18             case MotionEvent.ACTION_MOVE:
    19                 final int deltaX = x - lastXIntercept;
    20                 final int deltaY = y - lastYIntercept;
    21                 /*根据条件判断是否拦截该事件*/
    22                 if (Math.abs(deltaX) > Math.abs(deltaY)) {
    23                     intercepted = true;
    24                 } else {
    25                     intercepted = false;
    26                 }
    27                 break;
    28             case MotionEvent.ACTION_UP:
    29                 intercepted = false;
    30                 break;
    31 
    32         }
    33         lastXIntercept = x;
    34         lastYIntercept = y;
    35         return intercepted;
    36     }

    这几乎是一个实现外部拦截事件的模板,这里一定不要在ACTION_DOWN 中返回 true,否则会让子VIew没有机会得到事件,因为如果在ACTION_DOWN的时候返回了 true,同一个事件序列ViewGroup的disPatchTouchEvent就不会在调用onInterceptTouchEvent方法了,如果不明白可以参考我的上一遍文章。 
    还有就是 在ACTION_UP中返回false,因为如果父控件拦截了ACTION_UP,那么子View将得不到UP事件,那么将会影响子View的 Onclick方法等。但这对父控件是没有影响的,因为如果是父控件子ACITON_MOVE中 就拦截了事件,他们UP事件必定也会交给它处理,因为有那么一条定律叫做:父控件一但拦截了事件,那么同一个事件序列的所有事件都将交给他处理。这条结论在我的上一篇文章中已经分析过。 
    最后就是在 ACTION_MOVE中根据需求决定是否拦截

    2、内部拦截法,解决横竖冲突 
    内部拦截主要依赖于父控件的 requestDisallowInterceptTouchEvent方法,关于这个方法我的上篇文章其实已经分析过。他设置父控件的一个标志(FLAG_DISALLOW_INTERCEPT) 
    这个标志可以决定父控件是否拦截事件,如果设置了这个标志则不拦截,如果没设这个标志,它就会调用父控件的onInterceptTouchEvent()来询问父控件是否拦截。但这个标志对Down事件无效。 
    可以参考一下源码: 
    ViewGroup#dispatchTouchEvent

     1 // Handle an initial down.
     2             if (actionMasked == MotionEvent.ACTION_DOWN) {
     3                 // Throw away all previous state when starting a new touch gesture.
     4                 // The framework may have dropped the up or cancel event for the previous gesture
     5                 // due to an app switch, ANR, or some other state change.
     6                 cancelAndClearTouchTargets(ev);
     7                 //清楚标志
     8                 resetTouchState();
     9             }
    10 
    11             // Check for interception.
    12             final boolean intercepted;
    13             if (actionMasked == MotionEvent.ACTION_DOWN
    14                     || mFirstTouchTarget != null) {
    15                     //标志
    16                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    17                 if (!disallowIntercept) {
    18                     intercepted = onInterceptTouchEvent(ev);
    19                     ev.setAction(action); // restore action in case it was changed
    20                 } else {
    21                     intercepted = false;
    22                 }
    23             } else {
    24                 // There are no touch targets and this action is not an initial down
    25                 // so this view group continues to intercept touches.
    26                 intercepted = true;
    27             }

    那么我们如果想使用 内部拦截法拦截事件。 
    第一步: 
    a、我们要重写父控件的onInterceptTouchEvent,在ACTION_DOWN的时候返回false,负责的话子View调用requestDisallowInterceptTouchEvent也将无能为力。 
    b、还有就是其他事件的话都返回true,这样就把能否拦截事件的权利交给了子View。

    第二步: 
    在子View的dispatchTouchEvent中 来决定是否让父控件拦截事件。 
    a. 先要在MotionEvent.ACTION_DOWN:的时候使用mHorizontalEx2.requestDisallowInterceptTouchEvent(true);,负责的话,下一个事件到来时,就交给父控件了。 
    b. 然后在MotionEvent.ACTION_MOVE: 根据业务逻辑决定是否调用mHorizontalEx2.requestDisallowInterceptTouchEvent(false);来决定父控件是否拦截事件。 
    上代码: 
    HorizontalEx2.java

      1 /**
      2  * Created by blueberry on 2016/6/20.
      3  * <p/>
      4  * 内部拦截
      5  * 和 ListViewEx配合使用
      6  */
      7 public class HorizontalEx2 extends ViewGroup {
      8 
      9     private int lastX, lastY;
     10     private int childIndex;
     11     private Scroller mScroller;
     12     private VelocityTracker mVelocityTracker;
     13 
     14     public HorizontalEx2(Context context) {
     15         super(context);
     16         init();
     17     }
     18 
     19     public HorizontalEx2(Context context, AttributeSet attrs) {
     20         super(context, attrs);
     21         init();
     22     }
     23 
     24     public HorizontalEx2(Context context, AttributeSet attrs, int defStyleAttr) {
     25         super(context, attrs, defStyleAttr);
     26         init();
     27     }
     28 
     29     private void init() {
     30         mScroller = new Scroller(getContext());
     31         mVelocityTracker = VelocityTracker.obtain();
     32     }
     33 
     34     @Override
     35     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     36         int width = MeasureSpec.getSize(widthMeasureSpec);
     37         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     38         int height = MeasureSpec.getSize(heightMeasureSpec);
     39         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     40 
     41         int childCount = getChildCount();
     42         measureChildren(widthMeasureSpec, heightMeasureSpec);
     43 
     44         if (childCount == 0) {
     45             setMeasuredDimension(0, 0);
     46         } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
     47             height = getChildAt(0).getMeasuredHeight();
     48             width = childCount * getChildAt(0).getMeasuredWidth();
     49             setMeasuredDimension(width, height);
     50         } else if (widthMode == MeasureSpec.AT_MOST) {
     51             width = childCount * getChildAt(0).getMeasuredWidth();
     52             setMeasuredDimension(width, height);
     53         } else {
     54             height = getChildAt(0).getMeasuredHeight();
     55             setMeasuredDimension(width, height);
     56         }
     57     }
     58 
     59     @Override
     60     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     61         int leftOffset = 0;
     62         for (int i = 0; i < getChildCount(); i++) {
     63             View child = getChildAt(i);
     64             child.layout(l + leftOffset, t, r + leftOffset, b);
     65             leftOffset += child.getMeasuredWidth();
     66         }
     67     }
     68 
     69     /**
     70      * 不拦截Down事件,其他一律拦截
     71      * @param ev
     72      * @return
     73      */
     74     @Override
     75     public boolean onInterceptTouchEvent(MotionEvent ev) {
     76         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
     77             if (!mScroller.isFinished()) {
     78                 mScroller.abortAnimation();
     79                 return true;
     80             }
     81             return false;
     82         } else {
     83             return true;
     84         }
     85     }
     86 
     87     private boolean isFirstTouch = true;
     88 
     89     @Override
     90     public boolean onTouchEvent(MotionEvent event) {
     91         int x = (int) event.getX();
     92         int y = (int) event.getY();
     93         mVelocityTracker.addMovement(event);
     94         ViewConfiguration configuration = ViewConfiguration.get(getContext());
     95         switch (event.getAction()) {
     96             case MotionEvent.ACTION_DOWN:
     97                 if (!mScroller.isFinished()) {
     98                     mScroller.abortAnimation();
     99                 }
    100                 break;
    101             case MotionEvent.ACTION_MOVE:
    102                 if (isFirstTouch) {
    103                     isFirstTouch = false;
    104                     lastY = y;
    105                     lastX = x;
    106                 }
    107                 final int deltaX = x - lastX;
    108                 scrollBy(-deltaX, 0);
    109                 break;
    110             case MotionEvent.ACTION_UP:
    111                 isFirstTouch = true;
    112                 int scrollX = getScrollX();
    113                 mVelocityTracker.computeCurrentVelocity(1000, configuration.getScaledMaximumFlingVelocity());
    114                 float mVelocityX = mVelocityTracker.getXVelocity();
    115                 if (Math.abs(mVelocityX) > configuration.getScaledMinimumFlingVelocity()) {
    116                     childIndex = mVelocityX < 0 ? childIndex + 1 : childIndex - 1;
    117                 } else {
    118                     childIndex = (scrollX + getChildAt(0).getWidth() / 2) / getChildAt(0).getWidth();
    119                 }
    120                 childIndex = Math.min(getChildCount() - 1, Math.max(0, childIndex));
    121                 smoothScrollBy(childIndex*getChildAt(0).getWidth()-scrollX,0);
    122                 mVelocityTracker.clear();
    123                 break;
    124         }
    125 
    126         lastX = x;
    127         lastY = y;
    128         return true;
    129     }
    130 
    131     private void smoothScrollBy(int dx, int dy) {
    132         mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,500);
    133         invalidate();
    134     }
    135 
    136     @Override
    137     public void computeScroll() {
    138         if(mScroller.computeScrollOffset()){
    139             scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
    140             postInvalidate();
    141         }
    142     }
    143 
    144     @Override
    145     protected void onDetachedFromWindow() {
    146         super.onDetachedFromWindow();
    147         mVelocityTracker.recycle();
    148     }
    149 }

    ListViewEx.java

     1 /**
     2  * Created by blueberry on 2016/6/20.
     3  * 内部拦截事件
     4  */
     5 public class ListViewEx extends ListView {
     6 
     7     private int lastXIntercepted, lastYIntercepted;
     8 
     9     private HorizontalEx2 mHorizontalEx2;
    10 
    11     public ListViewEx(Context context) {
    12         super(context);
    13     }
    14 
    15     public ListViewEx(Context context, AttributeSet attrs) {
    16         super(context, attrs);
    17     }
    18 
    19     public ListViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
    20         super(context, attrs, defStyleAttr);
    21     }
    22 
    23     public HorizontalEx2 getmHorizontalEx2() {
    24         return mHorizontalEx2;
    25     }
    26 
    27     public void setmHorizontalEx2(HorizontalEx2 mHorizontalEx2) {
    28         this.mHorizontalEx2 = mHorizontalEx2;
    29     }
    30 
    31     /**
    32      * 使用 outter.requestDisallowInterceptTouchEvent();
    33      * 来决定父控件是否对事件进行拦截
    34      * @param ev
    35      * @return
    36      */
    37     @Override
    38     public boolean dispatchTouchEvent(MotionEvent ev) {
    39         int x = (int) ev.getX();
    40         int y = (int) ev.getY();
    41         switch (ev.getAction()) {
    42             case MotionEvent.ACTION_DOWN:
    43                 mHorizontalEx2.requestDisallowInterceptTouchEvent(true);
    44                 break;
    45             case MotionEvent.ACTION_MOVE:
    46                 final int deltaX = x-lastYIntercepted;
    47                 final int deltaY = y-lastYIntercepted;
    48                 if(Math.abs(deltaX)>Math.abs(deltaY)){
    49                     mHorizontalEx2.requestDisallowInterceptTouchEvent(false);
    50                 }
    51                 break;
    52             case MotionEvent.ACTION_UP:
    53                 break;
    54         }
    55         lastXIntercepted = x;
    56         lastYIntercepted = y;
    57         return super.dispatchTouchEvent(ev);
    58     }
    59 }

    调用代码:

     1 @Override
     2     public void showInnerHVData(List<String> data1, List<String> data2, List<String> data3) {
     3 
     4         ListViewEx listView1 = new ListViewEx(getContext());
     5         ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data1);
     6         listView1.setAdapter(adapter1);
     7         listView1.setmHorizontalEx2(mHorizontalEx2);
     8 
     9         ListViewEx listView2 = new ListViewEx(getContext());
    10         ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data2);
    11         listView2.setAdapter(adapter2);
    12         listView2.setmHorizontalEx2(mHorizontalEx2);
    13 
    14         ListViewEx listView3 = new ListViewEx(getContext());
    15         ArrayAdapter<String> adapter3 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data3);
    16         listView3.setAdapter(adapter3);
    17         listView3.setmHorizontalEx2(mHorizontalEx2);
    18 
    19         ViewGroup.LayoutParams params
    20                 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    21                 ViewGroup.LayoutParams.MATCH_PARENT);
    22 
    23         mHorizontalEx2.addView(listView1, params);
    24         mHorizontalEx2.addView(listView2, params);
    25         mHorizontalEx2.addView(listView3, params);
    26     }

    至此,2种拦截方法已经学习完毕,下面我们来学习如何解决同向滑动冲突。 
    其实和上面的2个例子思路是一样的,只是用来判断是否拦截的那块逻辑不同而已。

    下面的例子,是一个下拉刷新的一个控件。

    3、外部拦截 解决同向滑动冲突 
    RefreshLayoutBase.java

      1 package com.blueberry.sample.widget.refresh;
      2 
      3 import android.content.Context;
      4 import android.graphics.Color;
      5 import android.util.AttributeSet;
      6 import android.util.DisplayMetrics;
      7 import android.util.Log;
      8 import android.util.TypedValue;
      9 import android.view.MotionEvent;
     10 import android.view.View;
     11 import android.view.ViewConfiguration;
     12 import android.view.ViewGroup;
     13 import android.view.WindowManager;
     14 import android.widget.ProgressBar;
     15 import android.widget.Scroller;
     16 import android.widget.TextView;
     17 
     18 import com.blueberry.sample.R;
     19 
     20 /**
     21  * Created by blueberry on 2016/6/21.
     22  *
     23  *外部拦截(同向)
     24  *
     25  */
     26 public abstract class RefreshLayoutBase<T extends View> extends ViewGroup {
     27 
     28     private static final String TAG = "RefreshLayoutBase";
     29 
     30     public static final int STATUS_LOADING = 1;
     31     public static final int STATUS_RELEASE_TO_REFRESH = 2;
     32     public static final int STATUS_PULL_TO_REFRESH = 3;
     33     public static final int STATUS_IDLE = 4;
     34     public static final int STATUS_LOAD_MORE =5;
     35     private static int SCROLL_DURATION =500;
     36 
     37     protected ViewGroup mHeadView;
     38     protected ViewGroup mFootView;
     39     private T contentView;
     40     private ProgressBar headProgressBar;
     41     private TextView headTv;
     42     private ProgressBar footProgressBar;
     43     private TextView footTv;
     44 
     45     private boolean isFistTouch = true;
     46 
     47     protected int currentStatus = STATUS_IDLE;
     48     private int mScreenWidth;
     49     private int mScreenHeight;
     50     private int mLastXIntercepted;
     51     private int mLastYIntercepted;
     52     private int mLastX;
     53     private int mLastY;
     54     protected int mInitScrollY = 0;
     55     private int mTouchSlop;
     56 
     57     protected Scroller mScoller;
     58 
     59     private OnRefreshListener mOnRefreshListener;
     60 
     61     public RefreshLayoutBase(Context context) {
     62         this(context, null);
     63     }
     64 
     65     public RefreshLayoutBase(Context context, AttributeSet attrs) {
     66         this(context, attrs, 0);
     67     }
     68 
     69     public RefreshLayoutBase(Context context, AttributeSet attrs, int defStyleAttr) {
     70         super(context, attrs, defStyleAttr);
     71         getScreenSize();
     72         initView();
     73         mScoller = new Scroller(context);
     74         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     75         setPadding(0, 0, 0, 0);
     76     }
     77 
     78     public void setContentView(T view) {
     79         addView(view, 1);
     80     }
     81 
     82     public OnRefreshListener getOnRefreshListener() {
     83         return mOnRefreshListener;
     84     }
     85 
     86     public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
     87         this.mOnRefreshListener = mOnRefreshListener;
     88     }
     89 
     90     private void initView() {
     91         setupHeadView();
     92         setupFootView();
     93     }
     94 
     95     private void getScreenSize() {
     96         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
     97         DisplayMetrics metrics = new DisplayMetrics();
     98         wm.getDefaultDisplay().getMetrics(metrics);
     99         mScreenWidth = metrics.widthPixels;
    100         mScreenHeight = metrics.heightPixels;
    101     }
    102 
    103     private int dp2px(int dp) {
    104         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    105         DisplayMetrics metrics = new DisplayMetrics();
    106         wm.getDefaultDisplay().getMetrics(metrics);
    107         return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
    108     }
    109 
    110     /**
    111      * 设置头布局
    112      */
    113     private void setupHeadView() {
    114         mHeadView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_head_view, null);
    115         mHeadView.setBackgroundColor(Color.RED);
    116         headProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressbar);
    117         headTv = (TextView) mHeadView.findViewById(R.id.head_tv);
    118         /*设置 实际高度为 1/4 ,但内容区域只有 100dp*/
    119         ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, mScreenHeight / 4);
    120         mHeadView.setLayoutParams(layoutParams);
    121         mHeadView.setPadding(0, mScreenHeight / 4 - dp2px(100), 0, 0);
    122         addView(mHeadView);
    123     }
    124 
    125     /**
    126      * 设置尾布局
    127      */
    128     private void setupFootView() {
    129         mFootView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_foot_view, null);
    130         mFootView.setBackgroundColor(Color.BLUE);
    131         footProgressBar = (ProgressBar) mFootView.findViewById(R.id.fresh_foot_progressbar);
    132         footTv = (TextView) mFootView.findViewById(R.id.fresh_foot_tv);
    133         addView(mFootView);
    134     }
    135 
    136     @Override
    137     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    138         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    139         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    140         int height = MeasureSpec.getSize(heightMeasureSpec);
    141         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    142 
    143         int finalHeight = 0;
    144         for (int i = 0; i < getChildCount(); i++) {
    145             View child = getChildAt(i);
    146             measureChild(child, widthMeasureSpec, heightMeasureSpec);
    147             finalHeight += child.getMeasuredHeight();
    148         }
    149 
    150         if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
    151             widthSize = getChildAt(0).getMeasuredWidth();
    152             setMeasuredDimension(widthSize, finalHeight);
    153         } else if (widthMode == MeasureSpec.AT_MOST) {
    154             widthSize = getChildAt(0).getMeasuredWidth();
    155             setMeasuredDimension(widthSize, height);
    156         } else {
    157             setMeasuredDimension(widthSize, finalHeight);
    158         }
    159 
    160     }
    161 
    162 
    163     @Override
    164     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    165         int topOffset = 0;
    166         for (int i = 0; i < getChildCount(); i++) {
    167             View child = getChildAt(i);
    168             child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
    169             topOffset += child.getMeasuredHeight();
    170         }
    171         mInitScrollY = mHeadView.getMeasuredHeight() + getPaddingTop();
    172         scrollTo(0, mInitScrollY);
    173 
    174     }
    175 
    176     @Override
    177     public boolean onInterceptTouchEvent(MotionEvent ev) {
    178         boolean intercepted = false;
    179         int x = (int) ev.getX();
    180         int y = (int) ev.getY();
    181         switch (ev.getAction()) {
    182             case MotionEvent.ACTION_DOWN:
    183                 mLastXIntercepted = x;
    184                 mLastYIntercepted = y;
    185                 break;
    186             case MotionEvent.ACTION_MOVE:
    187                 final int deltaY = x - mLastYIntercepted;
    188                 if (isTop() && deltaY > 0 && Math.abs(deltaY) > mTouchSlop) {
    189                     /*下拉*/
    190                     intercepted = true;
    191                 }
    192                 break;
    193             case MotionEvent.ACTION_UP:
    194                 break;
    195         }
    196         mLastXIntercepted = x;
    197         mLastYIntercepted = y;
    198         return intercepted;
    199     }
    200 
    201     private void doRefresh() {
    202         Log.i(TAG, "doRefresh: ");
    203         if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
    204             mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
    205             currentStatus = STATUS_IDLE;
    206         } else if (currentStatus == STATUS_PULL_TO_REFRESH) {
    207             mScoller.startScroll(0,getScrollY(),0,0-getScrollY(),SCROLL_DURATION);
    208             if (null != mOnRefreshListener) {
    209                 currentStatus = STATUS_LOADING;
    210                 mOnRefreshListener.refresh();
    211             }
    212         }
    213         invalidate();
    214     }
    215 
    216     @Override
    217     public boolean onTouchEvent(MotionEvent event) {
    218         int x = (int) event.getX();
    219         int y = (int) event.getY();
    220         switch (event.getAction()) {
    221             case MotionEvent.ACTION_DOWN:
    222                 if (!mScoller.isFinished()) {
    223                     mScoller.abortAnimation();
    224                 }
    225                 mLastX = x;
    226                 mLastY = y;
    227                 break;
    228             case MotionEvent.ACTION_MOVE:
    229                 if (isFistTouch) {
    230                     isFistTouch = false;
    231                     mLastX = x;
    232                     mLastY = y;
    233                 }
    234                 final int deltaY = y - mLastY;
    235                 if (currentStatus != STATUS_LOADING) {
    236                     changeScrollY(deltaY);
    237                 }
    238                 break;
    239             case MotionEvent.ACTION_UP:
    240                 isFistTouch = true;
    241                 doRefresh();
    242                 break;
    243         }
    244 
    245         mLastX = x;
    246         mLastY = y;
    247         return true;
    248     }
    249 
    250     private void changeScrollY(int deltaY) {
    251         Log.i(TAG, "changeScrollY: ");
    252         int curY = getScrollY();
    253         if (deltaY > 0) {
    254             /*下拉*/
    255             if (curY - deltaY > getPaddingTop()) {
    256                 scrollBy(0, -deltaY);
    257             }
    258         } else {
    259             /*上拉*/
    260             if (curY - deltaY <= mInitScrollY) {
    261                 scrollBy(0, -deltaY);
    262             }
    263         }
    264 
    265         curY = getScrollY();
    266         int slop = mInitScrollY / 2;
    267         if (curY > 0 && curY <=slop) {
    268             currentStatus = STATUS_PULL_TO_REFRESH;
    269         } else if (curY > 0 && curY >= slop) {
    270             currentStatus = STATUS_RELEASE_TO_REFRESH;
    271         }
    272     }
    273 
    274     @Override
    275     public void computeScroll() {
    276         if (mScoller.computeScrollOffset()) {
    277             scrollTo(mScoller.getCurrX(), mScoller.getCurrY());
    278             postInvalidate();
    279         }
    280     }
    281 
    282     /**
    283      * 加载完成调用这个方法
    284      */
    285     public void refreshComplete() {
    286         mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
    287         currentStatus = STATUS_IDLE;
    288         invalidate();
    289     }
    290 
    291     /**
    292      * 显示 Footer
    293      */
    294     public void showFooter() {
    295         if(currentStatus==STATUS_LOAD_MORE) return ;
    296         currentStatus = STATUS_LOAD_MORE ;
    297         mScoller.startScroll(0, getScrollY(), 0, mFootView.getMeasuredHeight()
    298                 , SCROLL_DURATION);
    299         invalidate();
    300 
    301     }
    302 
    303 
    304     /**
    305      * loadMore完成之后调用
    306      */
    307     public void footerComplete() {
    308         mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
    309         invalidate();
    310         currentStatus = STATUS_IDLE;
    311     }
    312 
    313     public interface OnRefreshListener {
    314         void refresh();
    315     }
    316 
    317     abstract boolean isTop();
    318 
    319     abstract boolean isBottom();
    320 
    321 }

    它是一个抽象类,需要编写子类继承isTop()和 isBottom()方法、 
    下面给出它的一个实现类:

     1 package com.blueberry.sample.widget.refresh;
     2 
     3 import android.content.Context;
     4 import android.util.AttributeSet;
     5 import android.widget.AbsListView;
     6 import android.widget.ListView;
     7 
     8 /**
     9  * Created by blueberry on 2016/6/21.
    10  *
    11  * RefreshLayoutBase 的一个实现类
    12  */
    13 public class RefreshListView extends RefreshLayoutBase<ListView> {
    14 
    15     private static final String TAG = "RefreshListView";
    16 
    17     private ListView listView;
    18     private OnLoadListener loadListener;
    19 
    20     public RefreshListView(Context context) {
    21         super(context);
    22     }
    23 
    24     public RefreshListView(Context context, AttributeSet attrs) {
    25         super(context, attrs);
    26     }
    27 
    28     public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
    29         super(context, attrs, defStyleAttr);
    30     }
    31 
    32     public ListView getListView() {
    33         return listView;
    34     }
    35 
    36     public void setListView(final ListView listView) {
    37         this.listView = listView;
    38         setContentView(listView);
    39 
    40         this.listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    41             @Override
    42             public void onScrollStateChanged(AbsListView view, int scrollState) {
    43             }
    44 
    45             @Override
    46             public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    47 
    48                 /*这里存在一个bug: 当listView滑动到底部的时候,如果下拉也会出现footer
    49                 * 这是因为,暂时还没有想到如何判断是下拉还是上拉。
    50                 * 如果要解决此问题,我觉得应该重写listView 的onTouchEvent来判断手势方向
    51                 * 次模块主要解决竖向滑动冲突,故现将此问题放下。
    52                 * */
    53                 if (currentStatus == STATUS_IDLE
    54                         && getScrollY() <= mInitScrollY && isBottom()
    55                         ) {
    56                     showFooter();
    57                     if (null != loadListener) {
    58                         loadListener.onLoadMore();
    59                     }
    60                 }
    61 
    62             }
    63         });
    64     }
    65 
    66     public OnLoadListener getLoadListener() {
    67         return loadListener;
    68     }
    69 
    70     public void setLoadListener(OnLoadListener loadListener) {
    71         this.loadListener = loadListener;
    72     }
    73 
    74     @Override
    75     boolean isTop() {
    76         return listView.getFirstVisiblePosition() == 0
    77                 && getScrollY() <= mHeadView.getMeasuredHeight();
    78     }
    79 
    80     @Override
    81     boolean isBottom() {
    82         return listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1;
    83     }
    84 
    85     public interface OnLoadListener {
    86         void onLoadMore();
    87     }
    88 }

    4、内部拦截法解决同向滑动 
    同样是一个下拉刷新组件,因为实现原理都一样,所以这个写的比较随意些。主要还是如果解决滑动冲突。 
    RefreshLayoutBase2.java

      1 package com.blueberry.sample.widget.refresh;
      2 
      3 import android.content.Context;
      4 import android.graphics.Color;
      5 import android.util.AttributeSet;
      6 import android.util.Log;
      7 import android.view.MotionEvent;
      8 import android.view.View;
      9 import android.view.ViewGroup;
     10 import android.widget.ArrayAdapter;
     11 import android.widget.ListView;
     12 import android.widget.Scroller;
     13 
     14 import com.blueberry.sample.R;
     15 
     16 import java.util.ArrayList;
     17 import java.util.List;
     18 
     19 /**
     20  * Created by blueberry on 2016/6/22.
     21  * 结合内部类 ListVieEx
     22  * 内部拦截法,同向
     23  */
     24 public class RefreshLayoutBase2 extends ViewGroup {
     25 
     26     private static final String TAG = "RefreshLayoutBase2";
     27 
     28     private static List<String> datas;
     29 
     30     static {
     31         datas = new ArrayList<>();
     32         for (int i = 0; i < 40; i++) {
     33             datas.add("数据—" + i);
     34         }
     35     }
     36 
     37     private ViewGroup headView;
     38     private ListViewEx lv;
     39 
     40     private int lastY;
     41     public int mInitScrollY;
     42 
     43     private Scroller mScroller;
     44 
     45     public RefreshLayoutBase2(Context context) {
     46         this(context, null);
     47     }
     48 
     49     public RefreshLayoutBase2(Context context, AttributeSet attrs) {
     50         this(context, attrs, 0);
     51 
     52     }
     53 
     54     public RefreshLayoutBase2(Context context, AttributeSet attrs, int defStyleAttr) {
     55         super(context, attrs, defStyleAttr);
     56         mScroller = new Scroller(context);
     57         setupHeadView(context);
     58         setupContentView(context);
     59 
     60     }
     61 
     62     @Override
     63     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     64         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
     65         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     66         int height = MeasureSpec.getSize(heightMeasureSpec);
     67         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     68 
     69         int finalHeight = 0;
     70         for (int i = 0; i < getChildCount(); i++) {
     71             View child = getChildAt(i);
     72             measureChild(child, widthMeasureSpec, heightMeasureSpec);
     73             finalHeight += child.getMeasuredHeight();
     74         }
     75 
     76         if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
     77             widthSize = getChildAt(0).getMeasuredWidth();
     78             setMeasuredDimension(widthSize, finalHeight);
     79         } else if (widthMode == MeasureSpec.AT_MOST) {
     80             widthSize = getChildAt(0).getMeasuredWidth();
     81             setMeasuredDimension(widthSize, height);
     82         } else {
     83             setMeasuredDimension(widthSize, finalHeight);
     84         }
     85 
     86     }
     87 
     88     @Override
     89     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     90         int topOffset = 0;
     91         for (int i = 0; i < getChildCount(); i++) {
     92             View child = getChildAt(i);
     93             child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
     94             topOffset += child.getMeasuredHeight();
     95         }
     96         mInitScrollY = headView.getMeasuredHeight() + getPaddingTop();
     97         scrollTo(0, mInitScrollY);
     98 
     99     }
    100 
    101     /**
    102      * 不拦截Down 其他一律拦截
    103      * @param ev
    104      * @return
    105      */
    106     @Override
    107     public boolean onInterceptTouchEvent(MotionEvent ev) {
    108         if (ev.getAction() == MotionEvent.ACTION_DOWN) return false;
    109         return true;
    110     }
    111 
    112     @Override
    113     public boolean onTouchEvent(MotionEvent event) {
    114         int y = (int) event.getY();
    115         switch (event.getAction()) {
    116             case MotionEvent.ACTION_DOWN:
    117                 break;
    118             case MotionEvent.ACTION_MOVE:
    119                 final int deltaY = y-lastY;
    120                 Log.i(TAG, "onTouchEvent: deltaY: "+deltaY);
    121                 if (deltaY >= 0 && lv.isTop() && getScrollY() - deltaY >=getPaddingTop()) {
    122                         scrollBy(0, -deltaY);
    123                 }
    124                 break;
    125             case MotionEvent.ACTION_UP:
    126                 this.postDelayed(new Runnable() {
    127                     @Override
    128                     public void run() {
    129                         mScroller.startScroll(0,getScrollY(),0,mInitScrollY-getScrollY());
    130                         invalidate();
    131                     }
    132                 },2000);
    133                 break;
    134         }
    135 
    136         lastY = y ;
    137         return true;
    138     }
    139 
    140     private void setupHeadView(Context context) {
    141         headView = (ViewGroup) View.inflate(context, R.layout.fresh_head_view, null);
    142         headView.setBackgroundColor(Color.RED);
    143         ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);
    144         addView(headView, params);
    145     }
    146 
    147     public void setupContentView(Context context) {
    148         lv = new ListViewEx(context, this);
    149         lv.setBackgroundColor(Color.BLUE);
    150         ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, datas);
    151         lv.setAdapter(adapter);
    152         addView(lv, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    153     }
    154 
    155     @Override
    156     public void computeScroll() {
    157         if(mScroller.computeScrollOffset()){
    158             scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
    159             postInvalidate();
    160         }
    161     }
    162 
    163     public static class ListViewEx extends ListView {
    164 
    165         private RefreshLayoutBase2 outter;
    166 
    167         public ListViewEx(Context context, RefreshLayoutBase2 outter) {
    168             super(context);
    169             this.outter = outter;
    170         }
    171 
    172         public ListViewEx(Context context, AttributeSet attrs) {
    173             super(context, attrs);
    174         }
    175 
    176         public ListViewEx(Context context, AttributeSet attrs, int defStyleAttr) {
    177             super(context, attrs, defStyleAttr);
    178         }
    179 
    180         /**
    181          * 使用 outter.requestDisallowInterceptTouchEvent();
    182          * 来决定父控件是否对事件进行拦截
    183          * @param ev
    184          * @return
    185          */
    186         @Override
    187         public boolean dispatchTouchEvent(MotionEvent ev) {
    188             switch (ev.getAction()) {
    189                 case MotionEvent.ACTION_DOWN:
    190                     outter.requestDisallowInterceptTouchEvent(true);
    191                     break;
    192                 case MotionEvent.ACTION_MOVE:
    193 
    194                     if ( isTop() && outter.getScrollY() <= outter.mInitScrollY) {
    195                         outter.requestDisallowInterceptTouchEvent(false);
    196                     }
    197                     break;
    198 
    199             }
    200             return super.dispatchTouchEvent(ev);
    201         }
    202 
    203         public boolean isTop() {
    204             return getFirstVisiblePosition() ==0;
    205         }
    206     }
    207 }


  • 相关阅读:
    程序猿之歌
    How to solve the problem : &quot;You have been logged on with a temporary profile&quot;
    LeetCode Jump Game
    hdu 3496 Watch The Movie
    matlab矩阵内存预分配
    【Windows socket+IP+UDP+TCP】网络基础
    <html>
    行为类模式(十):模板方法(Template Method)
    行为类模式(九):策略(Strategy)
    行为类模式(八):状态(State)
  • 原文地址:https://www.cnblogs.com/zl1991/p/6341030.html
Copyright © 2011-2022 走看看