zoukankan      html  css  js  c++  java
  • 自定义控件(视图)2期笔记13:View的滑动冲突之 内部拦截法

    1. 内部拦截法:

    父容器不拦截事件,所有的事件全部都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理。

    这种方法和Android中的事件分发机制不一样,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法稍显负责一点。

    我们需要重写子元素的dispatchTouchEvent方法

    这种方法的伪代码是:

     1  @Override
     2     public boolean dispatchTouchEvent(MotionEvent event) {
     3         int x = (int) event.getX();
     4         int y = (int) event.getY();
     5 
     6         switch (event.getAction()) {
     7         case MotionEvent.ACTION_DOWN: {
     8             parent.requestDisallowInterceptTouchEvent(true);
     9             break;
    10         }
    11         case MotionEvent.ACTION_MOVE: {
    12             int deltaX = x - mLastX;
    13             int deltaY = y - mLastY;
    14             if (父容器需要此类点击事件) {
    15                 parent.requestDisallowInterceptTouchEvent(false);
    16             }
    17             break;
    18         }
    19         case MotionEvent.ACTION_UP: {
    20             break;
    21         }
    22         default:
    23             break;
    24         }
    25 
    26         mLastX = x;
    27         mLastY = y;
    28         return super.dispatchTouchEvent(event);
    29     }

    上面重写的子元素的dispatchTouchEvent方法,这里同时需要重写父容器的onInterceptTouchEvent方法,为什么呢?

    那是因为ACTION_DOWN事件并不受FLAG_DISALLOW_INTERCEPT这个标记位的控制,所以一旦父容器拦截ACTION_DOWN事件,那么所有的事件都无法传递到子元素之中,这样内部拦截法就无法起作用了。

    父容器所做的修改如下:

     1 @Override
     2     public boolean onInterceptTouchEvent(MotionEvent event) {
     3 
     4         int action = event.getAction();
     5         if (action == MotionEvent.ACTION_DOWN) {
     6             return false;
     7         } else {
     8             return true;
     9         }
    10     }

    2. 下面通过一个Demo示例说明:

    (1)首先我们创建一个Android工程,如下:

    (2)首先我们来到主布局activity_main.xml,如下:

     1 <com.himi.viewconflict1.ui.RevealLayout
     2     xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:tools="http://schemas.android.com/tools"
     4     android:layout_width="match_parent"
     5     android:layout_height="match_parent"
     6     android:orientation="vertical"
     7     android:padding="12dp"
     8     tools:context="${relativePackage}.${activityClass}" >
     9 
    10     <Button
    11         android:id="@+id/button1"
    12         style="@style/AppTheme.Button.Green"
    13         android:onClick="onButtonClick"
    14         android:text="滑动冲突场景1-内部拦截" />
    15 
    16 </com.himi.viewconflict1.ui.RevealLayout>

    (3)接下来来到MainActivity,如下:

     1 package com.himi.viewconflict;
     2 
     3 import android.app.Activity;
     4 import android.content.Intent;
     5 import android.os.Bundle;
     6 import android.view.View;
     7 
     8 public class MainActivity extends Activity {
     9 
    10     @Override
    11     protected void onCreate(Bundle savedInstanceState) {
    12         super.onCreate(savedInstanceState);
    13         setContentView(R.layout.activity_main);
    14     }
    15 
    16     
    17     
    18     public void onButtonClick(View view) {
    19          Intent intent = new Intent(this, DemoActivity_1.class);
    20          startActivity(intent);
    21     }
    22 }

    (4)上面很自然地跳转到DemoActivity_2之中,如下:

     1 package com.himi.viewconflict1;
     2 
     3 import java.util.ArrayList;
     4 
     5 import com.himi.viewconflict1.ui.HorizontalScrollViewEx2;
     6 import com.himi.viewconflict1.ui.ListViewEx;
     7 import com.himi.viewconflict1.utils.MyUtils;
     8 
     9 import android.app.Activity;
    10 import android.graphics.Color;
    11 import android.os.Bundle;
    12 import android.util.Log;
    13 import android.view.LayoutInflater;
    14 import android.view.MotionEvent;
    15 import android.view.View;
    16 import android.view.ViewGroup;
    17 import android.widget.AdapterView;
    18 import android.widget.AdapterView.OnItemClickListener;
    19 import android.widget.ArrayAdapter;
    20 import android.widget.TextView;
    21 import android.widget.Toast;
    22 
    23 public class DemoActivity_2 extends Activity {
    24     private static final String TAG = "DemoActivity_2";
    25 
    26     private HorizontalScrollViewEx2 mListContainer;
    27 
    28     @Override
    29     protected void onCreate(Bundle savedInstanceState) {
    30         super.onCreate(savedInstanceState);
    31         setContentView(R.layout.demo_2);
    32         Log.d(TAG, "onCreate");
    33         initView();
    34     }
    35 
    36     private void initView() {
    37         LayoutInflater inflater = getLayoutInflater();
    38         mListContainer = (HorizontalScrollViewEx2) findViewById(R.id.container);
    39         final int screenWidth = MyUtils.getScreenMetrics(this).widthPixels;
    40         final int screenHeight = MyUtils.getScreenMetrics(this).heightPixels;
    41         for (int i = 0; i < 3; i++) {
    42             ViewGroup layout = (ViewGroup) inflater.inflate(
    43                     R.layout.content_layout2, mListContainer, false);
    44             layout.getLayoutParams().width = screenWidth;
    45             TextView textView = (TextView) layout.findViewById(R.id.title);
    46             textView.setText("page " + (i + 1));
    47             layout.setBackgroundColor(Color
    48                     .rgb(255 / (i + 1), 255 / (i + 1), 0));
    49             createList(layout);
    50             mListContainer.addView(layout);
    51         }
    52     }
    53 
    54     private void createList(ViewGroup layout) {
    55         ListViewEx listView = (ListViewEx) layout.findViewById(R.id.list);
    56         ArrayList<String> datas = new ArrayList<String>();
    57         for (int i = 0; i < 50; i++) {
    58             datas.add("name " + i);
    59         }
    60         ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
    61                 R.layout.content_list_item, R.id.name, datas);
    62         listView.setAdapter(adapter);
    63         listView.setHorizontalScrollViewEx2(mListContainer);
    64         listView.setOnItemClickListener(new OnItemClickListener() {
    65             @Override
    66             public void onItemClick(AdapterView<?> parent, View view,
    67                     int position, long id) {
    68                 Toast.makeText(DemoActivity_2.this, "click item "+position,
    69                         Toast.LENGTH_SHORT).show();
    70 
    71             }
    72         });
    73     }
    74 
    75     @Override
    76     public boolean dispatchTouchEvent(MotionEvent ev) {
    77         Log.d(TAG, "dispatchTouchEvent action:" + ev.getAction());
    78         return super.dispatchTouchEvent(ev);
    79     }
    80     
    81     @Override
    82     public boolean onTouchEvent(MotionEvent event) {
    83         Log.d(TAG, "onTouchEvent action:" + event.getAction());
    84         return super.onTouchEvent(event);
    85     }
    86 }

    上面的DemoActivity_2主布局demo_2.xml,如下:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:background="#ffffff"
     6     android:orientation="vertical" >
     7 
     8     <com.himi.viewconflict1.ui.HorizontalScrollViewEx2
     9         android:id="@+id/container"
    10         android:layout_width="wrap_content"
    11         android:layout_height="match_parent" />
    12 
    13 
    14 </LinearLayout>

    上面使用到HorizontalScrollViewEx2是自定义控件(继承自ViewGroup),如下:

    HorizontalScrollViewEx2是父容器,这里需要重写它的onInterceptTouchEvent方法,让父容器不拦截ACTION_DOWN事件

      1 package com.himi.viewconflict1.ui;
      2 
      3 import android.content.Context;
      4 import android.util.AttributeSet;
      5 import android.util.Log;
      6 import android.view.MotionEvent;
      7 import android.view.VelocityTracker;
      8 import android.view.View;
      9 import android.view.ViewGroup;
     10 import android.widget.Scroller;
     11 
     12 public class HorizontalScrollViewEx2 extends ViewGroup {
     13     private static final String TAG = "HorizontalScrollViewEx2";
     14 
     15     private int mChildrenSize;
     16     private int mChildWidth;
     17     private int mChildIndex;
     18     // 分别记录上次滑动的坐标
     19     private int mLastX = 0;
     20     private int mLastY = 0;
     21 
     22     // 分别记录上次滑动的坐标(onInterceptTouchEvent)
     23     private int mLastXIntercept = 0;
     24     private int mLastYIntercept = 0;
     25 
     26     private Scroller mScroller;
     27     private VelocityTracker mVelocityTracker;
     28 
     29     public HorizontalScrollViewEx2(Context context) {
     30         super(context);
     31         init();
     32     }
     33 
     34     public HorizontalScrollViewEx2(Context context, AttributeSet attrs) {
     35         super(context, attrs);
     36         init();
     37     }
     38 
     39     public HorizontalScrollViewEx2(Context context, AttributeSet attrs,
     40             int defStyle) {
     41         super(context, attrs, defStyle);
     42         init();
     43     }
     44 
     45     private void init() {
     46         mScroller = new Scroller(getContext());
     47         mVelocityTracker = VelocityTracker.obtain();
     48     }
     49 
     50     @Override
     51     public boolean onInterceptTouchEvent(MotionEvent event) {
     52         int x = (int) event.getX();
     53         int y = (int) event.getY();
     54         int action = event.getAction();
     55         if (action == MotionEvent.ACTION_DOWN) {
     56             mLastX = x;
     57             mLastY = y;
     58             if (!mScroller.isFinished()) {
     59                 mScroller.abortAnimation();
     60                 return true;
     61             }
     62             return false;
     63         } else {
     64             return true;
     65         }
     66     }
     67 
     68     @Override
     69     public boolean onTouchEvent(MotionEvent event) {
     70         Log.d(TAG, "onTouchEvent action:" + event.getAction());
     71         mVelocityTracker.addMovement(event);
     72         int x = (int) event.getX();
     73         int y = (int) event.getY();
     74         switch (event.getAction()) {
     75         case MotionEvent.ACTION_DOWN: {
     76             if (!mScroller.isFinished()) {
     77                 mScroller.abortAnimation();
     78             }
     79             break;
     80         }
     81         case MotionEvent.ACTION_MOVE: {
     82             int deltaX = x - mLastX;
     83             int deltaY = y - mLastY;
     84             Log.d(TAG, "move, deltaX:" + deltaX + " deltaY:" + deltaY);
     85             scrollBy(-deltaX, 0);
     86             break;
     87         }
     88         case MotionEvent.ACTION_UP: {
     89             int scrollX = getScrollX();
     90             int scrollToChildIndex = scrollX / mChildWidth;
     91             Log.d(TAG, "current index:" + scrollToChildIndex);
     92             mVelocityTracker.computeCurrentVelocity(1000);
     93             float xVelocity = mVelocityTracker.getXVelocity();
     94             if (Math.abs(xVelocity) >= 50) {
     95                 mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
     96             } else {
     97                 mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
     98             }
     99             mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
    100             int dx = mChildIndex * mChildWidth - scrollX;
    101             smoothScrollBy(dx, 0);
    102             mVelocityTracker.clear();
    103             Log.d(TAG, "index:" + scrollToChildIndex + " dx:" + dx);
    104             break;
    105         }
    106         default:
    107             break;
    108         }
    109 
    110         mLastX = x;
    111         mLastY = y;
    112         return true;
    113     }
    114 
    115     @Override
    116     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    117         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    118         int measuredWidth = 0;
    119         int measuredHeight = 0;
    120         final int childCount = getChildCount();
    121         measureChildren(widthMeasureSpec, heightMeasureSpec);
    122 
    123         int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
    124         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    125         int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
    126         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    127         if (childCount == 0) {
    128             setMeasuredDimension(0, 0);
    129         } else if (heightSpecMode == MeasureSpec.AT_MOST) {
    130             final View childView = getChildAt(0);
    131             measuredHeight = childView.getMeasuredHeight();
    132             setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());
    133         } else if (widthSpecMode == MeasureSpec.AT_MOST) {
    134             final View childView = getChildAt(0);
    135             measuredWidth = childView.getMeasuredWidth() * childCount;
    136             setMeasuredDimension(measuredWidth, heightSpaceSize);
    137         } else {
    138             final View childView = getChildAt(0);
    139             measuredWidth = childView.getMeasuredWidth() * childCount;
    140             measuredHeight = childView.getMeasuredHeight();
    141             setMeasuredDimension(measuredWidth, measuredHeight);
    142         }
    143     }
    144 
    145     @Override
    146     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    147         Log.d(TAG, "" + getWidth());
    148         int childLeft = 0;
    149         final int childCount = getChildCount();
    150         mChildrenSize = childCount;
    151 
    152         for (int i = 0; i < childCount; i++) {
    153             final View childView = getChildAt(i);
    154             if (childView.getVisibility() != View.GONE) {
    155                 final int childWidth = childView.getMeasuredWidth();
    156                 mChildWidth = childWidth;
    157                 childView.layout(childLeft, 0, childLeft + childWidth,
    158                         childView.getMeasuredHeight());
    159                 childLeft += childWidth;
    160             }
    161         }
    162     }
    163 
    164     private void smoothScrollBy(int dx, int dy) {
    165         mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
    166         invalidate();
    167     }
    168 
    169     @Override
    170     public void computeScroll() {
    171         if (mScroller.computeScrollOffset()) {
    172             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    173             postInvalidate();
    174         }
    175     }
    176 
    177     @Override
    178     protected void onDetachedFromWindow() {
    179         mVelocityTracker.recycle();
    180         super.onDetachedFromWindow();
    181     }
    182 }

    (5)来到主布局之中,在HorizontalScrollViewEx2之中包含一个子布局content_layout2.xml,如下:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6 
     7     <TextView
     8         android:id="@+id/title"
     9         android:layout_width="wrap_content"
    10         android:layout_height="wrap_content"
    11         android:layout_marginTop="5dp"
    12         android:layout_marginBottom="5dp"
    13         android:text="TextView" />
    14 
    15     <com.himi.viewconflict1.ui.ListViewEx
    16         android:id="@+id/list"
    17         android:layout_width="match_parent"
    18         android:layout_height="match_parent"
    19         android:background="#fff4f7f9"
    20         android:cacheColorHint="#00000000"
    21         android:divider="#dddbdb"
    22         android:dividerHeight="1.0px"
    23         android:listSelector="@android:color/transparent" />
    24 
    25 </LinearLayout>

    上面的ListViewEx是自定义的控件(继承自ListView),在ListViewEx里面实现了内部拦截法的逻辑,如下:

     1 package com.himi.viewconflict1.ui;
     2 
     3 import android.content.Context;
     4 import android.util.AttributeSet;
     5 import android.util.Log;
     6 import android.view.MotionEvent;
     7 import android.widget.ListView;
     8 
     9 public class ListViewEx extends ListView {
    10     private static final String TAG = "ListViewEx";
    11 
    12     private HorizontalScrollViewEx2 mHorizontalScrollViewEx2;
    13 
    14     // 分别记录上次滑动的坐标
    15     private int mLastX = 0;
    16     private int mLastY = 0;
    17 
    18     public ListViewEx(Context context) {
    19         super(context);
    20     }
    21 
    22     public ListViewEx(Context context, AttributeSet attrs) {
    23         super(context, attrs);
    24     }
    25 
    26     public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
    27         super(context, attrs, defStyle);
    28     }
    29 
    30     public void setHorizontalScrollViewEx2(
    31             HorizontalScrollViewEx2 horizontalScrollViewEx2) {
    32         mHorizontalScrollViewEx2 = horizontalScrollViewEx2;
    33     }
    34 
    35     @Override
    36     public boolean dispatchTouchEvent(MotionEvent event) {
    37         int x = (int) event.getX();
    38         int y = (int) event.getY();
    39 
    40         switch (event.getAction()) {
    41         case MotionEvent.ACTION_DOWN: {
    42             mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);
    43             break;
    44         }
    45         case MotionEvent.ACTION_MOVE: {
    46             int deltaX = x - mLastX;
    47             int deltaY = y - mLastY;
    48             Log.d(TAG, "dx:" + deltaX + " dy:" + deltaY);
    49             if (Math.abs(deltaX) > Math.abs(deltaY)) {
    50                 mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);
    51             }
    52             break;
    53         }
    54         case MotionEvent.ACTION_UP: {
    55             break;
    56         }
    57         default:
    58             break;
    59         }
    60 
    61         mLastX = x;
    62         mLastY = y;
    63         return super.dispatchTouchEvent(event);
    64     }
    65 
    66 }

     void  requestDisallowInterceptTouchEvent(boolean  disallowIntercept):

    这个方法的入参一个boolean 变量,用来表示是否需要调用onInterceptTouchEvent来判断是否拦截.

    该标记如果为True,就如它的字面意思一样---不允许调用onInterceptTouchEvent(),结果就是,所有的父类方法都不会进行拦截,而把事件传递给子View. 该方法属于ViewGroup ,并且是个递归方法,也就是说一旦调用后,所有父类的disallowIntercept都会设置成True。即当前View的所有父类View,都不会调用自身的onInterceptTouchEvent()进行拦截。

    该标记如果为False,就如它的字面意思一样---允许调用onInterceptTouchEvent(),结果就是,父类可以拦截事件。

    接下来,来到Listview的Item布局content_list_item.xml,如下:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="50dp"
     5     android:gravity="center_vertical"
     6     android:orientation="vertical" >
     7 
     8     <TextView
     9         android:id="@+id/name"
    10         android:layout_width="wrap_content"
    11         android:layout_height="wrap_content"
    12         android:text="TextView" />
    13 
    14 </LinearLayout>

    (6)最终项目如下:

    (7)部署程序到手机上,如下:

    3. 示例源码下载

  • 相关阅读:
    [LeetCode] Remove Duplicates from Sorted List
    [LeetCode] Substring with Concatenation of All Words
    [LeetCode] Partition List
    [LeetCode] Reverse Nodes in kGroup
    [LeetCode] Remove Duplicates from Sorted List II
    [LeetCode] Reverse Linked List II
    [LeetCode] Text Justification
    [LeetCode] Swap Nodes in Pairs
    (转)介绍下Nuget在传统Asp.net项目中的使用
    Entity Framework中的Migrations
  • 原文地址:https://www.cnblogs.com/hebao0514/p/5700730.html
Copyright © 2011-2022 走看看