之前一片文章 初略的讲了一些关于事件传递的基本内容,现在这片博客,
主要是具体去运用事件传递拦截的相关内容,
ok 具体要实现的目标就是,
view 能够正常的吃掉点击事件, 但是如果是滑动事件时, 则是父View 来处理来滑动另一个View
基本原理就是
在View group中得onInterceptTouchEvent 方法中判断是否是滑动,
如果是滑动,那么就返回true 自身去消费滑动事件,
还需要注意的是, 在处理滑动事件的时候需要注意 两个手指滑动的情况
即 另一个手指按下也能够继续滑动,
不多说了上代码:
/**
* 只处理 纵向滑动事件, 其他事件不做 处理
*/
public class CustomScrollViewGroup extends RelativeLayout {
public static final String TAG = "CustomScrollViewGroup";
public CustomScrollViewGroup(Context context) {
super(context);
init();
}
public CustomScrollViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomScrollViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
private OnScrollListener mOnScrollListener;
private int mTouchSlop;
protected VelocityTracker mVelocityTracker;
private int mMinimumVelocity;
private int mMaximumVelocity;
/**
* Position of the last motion event.
*/
private float mLastMotionX;
private float mLastMotionY;
private float mInitialMotionY, mInitialMotionX;
protected int mActivePointerId = INVALID_POINTER;
private static final int INVALID_POINTER = -1;
private boolean mIsUnableToDrag = false;
private int mFlingDistance;
private boolean mIsBeingDragged = false;
boolean mScrollToEnd = false;//标记View是否已经完全消失,OvershootInterpolator回弹时间忽略
private boolean mQuickReturn = false;
boolean isTouchOnRecycleView = false;
private void init() {
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
final float density = getContext().getResources().getDisplayMetrics().density;
mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
LogUtil.d(TAG, "onInterceptTouchEvent ev = " + ev.getAction());
final int action = ev.getAction();
if (action == MotionEvent.ACTION_CANCEL
|| action == MotionEvent.ACTION_UP
|| (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {
endDrag();
return false;
}
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Remember where the motion event started
int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
mInitialMotionY = mLastMotionY = MotionEventCompat.getY(ev, index);
mIsBeingDragged = false;
mIsUnableToDrag = false;
break;
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER) {
break;
}
final float currentY = MotionEventCompat.getY(ev, activePointerIndex);
final float deltaY = mLastMotionY - currentY;
determineDrag(ev);
break;
}
if (!mIsBeingDragged) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
}
return mIsBeingDragged || mQuickReturn;
}
private int getPointerIndex(MotionEvent ev, int id) {
int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);
if (activePointerIndex == -1)
mActivePointerId = INVALID_POINTER;
return activePointerIndex;
}
private int mScrollY = 0;
@Override
public boolean onTouchEvent(MotionEvent ev) {
LogUtil.d(TAG, "onTouchEvent ev = " + ev.getAction());
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Remember where the motion event started
int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
mLastMotionY = mInitialMotionY = ev.getY();
mLastMotionX = mInitialMotionX = ev.getX();
break;
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
determineDrag(ev);
if (mIsUnableToDrag)
return false;
}
if (mIsBeingDragged) {
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
final float y = MotionEventCompat.getY(ev, activePointerIndex);
final float deltaY = mLastMotionY - y;
mLastMotionY = y;
float oldScrollY = mScrollY;
float scrollY = oldScrollY + deltaY;
mLastMotionX += scrollY - (int) scrollY;
mScrollY = (int) scrollY;
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(0, deltaY);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker, mActivePointerId);
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId != INVALID_POINTER) {
final float y = MotionEventCompat.getY(ev, activePointerIndex);
final int totalDelta = (int) (y - mInitialMotionY);// doww -> up detal
if (Math.abs(totalDelta) > mFlingDistance && Math.abs(initialVelocity) > mMinimumVelocity) {
if (initialVelocity > 0 && totalDelta > 0) {
// 向下滑
if (mOnScrollListener != null) {
mOnScrollListener.onFling(false);
}
} else if (initialVelocity < 0 && totalDelta < 0) {
// 向上滑
if (mOnScrollListener != null) {
mOnScrollListener.onFling(true);
}
} else {
if (mOnScrollListener != null) {
mOnScrollListener.onScrollOver();
}
}
} else {
if (mOnScrollListener != null) {
mOnScrollListener.onScrollOver();
}
// 不是 fling 最后是 划上 还是划下 外面判断
}
}
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int indexx = MotionEventCompat.getActionIndex(ev);
mLastMotionY = MotionEventCompat.getY(ev, indexx);
mLastMotionX = MotionEventCompat.getX(ev, indexx);
mActivePointerId = MotionEventCompat.getPointerId(ev, indexx);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
int pointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionY = MotionEventCompat.getY(ev, pointerIndex);
mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);
break;
}
return true;
}
private void determineDrag(MotionEvent ev) {
final int activePointerId = mActivePointerId;
final int pointerIndex = getPointerIndex(ev, activePointerId);
if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER)
return;
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float dy = y - mLastMotionY;
final float yDiff = Math.abs(dy);
if (yDiff > mTouchSlop && yDiff > xDiff) {
startDrag();
mLastMotionX = x;
mLastMotionY = y;
} else if (xDiff > mTouchSlop) {
mIsUnableToDrag = true;
}
}
// 开始滑动
private void startDrag() {
mIsBeingDragged = true;
mQuickReturn = false;
mScrollToEnd = false;
}
private void endDrag() {
mQuickReturn = false;
mIsBeingDragged = false;
mIsUnableToDrag = false;
mActivePointerId = INVALID_POINTER;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
mOnScrollListener = onScrollListener;
}
public interface OnScrollListener {
void onScroll(float distanceX, float distanceY);
void onFling(boolean isFlingUp);
void onScrollOver();
}
}
上面的是最主要的ViewGroup
下面在来看看 activity 中得代码
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
Button mButton;
View mView;
CustomScrollViewGroup mCustomScrollViewGroup;
int maxTranY;
float currentTransY = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.btn);
mView = findViewById(R.id.scrolledView);
mCustomScrollViewGroup = (CustomScrollViewGroup) findViewById(R.id.customscrollviewgroup);
maxTranY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, getResources().getDisplayMetrics());
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "click btn", Toast.LENGTH_SHORT).show();
}
});
mCustomScrollViewGroup.setOnScrollListener(new CustomScrollViewGroup.OnScrollListener() {
/**
* 滑动 的箭头
* @param distanceX 向左滑动 为正
* @param distanceY 向上滑动 为正
*/
@Override
public void onScroll(float distanceX, float distanceY) {
if (isAnimating) {
return;
}
currentTransY = currentTransY - distanceY;
if (currentTransY < -maxTranY) {
currentTransY = -maxTranY;
}
if (currentTransY > 0) {
currentTransY = 0;
}
mView.setTranslationY(currentTransY);
LogUtil.d(TAG, "onScroll distanceX = " + distanceX + " , distanceY = " + distanceY + ", currentTransY = " + currentTransY);
}
/**
* 快速滑动的回调
* @param isFlingUp 是否是向上快速滑动
*/
@Override
public void onFling(boolean isFlingUp) {
if (isAnimating) {
return;
}
LogUtil.d(TAG, "onFling isFlingUp = " + isFlingUp);
if (isFlingUp) {
animatorScrollUp();
} else {
animatorScrollDowm();
}
}
/**
* 滑动结束的监听
*/
@Override
public void onScrollOver() {
if (isAnimating) {
return;
}
LogUtil.d(TAG, "onScrollOver");
if (currentTransY < -maxTranY / 2) {// 向上滑超过了 1/2
animatorScrollUp();
} else {
animatorScrollDowm();
}
}
});
}
boolean isAnimating = false;
private void animatorScrollUp() {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mView, "TranslationY", mView.getTranslationY(), -maxTranY);
objectAnimator.setDuration(300);
isAnimating = true;
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
currentTransY = -maxTranY;
isAnimating = false;
}
});
objectAnimator.start();
}
private void animatorScrollDowm() {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mView, "TranslationY", mView.getTranslationY(), 0);
objectAnimator.setDuration(300);
isAnimating = true;
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
currentTransY = 0;
isAnimating = false;
}
});
objectAnimator.start();
}
}