iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >android多种滑动冲突的解决方案
  • 703
分享到

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

解决方案Android 2022-06-06 04:06:39 703人浏览 八月长安
摘要

一、前言 Android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。 滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。 所以我就写了4个例子来学习如何

一、前言

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滑动冲突的解决方法


--结束END--

本文标题: android多种滑动冲突的解决方案

本文链接: https://www.lsjlt.com/news/22355.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android中DrawerLayout+ViewPager滑动冲突的解决方法
    DrawerLayout 是 Android 官方的侧滑菜单控件,而 ViewPager 相信大家都很熟悉了。今天这里就讲一下当在 DrawerLayout 中嵌套 ViewPager 时,要如何解决滑动冲突的问题,效果如下:首先,让我们先...
    99+
    2023-05-31
    android drawerlayout viewpager
  • Android之解决RecyclerView与NestedScrollView的滑动冲突方法
    1、解决RecyclerView与NestedScrollView的滑动冲突 问题一:当我们滑动RecyclerView组件时,上方的轮播图并没有进行滑动(NestedScrollView没有滑动,即...
    99+
    2023-09-15
    android java 开发语言
  • ViewPager2滑动冲突的解决方法
    ViewPager2滑动冲突解决,供大家参考,具体内容如下 本文章对ViewPager2的滑动冲突没有提供完善的解决方案,仅为巩固解决滑动冲突方面的知识 首先看看没有解决滑动冲突时...
    99+
    2024-04-02
  • Android中怎么解决嵌套滑动冲突
    本篇文章为大家展示了Android中怎么解决嵌套滑动冲突,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一.会产生滑动冲突的情况那么什么时候会产生滑动冲突呢?比如你有个activity,activit...
    99+
    2023-05-30
    android
  • Android应用中出现滑动冲突如何解决
    今天就跟大家聊聊有关Android应用中出现滑动冲突如何解决,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。场景一:类似于ViewPager嵌套Fragmnet并且在Fragmnet中...
    99+
    2023-05-31
    android roi
  • Android应用中的View出现滑动冲突如何解决
    本篇文章给大家分享的是有关Android应用中的View出现滑动冲突如何解决,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、外部滑动方向和内部滑动方向不一致考虑这样一种场景,...
    99+
    2023-05-31
    view roi android
  • Android使用NestedScrollView 内嵌RecycleView滑动冲突问题解决
    目录场景描述实现思路问题和优化优化场景描述 使用NestedScrollView 内嵌RecycleView时,当用户上滑时,NestedScrollView需要首先响应上滑事件,直...
    99+
    2024-04-02
  • Android应用的中滑动事件出现冲突如何解决
    Android应用的中滑动事件出现冲突如何解决?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。外部拦截法外部拦截法是指在有点击事件时都要经过父容器,那么在父容器时如果需要拦截...
    99+
    2023-05-31
    android roi
  • springBoot启动报错log4j冲突的解决方案
    springBoot启动报错log4j冲突 先上一段报错内容 SLF4J: Class path contains multiple SLF4J bindings. SLF4J: ...
    99+
    2024-04-02
  • 在Android中使用listview时出现滑动冲突如何解决
    在Android中使用listview时出现滑动冲突如何解决?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Android listview的滑动冲突解决方法在Android开发...
    99+
    2023-05-31
    listview android roi
  • Android使用NestedScrollView内嵌RecycleView滑动冲突问题如何解决
    这篇“Android使用NestedScrollView内嵌RecycleView滑动冲突问题如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,...
    99+
    2023-07-02
  • @feignclient名字冲突的解决方案
    目录@feignclient名字冲突经过研究,解决方法如下@FeignClient同一个name,多个配置类解决方案@feignclient名字冲突 在启动springcloud项目...
    99+
    2024-04-02
  • spring security与corsFilter冲突的解决方案
    目录spring security与corsFilter冲突问题解决方案springboot解决跨域问题(CorsConfig)项目实践小结一下spring security与cor...
    99+
    2024-04-02
  • 使用ViewPager2时出现滑动冲突如何解决
    今天就跟大家聊聊有关使用ViewPager2时出现滑动冲突如何解决,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。MainActivity.javapackage com.e...
    99+
    2023-06-14
  • Keras多线程机制与flask多线程冲突的解决方案
    在使用flask部署Keras,tensorflow等框架时候,经常出现 FailedPreconditionError: Attempting to use uninitiali...
    99+
    2024-04-02
  • Android事件冲突解决悬浮窗拖拽处理方案
    目录需求场景问题暴露解决思路需求场景 最近项目中要做一个音乐播放悬浮按钮的功能,最终实现效果如下: 问题暴露 悬浮窗布局文件就不放了,就是水平LinearLayout里面放几个I...
    99+
    2023-03-01
    Android事件冲突悬浮窗拖拽 Android 事件冲突
  • springboot-启动bean冲突的解决
    目录启动bean冲突启动提示bean重复问题先说结论原理启动bean冲突 在一次启动中遇到了bean冲突的问题,提示存在两个名称重复的bean org.springframework...
    99+
    2024-04-02
  • jquery与js函数冲突的两种解决方法
    如果您还有别的要求,想继续使用原先的$(),同时还需要与别的类库不冲突的话,还有两种解决方法 其一: 复制代码 代码如下: jQuery.noConflict(); jQuery(f...
    99+
    2022-11-15
    jquery js函数 冲突
  • git解决冲突的方法
    这篇文章将为大家详细讲解有关git解决冲突的方法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。git解决冲突的方法:首先在项目目录上点击右键,点击双向红箭头的位置;然后选择需要修改冲突的文件,选择merg...
    99+
    2023-06-06
  • IDEA遇到 git pull 冲突的几种解决方法
    1 忽略本地修改,强制拉取远程到本地 主要是项目中的文档目录,看的时候可能多了些标注,现在远程文档更新,本地的版本已无用,可以强拉 git fetch --allgit reset --hard origin/devgit pull 关于c...
    99+
    2023-08-30
    intellij-idea git
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作