国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android解決View的滑動沖突的方法

2019-10-21 21:46:53
字體:
來源:轉載
供稿:網友

關于 Android 的 TouchEvent 事件分發機制可以看這里:Java_Android_Learn ,本文講解的是如何去解決 View 之間的滑動沖突

當父容器與子 View 都可以滑動時,就會產生滑動沖突。例如 ViewPager 中包含了 ListView 時,ViewPager 可以橫向滑動,而 ListView 可以豎向滑動,此時就會產生滑動沖突。而我們之所以在使用的過程中沒有發現這個問題,是因為 ViewPager 內部已經處理好滑動沖突了

解決 View 之間的滑動沖突的方法分為兩種,分別是外部攔截法和內部攔截法

一、外部攔截法

父容器根據需要在 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;  }
  • 根據實際的業務需求,判斷是否需要處理 ACTION_MOVE 事件,如果父 View 需要處理則返回 true,否則返回 false 并交由子 View 去處理
  • ACTION_DOWN 事件需要返回 false,父容器不能進行攔截,否則根據 View 的事件分發機制,后續的 ACTION_MOVE 與 ACTION_UP 事件都將默認交由父容器進行處理
  • 原則上 ACTION_UP 事件也需要返回 false,如果返回 true,那么子 View 將接收不到 ACTION_UP 事件,子 View 的onClick 事件也無法觸發

二、內部攔截法

內部攔截法則是要求父容器不攔截任何事件,所有事件都傳遞給子 View,子 View 根據需求判斷是自己消費事件還是傳回給父容器進行處理,思路可以看以下偽代碼:

子 View 修改其 dispatchTouchEvent 方法

public boolean dispatchTouchEvent(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);  }

父容器修改其 onInterceptTouchEvent 方法

public boolean onInterceptTouchEvent(MotionEvent event) {    int action = event.getAction();    if (action == MotionEvent.ACTION_DOWN) {      return false;    } else {      return true;    }  }
  • 內部攔截法要求父容器不能攔截 ACTION_DOWN 事件,否則一旦父容器攔截 ACTION_DOWN 事件,那么后續的觸摸事件都不會傳遞給子View
  • 滑動策略的邏輯放在子 View 的 dispatchTouchEvent 方法的 ACTION_MOVE 事件中,如果父容器需要處理事件則調用 parent.requestDisallowInterceptTouchEvent(false) 方法讓父容器去攔截事件

 三、滑動沖突實例

這里以 ViewPager 作為父容器,看看 ViewPager 與其內部 View 之間的滑動沖突情況

為了使 ViewPager 不處理滑動沖突,這里來重寫其 onInterceptTouchEvent() 方法

/** * 作者:葉應是葉 * 時間:2018/7/15 10:26 * 描述: */public class MyViewPager extends ViewPager {  public MyViewPager(@NonNull Context context) {    super(context);  }  public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {    super(context, attrs);  }  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {    return false;  }}

這里用一個布爾變量來控制 ViewPager 中每一個頁面包含的是 ListView 還是 TextView

public class MainActivity extends AppCompatActivity {  private List<View> viewList;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    ViewPager viewPager = findViewById(R.id.viewPager);    viewList = new ArrayList<>();    initData(false);    viewPager.setAdapter(new MyPagerAdapter(viewList));  }  private void initData(boolean flag) {    for (int j = 0; j < 4; j++) {      View view;      if (flag) {        ListView listView = new ListView(this);        List<String> dataList = new ArrayList<>();        for (int i = 0; i < 30; i++) {          dataList.add("leavesC " + i);        }        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList);        listView.setAdapter(adapter);        view = listView;      } else {        TextView textView = new TextView(this);        textView.setGravity(Gravity.CENTER);        textView.setText("leavesC " + j);        view = textView;      }      viewList.add(view);    }  }}

當子 View 為 TextView 時

Android,View,滑動沖突

然而此時還是沒有發生滑動沖突,ViewPager 還是可以正常使用。這是因為 TextView 默認是不可點擊的,因此 TextView 并不會消費觸摸事件,觸摸事件最后還是傳回給 ViewPager 進行處理,因為此時還是可以正常使用

如果為 TextView 設置 textView.setClickable(true); ,就會使得 ViewPager 無法滑動

Android,View,滑動沖突

當子 View 為 ListView 時,則只能上下滑動,而無法左右滑動

Android,View,滑動沖突

四、通過外部攔截法解決滑動沖突

外部攔截法僅需要修改父容器的 onInterceptTouchEvent() 方法即可,通過滑動時橫向滑動距離與豎向滑動距離之間的大小,判斷是否在進行左右滑動,如果判斷出當前是滑動操作,則使 ViewPager 消費該事件

/** * 作者:葉應是葉 * 時間:2018/7/15 10:26 * 描述: */public class MyViewPager extends ViewPager {  public MyViewPager(@NonNull Context context) {    super(context);  }  public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {    super(context, attrs);  }  private int lastXIntercept;  private int lastYIntercept;  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {    boolean intercepted = false;    int x = (int) ev.getX();    int y = (int) ev.getY();    final int action = ev.getAction() & MotionEvent.ACTION_MASK;    switch (action) {      case MotionEvent.ACTION_DOWN:        //不攔截此事件        intercepted = false;        //調用 ViewPager的 onInterceptTouchEvent 方法用于初始化 mActivePointerId        super.onInterceptTouchEvent(ev);        break;      case MotionEvent.ACTION_MOVE:        //橫向位移增量        int deltaX = x - lastXIntercept;        //豎向位移增量        int deltaY = y - lastYIntercept;        //如果橫向滑動距離大于豎向滑動距離,則認為使用者是想要左右滑動        //此時就使 ViewPager 攔截此事件        intercepted = Math.abs(deltaX) > Math.abs(deltaY);        break;      case MotionEvent.ACTION_UP:        //不攔截此事件        intercepted = false;        break;      default:        break;    }    lastXIntercept = x;    lastYIntercept = y;    return intercepted;  }}

五、通過內部攔截法解決滑動沖突

內部攔截法需要重寫 ListView 的 dispatchTouchEvent 方法

/** * 作者:葉應是葉 * 時間:2018/7/15 12:40 * 描述: */public class MyListView extends ListView {  public MyListView(Context context) {    super(context);  }  public MyListView(Context context, AttributeSet attrs) {    super(context, attrs);  }  private int lastX;  private int lastY;  @Override  public boolean dispatchTouchEvent(MotionEvent ev) {    int x = (int) ev.getX();    int y = (int) ev.getY();    final int action = ev.getAction() & MotionEvent.ACTION_MASK;    switch (action) {      case MotionEvent.ACTION_DOWN:        getParent().requestDisallowInterceptTouchEvent(true);        break;      case MotionEvent.ACTION_MOVE:        //橫向位移增量        int deltaX = x - lastX;        //豎向位移增量        int deltaY = y - lastY;        //如果橫向滑動距離大于豎向滑動距離,則認為使用者是想要左右滑動        //此時就通知父容器 ViewPager 處理此事件        if (Math.abs(deltaX) > Math.abs(deltaY)) {          getParent().requestDisallowInterceptTouchEvent(false);        }        break;      case MotionEvent.ACTION_UP:        break;      default:        break;    }    lastX = x;    lastY = y;    return super.dispatchTouchEvent(ev);  }}

同時也需要修改 MyViewPager 的 onInterceptTouchEvent 方法

/** * 作者:葉應是葉 * 時間:2018/7/15 10:26 * 描述: */public class MyViewPager extends ViewPager {  public MyViewPager(@NonNull Context context) {    super(context);  }  public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {    super(context, attrs);  }  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {    final int action = ev.getAction() & MotionEvent.ACTION_MASK;    if (action == MotionEvent.ACTION_DOWN) {      super.onInterceptTouchEvent(ev);      return false;    }    return true;  }}

更多的學習筆記看這里: Java_Android_Learn

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 十堰市| 昂仁县| 南平市| 元氏县| 光泽县| 隆昌县| 绥化市| 上饶县| 雷州市| 缙云县| 永丰县| 益阳市| 定兴县| 诸暨市| 澄江县| 小金县| 天气| 长海县| 普陀区| 梨树县| 柳州市| 通山县| 木兰县| 西昌市| 日喀则市| 重庆市| 石首市| 黎平县| 林西县| 上栗县| 平乐县| 孟州市| 吐鲁番市| 丰宁| 砚山县| 衡东县| 五常市| 抚松县| 大埔县| 饶阳县| 岳普湖县|