Android——滑动事件冲突解决
android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件。
android系统中的每个View的子类都具有下面三个与TouchEvent处理密切相关的方法:
(1)public boolean dispatchTouchEvent(MotionEvent ev)这个方法用来分发TouchEvent
(2)public boolean onInterceptTouchEvent(MotionEvent ev)这个方法用来拦截TouchEvent
(3)public boolean onTouchEvent(MotionEvent ev)这个方法用来处理TouchEvent
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层View的dispatchTouchEvent,然后由dispatchTouchEvent方法进行分发。
如果dispatchTouchEvent返回true,则交给这个view的onTouchEvent处理;如果返回false,则交给这个view的interceptTouchEvent方法来决定是否要拦截这个事件。
如果interceptTouchEvent返回true,也就是拦截了,则交给它的onTouchEvent来处理,如果interceptTouchEvent返回false,则传递给子view,由子view的dispatchTouchEvent再开始这个事件分发。
如果事件传递到某一层的子view的onTouchEvent上了,这个方法返回了false,则表明该onTouchEvent未处理完毕,那么这个事件会从这个view往上传递,都是onTouchEvent来接收。而如果传递到最上面的onTouchEvent也返回false的话,这个事件就好消失,而且接收不到下一次事件。
如果onTouchEvent返回true,则事件处理完毕,不会传递。
让子view先处理的方法是重写父view的onInterceptTouchEvent事件并返回false
public boolean onInterceptTouchEvent(MotionEvent ev){
return false;
}
转自:http://blog.csdn.net/spt110/article/details/7919870
转自: http://blog.csdn.net/a992036795/article/details/51735501
一、前言
Android 中解决滑动的方案有2种:外部拦截法 和内部拦截法。
滑动冲突也存在2种场景: 横竖滑动冲突、同向滑动冲突。
所以我就写了4个例子来学习如何解决滑动冲突的,这四个例子分别为: 外部拦截法解决横竖冲突、外部拦截法解决同向冲突、内部拦截法解决横竖冲突、内部拦截法解决同向冲突。
先上效果图:
二、实战
1、外部拦截法,解决横竖冲突
思路是,重写父控件的onInterceptTouchEvent方法,然后根据具体的需求,来决定父控件是否拦截事件。如果拦截返回返回true,不拦截返回false。关于为什么返回true就代表拦截事件。可以参考我的上一篇博客:http://blog.csdn.net/a992036795/article/details/51698023 。 如果父控件拦截了事件,则在父控件的onTouchEvent进行相应的事件处理。
我的这个例子,是一个横向滑动的ViewGroup里面包含了3个竖向滑动的ListView。下面我附上代码:
HorizontalEx.java
/**
* Created by blueberry on 2016/6/20.
*
* 解决交错的滑动冲突
*
* 外部拦截法
*/
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事件,则子类不会拿到这个事件序列*/
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:
/*因为这里父控件拿不到Down事件,所以使用一个布尔值,
当事件第一次来到父控件时,对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();
}
}
调用代码:
@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()) {
/*如果拦截了Down事件,则子类不会拿到这个事件序列*/
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事件无效。
可以参考一下源码:
ViewGroup#dispatchTouchEvent
// 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
/**
* Created by blueberry on 2016/6/20.
* <p/>
* 内部拦截
* 和 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
/**
* Created by blueberry on 2016/6/20.
* 内部拦截事件
*/
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();
* 来决定父控件是否对事件进行拦截
* @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);
}
}
调用代码:
@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; /**
* Created by blueberry on 2016/6/21.
*
*外部拦截(同向)
*
*/
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);
/*设置 实际高度为 1/4 ,但内容区域只有 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);
} /**
* 设置尾布局
*/
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();
} /**
* 显示 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完成之后调用
*/
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; /**
* Created by blueberry on 2016/6/21.
*
* RefreshLayoutBase 的一个实现类
*/
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) { /*这里存在一个bug: 当listView滑动到底部的时候,如果下拉也会出现footer
* 这是因为,暂时还没有想到如何判断是下拉还是上拉。
* 如果要解决此问题,我觉得应该重写listView 的onTouchEvent来判断手势方向
* 次模块主要解决竖向滑动冲突,故现将此问题放下。
* */
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; /**
* Created by blueberry on 2016/6/22.
* 结合内部类 ListVieEx
* 内部拦截法,同向
*/
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); } /**
* 不拦截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();
* 来决定父控件是否对事件进行拦截
* @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;
}
}
}
Android——滑动事件冲突解决的更多相关文章
- Android动画及滑动事件冲突解决(转载)
原文链接:http://blog.csdn.net/singwhatiwanna/article/details/38168103 Android开发中动画和事件处理是程序员迈向高手的必经之路,也是重 ...
- SwipeRefreshLayout与ViewPager滑动事件冲突解决
问题描写叙述: 开发中发现,SwipeRefreshLayout的下拉刷新,与ViewPager开发的banner的左右滑动事件有一点冲突,导致banner的左右滑动不够顺畅. 非常easy在bann ...
- Android滑动事件冲突
首先,我们假设这样一个场景:一个ViewPager里面嵌套一个ViewPager,内部滑动方向和外部滑动方向一样时,该怎么解决这一冲突呢? 针对滑动冲突这里给出两种解决方案:外部拦截法,内部拦截法. ...
- 重写ListView解决ListView内部ViewPaper滑动事件冲突问题
非常easy 重写ListView 其它类似问题解决ScrollView嵌套ViewPager出现的滑动冲突问题 http://blog.csdn.net/zhangyiacm/article/det ...
- 李氏滑动事件冲突解决方案 之 处理子ViewGroup的超棒方案
父ViewGroup(CurView) 和 子 ViewGroup(ParentView) 滑动事件冲突解决方案 之 处理子ViewGroup的超棒方案: 子ViewGroup 以 SlipRelat ...
- Android 解决Gallery下ScrollView滑动事件冲突
在Gallery下,里面内容过长超出屏幕,这时我们可以用ScrollView来滚动,但是这样做了以后,会发现一个问题,Gallery的滑动事件和ScrollView的滑动事件起冲突,这时我们可以自定义 ...
- android 解决ListView点击与滑动事件冲突
如果你的ListView的Item有滑动功能,但又点击Item跳转到其它activity,这样若是在Adapter里面写点击事件是会导致滑动事件获取不到焦点而失效: 解决方法:不要在adapter里面 ...
- webview滑动事件 与内部html左右滑动事件冲突问题的解决办法
最近在做个混合app , 用html做页面,然后通过webview嵌套在activity中,效果是这样: 开始还是比较顺利,增加了菜单退出按钮,返回键页面回退功能,页面加载显示加载图标(在app端实现 ...
- Android ScrollView 嵌套 ListView、 ListView 嵌套ScrollView Scroll事件冲突解决办法
本人菜鸟一名,最近工作了,开始学习Android. 最近在做项目的时候,UX给了个design,大概就是下拉刷新的ListView中嵌套了ScrollView,而且还要在ScrollView中添加动画 ...
随机推荐
- android 解压缩mac zip压缩文件
之前被android unzip坑了一次,在此记录 使用java zip解压zip文件出现 java.util.zip.ZipException: unknown format 错误,zip文件在系统 ...
- [uiautomator篇][1] 官网译文
http://www.jianshu.com/p/7718860ec657 2016.07.25 20:59 字数 3675 Android UIAutomator浅谈 --------------- ...
- hdu5443 The Water Problem
The Water Problem Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Othe ...
- 【Luogu】P2473奖励关(期望DP)
题目链接 逆推期望DP.设f[i][j]为1~i-1中吃到的宝物集合为j,在i~k轮能得到的最大期望分数. 如果不吃显然f[i][j]+=f[i+1][j]/n 如果吃就是f[i][j]+=max(f ...
- 具体knn算法概念参考knn代码python实现
具体knn算法概念参考knn代码python实现上面是参考<机器学习实战>的代码,和knn的思想 # _*_ encoding=utf8 _*_ import numpy as npimp ...
- Spring和redis简单测试demo
1.1 首先创建一个Maven工程 File --> New --> Other,然后选择Maven目录下的Maven Project,如下图: 然后在弹出的面板中选择配置后,下一步即可, ...
- eclipse中maven项目交付svn忽略配置文件
eclipse与maven插件的结合为我们快速搭建开发环境提供了便捷条件,然而maven编译出来的class文件.配置文件和打包文件实际上都不需要进行版本控制,团队中每个人的开发环境可能不太一样,将. ...
- spring boot-html和templates
静态页面 spring boot项目只有src目录,没有webapp目录,会将静态访问(html/图片等)映射到其自动配置的静态目录,如下 /static /public /resources / ...
- 【CF645D】 Robot Rapping Results Report(拓扑排序,二分)
题意:有一张N点M边的有向图,求最小的K使根据前K条边就能够确定图是否有唯一的拓扑序, 若没有唯一拓扑序输出-1 思路:二分答案再拓扑排序,以入度为0的节点作为新的一层,若某一层的节点个数<&g ...
- 介绍一款移动端滚动加载的插件---dropload
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...