最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

android中view手勢滑動(dòng)沖突的解決方法

 更新時(shí)間:2016年11月10日 16:17:08   作者:betterAndroider  
本篇文章主要介紹了android中view手勢滑動(dòng)沖突的解決方法,主要解決方法有兩種,外部和內(nèi)部攔截。有需要的可以參考下。

Android手勢事件的沖突跟點(diǎn)擊事件的分發(fā)過程息息相關(guān),由三個(gè)重要的方法來共同完成,分別是:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。

public boolean dispatchTouchEvent(MotionEvent ev)

這個(gè)方法用來進(jìn)行事件的分發(fā)。如果事件傳遞到view,那么這個(gè)方法一定會(huì)被調(diào)用,返回結(jié)果受當(dāng)前View的onTouchEvent和下級View的dispatchTouchEvent方法的影響,表示是否消耗當(dāng)前事件。

   

 public boolean onInterceptTouchEvent(MotionEvent event)

在上述方法內(nèi)部調(diào)用,用來判斷是攔截某個(gè)事件,如果當(dāng)前View攔截了某個(gè)事件,那么在同一個(gè)事件序列當(dāng)中,此方法不會(huì)被再次調(diào)用,返回結(jié)果表示是否攔截當(dāng)前事件。

 public boolean onTouchEvent(MotionEvent event)

 在dispathcTouchEvent方法中調(diào)用,用來處理點(diǎn)擊事件,返回結(jié)果表示是否消耗當(dāng)前事件,如果不消耗,則在同一個(gè)事件序列中,當(dāng)前View無法再次接到事件。

例:

  public boolean dispatchTouchEvent(MotionEvent ev){

       boolean consume = false;

      if(onInterceptTouchEvent(ev)){

          consume = onTouchEvent(ev);

       }  else {

         consum = child.dispathcTouchEvent(ev);

      }

    return consume;

   }

手勢沖突的解決方法就是用上面的三個(gè)方法;主要分為兩種解決方法:·1外部攔截法 2內(nèi)部攔截法

1.常見的滑動(dòng)沖突場景

1.1 外部滑動(dòng)方向和內(nèi)部滑動(dòng)的方向不一致

這種情況我們經(jīng)常遇見,比如使用viewpaper+listview時(shí),在這種效果中,可以通過左右滑動(dòng)切換頁面,而每一個(gè)頁面往往又是一個(gè)listview,本來在這種情況下是有沖突的,但是Viewpaper內(nèi)部處理了這個(gè)滑動(dòng)沖突,因此采用viewpaper我們無需關(guān)注這個(gè)問題,如果我們采用的不是Viewpaper而是ScrollView等,那么必須手動(dòng)處理滑動(dòng)沖突,否則內(nèi)外兩層只能有一層滑動(dòng),那就是滑動(dòng)沖突。另外內(nèi)部左右滑動(dòng),外部上下滑動(dòng)也同樣屬于該類。

1.2 外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向一致

這種情況就比較復(fù)雜,當(dāng)內(nèi)外兩層都在同一個(gè)方向可以滑動(dòng)的時(shí)候,顯然存在邏輯問題,因?yàn)楫?dāng)手指開始滑動(dòng)的時(shí)候,系統(tǒng)無法知道用戶到底是想讓那一層動(dòng),所以當(dāng)手指滑動(dòng)的時(shí)候就會(huì)出現(xiàn)問題,要么只能一層動(dòng),要么內(nèi)外兩成動(dòng)的都很卡頓。

2.給出解決方案

2.1 外部攔截法

針對場景1,我們可以發(fā)現(xiàn)外部和內(nèi)部的滑動(dòng)方向不一樣也就是說只要判斷當(dāng)前dy和dx的大小,如果dy>dx,那么當(dāng)前就是豎直滑動(dòng),否則就是水平滑動(dòng)。明確了這個(gè)我就就可以根據(jù)當(dāng)前的手勢開始攔截了。


從上一節(jié)中我們分析了view的事件分發(fā),我們知道點(diǎn)擊事件的分發(fā)順序是 通過父布局分發(fā),如果父布局沒有攔截,即onInterceptTouchEvent返回false,才會(huì)傳遞給子View。所以我們就可以利用onInterceptTouchEvent()這個(gè)方法來進(jìn)行事件的攔截。來看一下代碼:

 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(父容器攔截的規(guī)則){
        intercepted=true;
      }else{
        intercepted=false;
      }
      break;
    }
    case MotionEvent.ACTION_UP: {
      intercepted = false;
      break;
    }
    default:
      break;
    }
    mLastXIntercept=x;
    mLastYIntercept=y;
    return intercepted;
  }

上面的代碼差多就是外部攔截的通用模板了,在onInterceptTouchEvent方法中,

首先是ACTION_DOWN這個(gè)事件,父容器必須返回false,即不攔截事件,因?yàn)橐坏└溉萜鲾r截了ACTION_DOWN這個(gè)事件,那么后續(xù)的ACTION_MOVE和ACTION_UP事件將直接交給父容器處理,這個(gè)時(shí)候事件沒法繼續(xù)傳遞給子元素了;

然后是ACTION_MOVE這個(gè)事件,這個(gè)事件可以根據(jù)需要決定是否攔截,如果父容器需要攔截就返回true,否則返回false;

最后是ACTION_UP這個(gè)事件,這里必須返回false,因?yàn)檫@個(gè)事件本身也沒有太多意義。

下面我們來具體做一下攔截的操作,我們需要在水平滑動(dòng)的時(shí)候父容器攔截事件。

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: {
      int deltaX=x-mLastXIntercept;
      int deltaY=y=mLastYIntercept;
      if(Math.abs(deltaX)>Math.abs(deltaY)){
        intercepted=true;
      }else{
        intercepted=false;
      }
      break;
    }
    case MotionEvent.ACTION_UP: {
      intercepted = false;
      break;
    }
    default:
      break;
    }
    mLastXIntercept=x;
    mLastYIntercept=y;
    return intercepted;
  }

 從上面的代碼來看,我們只是修改了一下攔截條件而已,所以說外部攔截還是很簡單方便的。在滑動(dòng)的過程中,當(dāng)水平方向的距離大時(shí)就判定水平滑動(dòng)。

還是一貫我們做實(shí)驗(yàn)來證明理論的風(fēng)格,我們來自定義一個(gè)HorizontalScrollView來體現(xiàn)一下用外部攔截法解決沖突的快感。

先上一下代碼:

package com.gxl.viewtest;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.text.LoginFilter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * s
 * Created by GXL on 2016/7/25 0025.
 */
public class HorizontalScrollView extends ViewGroup {

  private final String TAG = "HorizontalScrollView";
  private VelocityTracker mVelocityTracker;
  private Scroller mScroller;
  private int mChildrenSize;
  private int mChildWidth;
  private int mChildIndex;
  //上次滑動(dòng)的坐標(biāo)
  private int mLastX = 0;
  private int mLastY = 0;
  //上次上次攔截滑動(dòng)的坐標(biāo)
  private int mLastXIntercept = 0;
  private int mLastYIntercept = 0;

  public HorizontalScrollView(Context context) {
    super(context);
    init(context);
  }

  public HorizontalScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }

  public HorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }

  public void init(Context context) {
    mVelocityTracker = VelocityTracker.obtain();
    mScroller = new Scroller(context);
  }

  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: {
        int deltaX = x - mLastXIntercept;
        int deltaY = y - mLastYIntercept;
        if (Math.abs(deltaX) > Math.abs(deltaY)) {
          intercepted = true;
        } else {
          intercepted = false;
        }
        break;
      }
      case MotionEvent.ACTION_UP: {
        intercepted = false;
        break;
      }
      default:
        break;
    }
    mLastX = x;
    mLastY = y;
    mLastXIntercept = x;
    mLastYIntercept = y;
    return intercepted;
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    mVelocityTracker.addMovement(event);
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        break;
      case MotionEvent.ACTION_MOVE:
        int deltaX = x - mLastX;
        if((getScrollX()-deltaX)>=0&&(getScrollX()-deltaX)<=(getMeasuredWidth()-ScreenUtils.getScreenWidth(getContext()))) {
          scrollBy(-deltaX, 0);
        }
        break;
      case MotionEvent.ACTION_UP:
        mVelocityTracker.computeCurrentVelocity(1000);
        float xVelocityTracker = mVelocityTracker.getXVelocity();
        if (Math.abs(xVelocityTracker) > 50) {
          if (xVelocityTracker > 0) {
            Log.i(TAG, "快速向右劃");
          } else {
            Log.i(TAG, "快速向左劃");
          }
        }
        mVelocityTracker.clear();
        break;
    }
    mLastX = x;
    mLastY = y;
    return true;
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int measuredWidth = 0;
    int measureHeight = 0;
    final int childCount = getChildCount();
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
    int widthSpaceMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
    int heightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);

    if (childCount == 0) {
      setMeasuredDimension(0, 0);
    } else if (heightSpaceMode == MeasureSpec.AT_MOST && widthSpaceMode == MeasureSpec.AT_MOST) {
      final View childView = getChildAt(0);
      measuredWidth = childView.getMeasuredWidth() * childCount;
      measureHeight = childView.getMeasuredHeight();
      setMeasuredDimension(measuredWidth, measureHeight);
    } else if (heightSpaceMode == MeasureSpec.AT_MOST) {
      measureHeight = getChildAt(0).getMeasuredHeight();
      setMeasuredDimension(widthSpaceSize, measureHeight);
    } else if (widthSpaceMode == MeasureSpec.AT_MOST) {
      final View childView = getChildAt(0);
      measuredWidth = childView.getMeasuredWidth() * childCount;
      setMeasuredDimension(measuredWidth, heightSpaceSize);
    }
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    Log.i(TAG, "onLayout: " + getMeasuredWidth());
    int childleft = 0;
    final int childCount = getChildCount();
    mChildrenSize = childCount;
    for (int i = 0; i < mChildrenSize; i++) {
      final View childView = getChildAt(i);
      if (childView.getVisibility() != View.GONE) {
        final int childWidth = childView.getMeasuredWidth();
        mChildWidth = childWidth;
        childView.layout(childleft, 0, childleft + mChildWidth, childView.getMeasuredHeight());
        childleft += childWidth;
      }
    }
  }

  private void smoothScrollTo(int destX,int destY)
  {
    int scrollX=getScrollX();
    int delta=destX-scrollX;
    mScroller.startScroll(scrollX,0,delta,0,1000);
  }

  @Override
  public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      postInvalidate();
    }
  }
}

再來看一下布局文件
  

 <com.gxl.viewtest.HorizontalScrollView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:background="#00ff00"
    >

    <ListView
      android:id="@+id/listview1"
      android:layout_width="600dp"
      android:layout_height="match_parent"
      android:background="@color/colorPrimary"
      >
    </ListView>

    <ListView
      android:id="@+id/listview2"
      android:layout_width="600dp"
      android:layout_height="match_parent"
      android:background="@color/colorAccent"
      >
    </ListView>

    <ListView
      android:id="@+id/listview3"
      android:layout_width="600dp"
      android:layout_height="match_parent"
      android:background="#ff0000"
      >
    </ListView>

  </com.gxl.viewtest.HorizontalScrollView>

以上就是外部處理滑動(dòng)沖突的代碼,認(rèn)真看一下,思路還是很清晰的。里面還涉及了一些自定義View的知識(shí),我會(huì)在后面的博文中認(rèn)真分析一下代碼,你先看一下onInterceptTouchEvent處理滑動(dòng)沖突的部分。
看一下效果圖哈。

2.2 內(nèi)部攔截法

內(nèi)部攔截法是指父容器不攔截任何事件,所有的事件都傳遞給子元素,如果子元素需要此事件就直接消耗掉,否則就交給父容器去處理,這種方法和Android中的事件分發(fā)機(jī)制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,這個(gè)方法的大體解釋就是:

requestDisallowInterceptTouchEvent是ViewGroup類中的一個(gè)公用方法,參數(shù)是一個(gè)boolean值,官方介紹如下

Called when a child does not want this parent and its ancestors to intercept touch events with ViewGroup.onInterceptTouchEvent(MotionEvent).
This parent should pass this call onto its parents. This parent must obey this request for the duration of the touch (that is, only clear the flag after this parent has received an up or a cancel.

android系統(tǒng)中,一次點(diǎn)擊事件是從父view傳遞到子view中,每一層的view可以決定是否攔截并處理點(diǎn)擊事件或者傳遞到下一層,如果子view不處理點(diǎn)擊事件,則該事件會(huì)傳遞會(huì)父view,由父view去決定是否處理該點(diǎn)擊事件。在子view可以通過設(shè)置此方法去告訴父view不要攔截并處理點(diǎn)擊事件,父view應(yīng)該接受這個(gè)請求直到此次點(diǎn)擊事件結(jié)束。

使用起來外部攔截事件略顯復(fù)雜一點(diǎn)。下面我也先來看一下它的通用模板(注意下面的代碼是定義在子View中的):

public boolean onInterceptTouchEvent(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-mLastXIntercept;
      int deltaY=y=mLastYIntercept;
      if(父容器需要攔截的事件){
        parent.requestDisallowInterceptTouchEvent(false); //父布局需要要攔截此事件
      }
      break;
    }
    case MotionEvent.ACTION_UP: {
      intercepted = false;
      break;
    }
    default:
      break;
    }
    mLastXIntercept=x;
    mLastYIntercept=y;
    return super.dispathTouchEvent(event);
  }

上述代碼是內(nèi)部攔截法的典型代碼,當(dāng)面對不同的滑動(dòng)策略時(shí)只需要修改里面的條件即可,其他不需要修改做改動(dòng)而且也不能改動(dòng)。

 以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論

武胜县| 龙泉市| 龙海市| 双牌县| 普陀区| 太仆寺旗| 新邵县| 穆棱市| 四会市| 美姑县| 安平县| 临沭县| 清苑县| 贞丰县| 桐梓县| 水富县| 武穴市| 抚州市| 红安县| 嘉祥县| 麟游县| 青田县| 临江市| 南岸区| 汽车| 大石桥市| 梅州市| 鹰潭市| 台北县| 烟台市| 务川| 涞水县| 剑阁县| 北宁市| 师宗县| 垦利县| 天祝| 绥宁县| 紫阳县| 泰顺县| 南雄市|