文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

android多种滑动冲突的解决方案

2022-06-06 04:28

关注

一、前言

Android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。

滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。

所以我就写了4个例子来学习如何解决滑动冲突的,这四个例子分别为: 外部拦截法解决横竖冲突、外部拦截法解决同向冲突、内部拦截法解决横竖冲突、内部拦截法解决同向冲突。

先上效果图:

二、实战

1、外部拦截法,解决横竖冲突

思路是,重写父控件的onInterceptTouchEvent方法,然后根据具体的需求,来决定父控件是否拦截事件。如果拦截返回返回true,不拦截返回false。如果父控件拦截了事件,则在父控件的onTouchEvent进行相应的事件处理。

我的这个例子,是一个横向滑动的ViewGroup里面包含了3个竖向滑动的ListView。下面我附上代码,HorizontalEx.Java:



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();
  }
 }
 
 @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:
    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;
    
    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:
    
    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();
 }
}

调用代码:


@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);
 }

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


@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:
    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;
    
    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;
 }

这几乎是一个实现外部拦截事件的模板,这里一定不要在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事件无效。

可以参考一下源码:   


 // 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);
    //清楚标志
    resetTouchState();
   }
   // Check for interception.
   final boolean intercepted;
   if (actionMasked == MotionEvent.ACTION_DOWN
     || mFirstTouchTarget != null) {
     //标志
    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;
   }

那么我们如果想使用 内部拦截法拦截事件。

第一步:

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:



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();
  }
 }
 
 @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



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;
 }
 
 @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);
 }
}

调用代码:


 @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种拦截方法已经学习完毕,下面我们来学习如何解决同向滑动冲突。

其实和上面的2个例子思路是一样的,只是用来判断是否拦截的那块逻辑不同而已。

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

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

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);
 }
 
 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);
  
  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);
 }
 
 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();
  }
 }
 
 public void refreshComplete() {
  mScoller.startScroll(0, getScrollY(), 0, mInitScrollY - getScrollY(), SCROLL_DURATION);
  currentStatus = STATUS_IDLE;
  invalidate();
 }
 
 public void showFooter() {
  if(currentStatus==STATUS_LOAD_MORE) return ;
  currentStatus = STATUS_LOAD_MORE ;
  mScoller.startScroll(0, getScrollY(), 0, mFootView.getMeasuredHeight()
    , SCROLL_DURATION);
  invalidate();
 }
 
 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();
}

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


package com.blueberry.sample.widget.refresh;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.ListView;

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) {
    
    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、内部拦截法解决同向滑动

同样是一个下拉刷新组件,因为实现原理都一样,所以这个写的比较随意些。主要还是如果解决滑动冲突。

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;

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("数据—" + 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);
 }
 
 @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);
  }
  
  @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;
  }
 }
}
您可能感兴趣的文章:Android listview的滑动冲突解决方法浅谈Android View滑动冲突的解决方法Android下拉刷新与轮播图滑动冲突解决方案Android滑动冲突的完美解决浅谈Android实践之ScrollView中滑动冲突处理解决方案Android中RecyclerView嵌套滑动冲突解决的代码片段android中view手势滑动冲突的解决方法Android滑动冲突的完美解决方案Android App中ViewPager所带来的滑动冲突问题解决方法Android中DrawerLayout+ViewPager滑动冲突的解决方法


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯