android多種滑動(dòng)沖突的解決方案

一、前言

公司主營(yíng)業(yè)務(wù):成都做網(wǎng)站、網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)公司推出恩陽(yáng)免費(fèi)做網(wǎng)站回饋大家。

Android 中解決滑動(dòng)的方案有2種:外部攔截法 和內(nèi)部攔截法。

滑動(dòng)沖突也存在2種場(chǎng)景: 橫豎滑動(dòng)沖突、同向滑動(dòng)沖突。

所以我就寫(xiě)了4個(gè)例子來(lái)學(xué)習(xí)如何解決滑動(dòng)沖突的,這四個(gè)例子分別為: 外部攔截法解決橫豎沖突、外部攔截法解決同向沖突、內(nèi)部攔截法解決橫豎沖突、內(nèi)部攔截法解決同向沖突。

先上效果圖:

android多種滑動(dòng)沖突的解決方案

二、實(shí)戰(zhàn)

1、外部攔截法,解決橫豎沖突

思路是,重寫(xiě)父控件的onInterceptTouchEvent方法,然后根據(jù)具體的需求,來(lái)決定父控件是否攔截事件。如果攔截返回返回true,不攔截返回false。如果父控件攔截了事件,則在父控件的onTouchEvent進(jìn)行相應(yīng)的事件處理。

我的這個(gè)例子,是一個(gè)橫向滑動(dòng)的ViewGroup里面包含了3個(gè)豎向滑動(dòng)的ListView。下面我附上代碼,HorizontalEx.Java:

/**
 * Created by blueberry on 2016/6/20.
 *
 * 解決交錯(cuò)的滑動(dòng)沖突
 *
 * 外部攔截法
 */
public class HorizontalEx extends ViewGroup {

 private static final String TAG = "HorizontalEx";

 private boolean isFirstTouch = true;
 private int childIndex;
 private int childCount;
 private int lastXIntercept, lastYIntercept, lastX, lastY;

 private Scroller mScroller;
 private VelocityTracker mVelocityTracker;

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

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

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

 private void init() {
  mScroller = new Scroller(getContext());
  mVelocityTracker = VelocityTracker.obtain();
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int width = MeasureSpec.getSize(widthMeasureSpec);
  int height = MeasureSpec.getSize(heightMeasureSpec);
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);

  childCount = getChildCount();
  measureChildren(widthMeasureSpec, heightMeasureSpec);

  if (childCount == 0) {
   setMeasuredDimension(0, 0);
  } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
   width = childCount * getChildAt(0).getMeasuredWidth();
   height = getChildAt(0).getMeasuredHeight();
   setMeasuredDimension(width, height);
  } else if (widthMode == MeasureSpec.AT_MOST) {
   width = childCount * getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(width, height);
  } else {
   height = getChildAt(0).getMeasuredHeight();
   setMeasuredDimension(width, height);
  }
 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  int left = 0;
  for (int i = 0; i < getChildCount(); i++) {
   final View child = getChildAt(i);
   child.layout(left + l, t, r + left, b);
   left += child.getMeasuredWidth();
  }
 }

 /**
  * 攔截事件
  * @param ev
  * @return
  */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  boolean intercepted = false;
  int x = (int) ev.getX();
  int y = (int) ev.getY();

  switch (ev.getAction()) {
   /*如果攔截了Down事件,則子類不會(huì)拿到這個(gè)事件序列*/
   case MotionEvent.ACTION_DOWN:
    lastXIntercept = x;
    lastYIntercept = y;
    intercepted = false;
    if (!mScroller.isFinished()) {
     mScroller.abortAnimation();
     intercepted = true;
    }
    break;
   case MotionEvent.ACTION_MOVE:
    final int deltaX = x - lastXIntercept;
    final int deltaY = y - lastYIntercept;
    /*根據(jù)條件判斷是否攔截該事件*/
    if (Math.abs(deltaX) > Math.abs(deltaY)) {
     intercepted = true;
    } else {
     intercepted = false;
    }
    break;
   case MotionEvent.ACTION_UP:
    intercepted = false;
    break;

  }
  lastXIntercept = x;
  lastYIntercept = y;
  return intercepted;
 }


 @Override
 public boolean onTouchEvent(MotionEvent event) {
  int x = (int) event.getX();
  int y = (int) event.getY();
  mVelocityTracker.addMovement(event);
  ViewConfiguration configuration = ViewConfiguration.get(getContext());
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    if (!mScroller.isFinished()) {
     mScroller.abortAnimation();
    }
    break;
   case MotionEvent.ACTION_MOVE:
    /*因?yàn)檫@里父控件拿不到Down事件,所以使用一個(gè)布爾值,
     當(dāng)事件第一次來(lái)到父控件時(shí),對(duì)lastX,lastY賦值*/
    if (isFirstTouch) {
     lastX = x;
     lastY = y;
     isFirstTouch = false;
    }
    final int deltaX = x - lastX;
    scrollBy(-deltaX, 0);
    break;
   case MotionEvent.ACTION_UP:
    int scrollX = getScrollX();
    final int childWidth = getChildAt(0).getWidth();
    mVelocityTracker.computeCurrentVelocity(1000, configuration.getScaledMaximumFlingVelocity());
    float xVelocity = mVelocityTracker.getXVelocity();
    if (Math.abs(xVelocity) > configuration.getScaledMinimumFlingVelocity()) {
     childIndex = xVelocity < 0 ? childIndex + 1 : childIndex - 1;
    } else {
     childIndex = (scrollX + childWidth / 2) / childWidth;
    }
    childIndex = Math.min(getChildCount() - 1, Math.max(childIndex, 0));
    smoothScrollBy(childIndex * childWidth - scrollX, 0);
    mVelocityTracker.clear();
    isFirstTouch = true;
    break;
  }

  lastX = x;
  lastY = y;
  return true;
 }

 void smoothScrollBy(int dx, int dy) {
  mScroller.startScroll(getScrollX(), getScrollY(), dx, dy, 500);
  invalidate();
 }

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

 @Override
 protected void onDetachedFromWindow() {
  super.onDetachedFromWindow();
  mVelocityTracker.recycle();
 }
}

調(diào)用代碼:

@Override
 public void showOutHVData(List<String> data1, List<String> data2, List<String> data3) {
  ListView listView1 = new ListView(getContext());
  ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data1);
  listView1.setAdapter(adapter1);

  ListView listView2 = new ListView(getContext());
  ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data2);
  listView2.setAdapter(adapter2);

  ListView listView3 = new ListView(getContext());
  ArrayAdapter<String> adapter3 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data3);
  listView3.setAdapter(adapter3);

  ViewGroup.LayoutParams params
    = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.MATCH_PARENT);

  mHorizontalEx.addView(listView1, params);
  mHorizontalEx.addView(listView2, params);
  mHorizontalEx.addView(listView3, params);
 }

其實(shí)外部攔截的主要思想都在于對(duì)onInterceptTouchEvent的重寫(xiě)。

@Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  boolean intercepted = false;
  int x = (int) ev.getX();
  int y = (int) ev.getY();

  switch (ev.getAction()) {
   /*如果攔截了Down事件,則子類不會(huì)拿到這個(gè)事件序列*/
   case MotionEvent.ACTION_DOWN:
    lastXIntercept = x;
    lastYIntercept = y;
    intercepted = false;
    if (!mScroller.isFinished()) {
     mScroller.abortAnimation();
     intercepted = true;
    }
    break;
   case MotionEvent.ACTION_MOVE:
    final int deltaX = x - lastXIntercept;
    final int deltaY = y - lastYIntercept;
    /*根據(jù)條件判斷是否攔截該事件*/
    if (Math.abs(deltaX) > Math.abs(deltaY)) {
     intercepted = true;
    } else {
     intercepted = false;
    }
    break;
   case MotionEvent.ACTION_UP:
    intercepted = false;
    break;

  }
  lastXIntercept = x;
  lastYIntercept = y;
  return intercepted;
 }

這幾乎是一個(gè)實(shí)現(xiàn)外部攔截事件的模板,這里一定不要在ACTION_DOWN 中返回 true,否則會(huì)讓子VIew沒(méi)有機(jī)會(huì)得到事件,因?yàn)槿绻贏CTION_DOWN的時(shí)候返回了 true,同一個(gè)事件序列ViewGroup的disPatchTouchEvent就不會(huì)在調(diào)用onInterceptTouchEvent方法了。

還有就是 在ACTION_UP中返回false,因?yàn)槿绻缚丶r截了ACTION_UP,那么子View將得不到UP事件,那么將會(huì)影響子View的 Onclick方法等。但這對(duì)父控件是沒(méi)有影響的,因?yàn)槿绻歉缚丶覣CITON_MOVE中 就攔截了事件,他們UP事件必定也會(huì)交給它處理,因?yàn)橛心敲匆粭l定律叫做:父控件一但攔截了事件,那么同一個(gè)事件序列的所有事件都將交給他處理。這條結(jié)論在我的上一篇文章中已經(jīng)分析過(guò)。

最后就是在 ACTION_MOVE中根據(jù)需求決定是否攔截。

2、內(nèi)部攔截法,解決橫豎沖突

內(nèi)部攔截主要依賴于父控件的 requestDisallowInterceptTouchEvent方法,關(guān)于這個(gè)方法我的上篇文章其實(shí)已經(jīng)分析過(guò)。他設(shè)置父控件的一個(gè)標(biāo)志(FLAG_DISALLOW_INTERCEPT)

這個(gè)標(biāo)志可以決定父控件是否攔截事件,如果設(shè)置了這個(gè)標(biāo)志則不攔截,如果沒(méi)設(shè)這個(gè)標(biāo)志,它就會(huì)調(diào)用父控件的onInterceptTouchEvent()來(lái)詢問(wèn)父控件是否攔截。但這個(gè)標(biāo)志對(duì)Down事件無(wú)效。

可以參考一下源碼:   

 // Handle an initial down.
   if (actionMasked == MotionEvent.ACTION_DOWN) {
    // Throw away all previous state when starting a new touch gesture.
    // The framework may have dropped the up or cancel event for the previous gesture
    // due to an app switch, ANR, or some other state change.
    cancelAndClearTouchTargets(ev);
    //清楚標(biāo)志
    resetTouchState();
   }

   // Check for interception.
   final boolean intercepted;
   if (actionMasked == MotionEvent.ACTION_DOWN
     || mFirstTouchTarget != null) {
     //標(biāo)志
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
     intercepted = onInterceptTouchEvent(ev);
     ev.setAction(action); // restore action in case it was changed
    } else {
     intercepted = false;
    }
   } else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
   }

那么我們?nèi)绻胧褂?內(nèi)部攔截法攔截事件。

第一步:

a、我們要重寫(xiě)父控件的onInterceptTouchEvent,在ACTION_DOWN的時(shí)候返回false,負(fù)責(zé)的話子View調(diào)用requestDisallowInterceptTouchEvent也將無(wú)能為力。

b、還有就是其他事件的話都返回true,這樣就把能否攔截事件的權(quán)利交給了子View。

第二步:

在子View的dispatchTouchEvent中 來(lái)決定是否讓父控件攔截事件。

a. 先要在MotionEvent.ACTION_DOWN:的時(shí)候使用mHorizontalEx2.requestDisallowInterceptTouchEvent(true);,負(fù)責(zé)的話,下一個(gè)事件到來(lái)時(shí),就交給父控件了。

b. 然后在MotionEvent.ACTION_MOVE: 根據(jù)業(yè)務(wù)邏輯決定是否調(diào)用mHorizontalEx2.requestDisallowInterceptTouchEvent(false);來(lái)決定父控件是否攔截事件。

上代碼HorizontalEx2.java:

/**
 * Created by blueberry on 2016/6/20.
 * 內(nèi)部攔截
 * 和 ListViewEx配合使用
 */
public class HorizontalEx2 extends ViewGroup {

 private int lastX, lastY;
 private int childIndex;
 private Scroller mScroller;
 private VelocityTracker mVelocityTracker;

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

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

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

 private void init() {
  mScroller = new Scroller(getContext());
  mVelocityTracker = VelocityTracker.obtain();
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int width = MeasureSpec.getSize(widthMeasureSpec);
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int height = MeasureSpec.getSize(heightMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);

  int childCount = getChildCount();
  measureChildren(widthMeasureSpec, heightMeasureSpec);

  if (childCount == 0) {
   setMeasuredDimension(0, 0);
  } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
   height = getChildAt(0).getMeasuredHeight();
   width = childCount * getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(width, height);
  } else if (widthMode == MeasureSpec.AT_MOST) {
   width = childCount * getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(width, height);
  } else {
   height = getChildAt(0).getMeasuredHeight();
   setMeasuredDimension(width, height);
  }
 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  int leftOffset = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   child.layout(l + leftOffset, t, r + leftOffset, b);
   leftOffset += child.getMeasuredWidth();
  }
 }

 /**
  * 不攔截Down事件,其他一律攔截
  * @param ev
  * @return
  */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  if (ev.getAction() == MotionEvent.ACTION_DOWN) {
   if (!mScroller.isFinished()) {
    mScroller.abortAnimation();
    return true;
   }
   return false;
  } else {
   return true;
  }
 }

 private boolean isFirstTouch = true;

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  int x = (int) event.getX();
  int y = (int) event.getY();
  mVelocityTracker.addMovement(event);
  ViewConfiguration configuration = ViewConfiguration.get(getContext());
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    if (!mScroller.isFinished()) {
     mScroller.abortAnimation();
    }
    break;
   case MotionEvent.ACTION_MOVE:
    if (isFirstTouch) {
     isFirstTouch = false;
     lastY = y;
     lastX = x;
    }
    final int deltaX = x - lastX;
    scrollBy(-deltaX, 0);
    break;
   case MotionEvent.ACTION_UP:
    isFirstTouch = true;
    int scrollX = getScrollX();
    mVelocityTracker.computeCurrentVelocity(1000, configuration.getScaledMaximumFlingVelocity());
    float mVelocityX = mVelocityTracker.getXVelocity();
    if (Math.abs(mVelocityX) > configuration.getScaledMinimumFlingVelocity()) {
     childIndex = mVelocityX < 0 ? childIndex + 1 : childIndex - 1;
    } else {
     childIndex = (scrollX + getChildAt(0).getWidth() / 2) / getChildAt(0).getWidth();
    }
    childIndex = Math.min(getChildCount() - 1, Math.max(0, childIndex));
    smoothScrollBy(childIndex*getChildAt(0).getWidth()-scrollX,0);
    mVelocityTracker.clear();
    break;
  }

  lastX = x;
  lastY = y;
  return true;
 }

 private void smoothScrollBy(int dx, int dy) {
  mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,500);
  invalidate();
 }

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

 @Override
 protected void onDetachedFromWindow() {
  super.onDetachedFromWindow();
  mVelocityTracker.recycle();
 }
}

ListViewEx.java

/**
 * 內(nèi)部攔截事件
 */
public class ListViewEx extends ListView {

 private int lastXIntercepted, lastYIntercepted;

 private HorizontalEx2 mHorizontalEx2;

 public ListViewEx(Context context) {
  super(context);
 }

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

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

 public HorizontalEx2 getmHorizontalEx2() {
  return mHorizontalEx2;
 }

 public void setmHorizontalEx2(HorizontalEx2 mHorizontalEx2) {
  this.mHorizontalEx2 = mHorizontalEx2;
 }

 /**
  * 使用 outter.requestDisallowInterceptTouchEvent();
  * 來(lái)決定父控件是否對(duì)事件進(jìn)行攔截
  * @param ev
  * @return
  */
 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  int x = (int) ev.getX();
  int y = (int) ev.getY();
  switch (ev.getAction()) {
   case MotionEvent.ACTION_DOWN:
    mHorizontalEx2.requestDisallowInterceptTouchEvent(true);
    break;
   case MotionEvent.ACTION_MOVE:
    final int deltaX = x-lastYIntercepted;
    final int deltaY = y-lastYIntercepted;
    if(Math.abs(deltaX)>Math.abs(deltaY)){
     mHorizontalEx2.requestDisallowInterceptTouchEvent(false);
    }
    break;
   case MotionEvent.ACTION_UP:
    break;
  }
  lastXIntercepted = x;
  lastYIntercepted = y;
  return super.dispatchTouchEvent(ev);
 }
}

調(diào)用代碼:

 @Override
 public void showInnerHVData(List<String> data1, List<String> data2, List<String> data3) {

  ListViewEx listView1 = new ListViewEx(getContext());
  ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data1);
  listView1.setAdapter(adapter1);
  listView1.setmHorizontalEx2(mHorizontalEx2);

  ListViewEx listView2 = new ListViewEx(getContext());
  ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data2);
  listView2.setAdapter(adapter2);
  listView2.setmHorizontalEx2(mHorizontalEx2);

  ListViewEx listView3 = new ListViewEx(getContext());
  ArrayAdapter<String> adapter3 = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, data3);
  listView3.setAdapter(adapter3);
  listView3.setmHorizontalEx2(mHorizontalEx2);

  ViewGroup.LayoutParams params
    = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.MATCH_PARENT);

  mHorizontalEx2.addView(listView1, params);
  mHorizontalEx2.addView(listView2, params);
  mHorizontalEx2.addView(listView3, params);
 }

至此,2種攔截方法已經(jīng)學(xué)習(xí)完畢,下面我們來(lái)學(xué)習(xí)如何解決同向滑動(dòng)沖突。

其實(shí)和上面的2個(gè)例子思路是一樣的,只是用來(lái)判斷是否攔截的那塊邏輯不同而已。

下面的例子,是一個(gè)下拉刷新的一個(gè)控件。

3、外部攔截 解決同向滑動(dòng)沖突

RefreshLayoutBase.java

package com.blueberry.sample.widget.refresh;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;

import com.blueberry.sample.R;

/**

 *外部攔截(同向)
 *
 */
public abstract class RefreshLayoutBase<T extends View> extends ViewGroup {

 private static final String TAG = "RefreshLayoutBase";

 public static final int STATUS_LOADING = 1;
 public static final int STATUS_RELEASE_TO_REFRESH = 2;
 public static final int STATUS_PULL_TO_REFRESH = 3;
 public static final int STATUS_IDLE = 4;
 public static final int STATUS_LOAD_MORE =5;
 private static int SCROLL_DURATION =500;

 protected ViewGroup mHeadView;
 protected ViewGroup mFootView;
 private T contentView;
 private ProgressBar headProgressBar;
 private TextView headTv;
 private ProgressBar footProgressBar;
 private TextView footTv;

 private boolean isFistTouch = true;

 protected int currentStatus = STATUS_IDLE;
 private int mScreenWidth;
 private int mScreenHeight;
 private int mLastXIntercepted;
 private int mLastYIntercepted;
 private int mLastX;
 private int mLastY;
 protected int mInitScrollY = 0;
 private int mTouchSlop;

 protected Scroller mScoller;

 private OnRefreshListener mOnRefreshListener;

 public RefreshLayoutBase(Context context) {
  this(context, null);
 }

 public RefreshLayoutBase(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public RefreshLayoutBase(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  getScreenSize();
  initView();
  mScoller = new Scroller(context);
  mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  setPadding(0, 0, 0, 0);
 }

 public void setContentView(T view) {
  addView(view, 1);
 }

 public OnRefreshListener getOnRefreshListener() {
  return mOnRefreshListener;
 }

 public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
  this.mOnRefreshListener = mOnRefreshListener;
 }

 private void initView() {
  setupHeadView();
  setupFootView();
 }

 private void getScreenSize() {
  WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics metrics = new DisplayMetrics();
  wm.getDefaultDisplay().getMetrics(metrics);
  mScreenWidth = metrics.widthPixels;
  mScreenHeight = metrics.heightPixels;
 }

 private int dp2px(int dp) {
  WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics metrics = new DisplayMetrics();
  wm.getDefaultDisplay().getMetrics(metrics);
  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
 }

 /**
  * 設(shè)置頭布局
  */
 private void setupHeadView() {
  mHeadView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_head_view, null);
  mHeadView.setBackgroundColor(Color.RED);
  headProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressbar);
  headTv = (TextView) mHeadView.findViewById(R.id.head_tv);
  /*設(shè)置 實(shí)際高度為 1/4 ,但內(nèi)容區(qū)域只有 100dp*/
  ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, mScreenHeight / 4);
  mHeadView.setLayoutParams(layoutParams);
  mHeadView.setPadding(0, mScreenHeight / 4 - dp2px(100), 0, 0);
  addView(mHeadView);
 }

 /**
  * 設(shè)置尾布局
  */
 private void setupFootView() {
  mFootView = (ViewGroup) View.inflate(getContext(), R.layout.fresh_foot_view, null);
  mFootView.setBackgroundColor(Color.BLUE);
  footProgressBar = (ProgressBar) mFootView.findViewById(R.id.fresh_foot_progressbar);
  footTv = (TextView) mFootView.findViewById(R.id.fresh_foot_tv);
  addView(mFootView);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int height = MeasureSpec.getSize(heightMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);

  int finalHeight = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   measureChild(child, widthMeasureSpec, heightMeasureSpec);
   finalHeight += child.getMeasuredHeight();
  }

  if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
   widthSize = getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(widthSize, finalHeight);
  } else if (widthMode == MeasureSpec.AT_MOST) {
   widthSize = getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(widthSize, height);
  } else {
   setMeasuredDimension(widthSize, finalHeight);
  }

 }


 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  int topOffset = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
   topOffset += child.getMeasuredHeight();
  }
  mInitScrollY = mHeadView.getMeasuredHeight() + getPaddingTop();
  scrollTo(0, mInitScrollY);

 }

 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  boolean intercepted = false;
  int x = (int) ev.getX();
  int y = (int) ev.getY();
  switch (ev.getAction()) {
   case MotionEvent.ACTION_DOWN:
    mLastXIntercepted = x;
    mLastYIntercepted = y;
    break;
   case MotionEvent.ACTION_MOVE:
    final int deltaY = x - mLastYIntercepted;
    if (isTop() && deltaY > 0 && Math.abs(deltaY) > mTouchSlop) {
     /*下拉*/
     intercepted = true;
    }
    break;
   case MotionEvent.ACTION_UP:
    break;
  }
  mLastXIntercepted = x;
  mLastYIntercepted = y;
  return intercepted;
 }

 private void doRefresh() {
  Log.i(TAG, "doRefresh: ");
  if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
   mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
   currentStatus = STATUS_IDLE;
  } else if (currentStatus == STATUS_PULL_TO_REFRESH) {
   mScoller.startScroll(0,getScrollY(),0,0-getScrollY(),SCROLL_DURATION);
   if (null != mOnRefreshListener) {
    currentStatus = STATUS_LOADING;
    mOnRefreshListener.refresh();
   }
  }
  invalidate();
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  int x = (int) event.getX();
  int y = (int) event.getY();
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    if (!mScoller.isFinished()) {
     mScoller.abortAnimation();
    }
    mLastX = x;
    mLastY = y;
    break;
   case MotionEvent.ACTION_MOVE:
    if (isFistTouch) {
     isFistTouch = false;
     mLastX = x;
     mLastY = y;
    }
    final int deltaY = y - mLastY;
    if (currentStatus != STATUS_LOADING) {
     changeScrollY(deltaY);
    }
    break;
   case MotionEvent.ACTION_UP:
    isFistTouch = true;
    doRefresh();
    break;
  }

  mLastX = x;
  mLastY = y;
  return true;
 }

 private void changeScrollY(int deltaY) {
  Log.i(TAG, "changeScrollY: ");
  int curY = getScrollY();
  if (deltaY > 0) {
   /*下拉*/
   if (curY - deltaY > getPaddingTop()) {
    scrollBy(0, -deltaY);
   }
  } else {
   /*上拉*/
   if (curY - deltaY <= mInitScrollY) {
    scrollBy(0, -deltaY);
   }
  }

  curY = getScrollY();
  int slop = mInitScrollY / 2;
  if (curY > 0 && curY <=slop) {
   currentStatus = STATUS_PULL_TO_REFRESH;
  } else if (curY > 0 && curY >= slop) {
   currentStatus = STATUS_RELEASE_TO_REFRESH;
  }
 }

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

 /**
  * 加載完成調(diào)用這個(gè)方法
  */
 public void refreshComplete() {
  mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
  currentStatus = STATUS_IDLE;
  invalidate();
 }

 /**
  * 顯示 Footer
  */
 public void showFooter() {
  if(currentStatus==STATUS_LOAD_MORE) return ;
  currentStatus = STATUS_LOAD_MORE ;
  mScoller.startScroll(0, getScrollY(), 0, mFootView.getMeasuredHeight()
    , SCROLL_DURATION);
  invalidate();

 }


 /**
  * loadMore完成之后調(diào)用
  */
 public void footerComplete() {
  mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
  invalidate();
  currentStatus = STATUS_IDLE;
 }

 public interface OnRefreshListener {
  void refresh();
 }

 abstract boolean isTop();

 abstract boolean isBottom();

}

它是一個(gè)抽象類,需要編寫(xiě)子類繼承isTop()和 isBottom()方法。下面給出它的一個(gè)實(shí)現(xiàn)類:

package com.blueberry.sample.widget.refresh;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.ListView;

/**
 * Created by blueberry on 2016/6/21.
 *
 * RefreshLayoutBase 的一個(gè)實(shí)現(xiàn)類
 */
public class RefreshListView extends RefreshLayoutBase<ListView> {

 private static final String TAG = "RefreshListView";

 private ListView listView;
 private OnLoadListener loadListener;

 public RefreshListView(Context context) {
  super(context);
 }

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

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

 public ListView getListView() {
  return listView;
 }

 public void setListView(final ListView listView) {
  this.listView = listView;
  setContentView(listView);

  this.listView.setOnScrollListener(new AbsListView.OnScrollListener() {
   @Override
   public void onScrollStateChanged(AbsListView view, int scrollState) {
   }

   @Override
   public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    /*這里存在一個(gè)bug: 當(dāng)listView滑動(dòng)到底部的時(shí)候,如果下拉也會(huì)出現(xiàn)footer
    * 這是因?yàn)椋瑫簳r(shí)還沒(méi)有想到如何判斷是下拉還是上拉。
    * 如果要解決此問(wèn)題,我覺(jué)得應(yīng)該重寫(xiě)listView 的onTouchEvent來(lái)判斷手勢(shì)方向
    * 次模塊主要解決豎向滑動(dòng)沖突,故現(xiàn)將此問(wèn)題放下。
    * */
    if (currentStatus == STATUS_IDLE
      && getScrollY() <= mInitScrollY && isBottom()
      ) {
     showFooter();
     if (null != loadListener) {
      loadListener.onLoadMore();
     }
    }

   }
  });
 }

 public OnLoadListener getLoadListener() {
  return loadListener;
 }

 public void setLoadListener(OnLoadListener loadListener) {
  this.loadListener = loadListener;
 }

 @Override
 boolean isTop() {
  return listView.getFirstVisiblePosition() == 0
    && getScrollY() <= mHeadView.getMeasuredHeight();
 }

 @Override
 boolean isBottom() {
  return listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1;
 }

 public interface OnLoadListener {
  void onLoadMore();
 }
}

4、內(nèi)部攔截法解決同向滑動(dòng)

同樣是一個(gè)下拉刷新組件,因?yàn)閷?shí)現(xiàn)原理都一樣,所以這個(gè)寫(xiě)的比較隨意些。主要還是如果解決滑動(dòng)沖突。

RefreshLayoutBase2.java

package com.blueberry.sample.widget.refresh;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Scroller;

import com.blueberry.sample.R;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by blueberry on 2016/6/22.
 * 結(jié)合內(nèi)部類 ListVieEx
 * 內(nèi)部攔截法,同向
 */
public class RefreshLayoutBase2 extends ViewGroup {

 private static final String TAG = "RefreshLayoutBase2";

 private static List<String> datas;

 static {
  datas = new ArrayList<>();
  for (int i = 0; i < 40; i++) {
   datas.add("數(shù)據(jù)—" + i);
  }
 }

 private ViewGroup headView;
 private ListViewEx lv;

 private int lastY;
 public int mInitScrollY;

 private Scroller mScroller;

 public RefreshLayoutBase2(Context context) {
  this(context, null);
 }

 public RefreshLayoutBase2(Context context, AttributeSet attrs) {
  this(context, attrs, 0);

 }

 public RefreshLayoutBase2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  mScroller = new Scroller(context);
  setupHeadView(context);
  setupContentView(context);

 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int height = MeasureSpec.getSize(heightMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);

  int finalHeight = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   measureChild(child, widthMeasureSpec, heightMeasureSpec);
   finalHeight += child.getMeasuredHeight();
  }

  if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
   widthSize = getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(widthSize, finalHeight);
  } else if (widthMode == MeasureSpec.AT_MOST) {
   widthSize = getChildAt(0).getMeasuredWidth();
   setMeasuredDimension(widthSize, height);
  } else {
   setMeasuredDimension(widthSize, finalHeight);
  }

 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  int topOffset = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   child.layout(getPaddingLeft(), getPaddingTop() + topOffset, r, getPaddingTop() + child.getMeasuredHeight() + topOffset);
   topOffset += child.getMeasuredHeight();
  }
  mInitScrollY = headView.getMeasuredHeight() + getPaddingTop();
  scrollTo(0, mInitScrollY);

 }

 /**
  * 不攔截Down 其他一律攔截
  * @param ev
  * @return
  */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  if (ev.getAction() == MotionEvent.ACTION_DOWN) return false;
  return true;
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  int y = (int) event.getY();
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    break;
   case MotionEvent.ACTION_MOVE:
    final int deltaY = y-lastY;
    Log.i(TAG, "onTouchEvent: deltaY: "+deltaY);
    if (deltaY >= 0 && lv.isTop() && getScrollY() - deltaY >=getPaddingTop()) {
      scrollBy(0, -deltaY);
    }
    break;
   case MotionEvent.ACTION_UP:
    this.postDelayed(new Runnable() {
     @Override
     public void run() {
      mScroller.startScroll(0,getScrollY(),0,mInitScrollY-getScrollY());
      invalidate();
     }
    },2000);
    break;
  }

  lastY = y ;
  return true;
 }

 private void setupHeadView(Context context) {
  headView = (ViewGroup) View.inflate(context, R.layout.fresh_head_view, null);
  headView.setBackgroundColor(Color.RED);
  ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);
  addView(headView, params);
 }

 public void setupContentView(Context context) {
  lv = new ListViewEx(context, this);
  lv.setBackgroundColor(Color.BLUE);
  ArrayAdapter<String> adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, datas);
  lv.setAdapter(adapter);
  addView(lv, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
 }

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

 public static class ListViewEx extends ListView {

  private RefreshLayoutBase2 outter;

  public ListViewEx(Context context, RefreshLayoutBase2 outter) {
   super(context);
   this.outter = outter;
  }

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

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

  /**
   * 使用 outter.requestDisallowInterceptTouchEvent();
   * 來(lái)決定父控件是否對(duì)事件進(jìn)行攔截
   * @param ev
   * @return
   */
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
   switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
     outter.requestDisallowInterceptTouchEvent(true);
     break;
    case MotionEvent.ACTION_MOVE:

     if ( isTop() && outter.getScrollY() <= outter.mInitScrollY) {
      outter.requestDisallowInterceptTouchEvent(false);
     }
     break;

   }
   return super.dispatchTouchEvent(ev);
  }

  public boolean isTop() {
   return getFirstVisiblePosition() ==0;
  }
 }
}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

本文標(biāo)題:android多種滑動(dòng)沖突的解決方案
標(biāo)題URL:http://bm7419.com/article26/pcedcg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、域名注冊(cè)、網(wǎng)站收錄定制開(kāi)發(fā)、做網(wǎng)站關(guān)鍵詞優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司