產品想要上面效果.android
摸索玩轉如下三個touch事件方法:markdown
onInterceptTouchEvent
複製代碼
onTouchEvent
複製代碼
dispatchTouchEvent
複製代碼
直接貼代碼吧. 反正我寫了註釋:ide
package com.test.scrolltest;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
/**
* 自由滾動的RecyclerView. 須要嵌套使用
*/
public class FreeScrollRecycleView extends RecyclerView{
/** 記錄父View的滾動狀態 */
int mFatherScrollState = -1;
/** */
private OnScrollListener mOnScrollListener;
/** 父View*/
private FreeScrollRecycleView mFatherScrollableView;
/** 子View*/
private FreeScrollRecycleView mJustStopScrollingChildView;
public FreeScrollRecycleView(@NonNull Context context) {
super(context);
}
public FreeScrollRecycleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public FreeScrollRecycleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//監聽父View的滾動
setFatherScrollableView(mFatherScrollableView);
}
protected void setFatherScrollableView(FreeScrollRecycleView fatherScrollableView) {
if(fatherScrollableView != null){
mFatherScrollableView = fatherScrollableView;
if(mOnScrollListener == null){
mOnScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
//記錄父view滾動狀態
mFatherScrollState = newState;
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
};
}
mFatherScrollableView.removeOnScrollListener(mOnScrollListener);
mFatherScrollableView.addOnScrollListener(mOnScrollListener);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(mFatherScrollableView != null){
mFatherScrollableView.removeOnScrollListener(mOnScrollListener);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
boolean ret = super.onInterceptTouchEvent(e);
/*
不攔截父View的Touch事件.
當前是子View時, 在開始滾動時, 會在super.onInterceptTouchEvent()中調阻止父View滾動,
因此在執行完onInterceptTouchEvent()後, 須要重置父View, 容許其接受事件:
*/
getParent().requestDisallowInterceptTouchEvent(false);
Log.d("FreeScrollRecycleView", getResources().getResourceName(getId())+": onInterceptTouchEvent: "+ret+" event:"+e.getAction()+"|"+e.hashCode());
return ret;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if(e.getAction()==MotionEvent.ACTION_CANCEL
&& mFatherScrollState ==SCROLL_STATE_DRAGGING){
Log.d("FreeScrollRecycleView", getResources().getResourceName(getId())+": onTouchEvent: event:"+e.getAction()+"|"+e.hashCode());
/*
當前是子View時, 若父View能夠開始滾動時, 則會向子View發送CANCEL事件, 阻止子View滾動.
可是此時並不想子View中止滾動, 因此強行設置ACTION_MOVE
*/
e.setAction(MotionEvent.ACTION_MOVE);
}
boolean ret = super.onTouchEvent(e);
/*
滾動時, 也會要求父佈局不接受事件, 因此onTouchEvent()執行完畢後, 從新容許父View接受事件:
*/
getParent().requestDisallowInterceptTouchEvent(false);
Log.d("FreeScrollRecycleView", getResources().getResourceName(getId())+": onTouchEvent: "+ret+" event:"+e.getAction()+"|"+e.hashCode()+" mfatherScrollState:"+ mFatherScrollState);
return ret;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_CANCEL
&& mFatherScrollState ==SCROLL_STATE_DRAGGING){
Log.d("FreeScrollRecycleView", getResources().getResourceName(getId())+": dispatchTouchEvent: event:"+ev.getAction()+"|"+ev.hashCode());
/*
這個CANCEL事件, 是當父View從發送過來的,
由於父View開始滾動了(father.onInterceptTouchEvent()==true),它要阻止子View滾動
但此時子View也想滾動, 因此強制設置爲 ACTION_MOVE
*/
ev.setAction(MotionEvent.ACTION_MOVE);
}
boolean ret = super.dispatchTouchEvent(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if(mFatherScrollableView != null) {
//當前是子View, 告知父View, 須要手動發送Touch事件的對象是this
mFatherScrollableView.mJustStopScrollingChildView = this;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
default:
if(mFatherScrollableView != null) {
//當前是子View, 告知父View, 清空手動發送Touch事件的對象
mFatherScrollableView.mJustStopScrollingChildView = null;
}
//當前是父View. 清空須要手動發送Touch事件的子View
mJustStopScrollingChildView = null;
}
if(mJustStopScrollingChildView != null){
//向子View分發事件
mJustStopScrollingChildView.dispatchTouchEvent(ev);
}
Log.d("FreeScrollRecycleView", getResources().getResourceName(getId())+": dispatchTouchEvent: "+ret+" event:"+ev.getAction()+"|"+ev.hashCode());
return ret;
}
}
複製代碼