BottomSheetBehavior研究与思考
扩展BottomSheetBehavior实现横向手势,控制Dialog的显示隐藏
- 重写 onInterceptTouchEvent, 在发现横划及时拦截,事件到 onTouchEvent
- 转换 move 事件下的 MotionEvent,使横划转成竖滑
- BottomSheetBehavior 的滑动View是通过 ViewDragHelper 实现。
viewDragHelper.processTouchEvent(event) - viewDragHelper 通过 dragTo 实现View的容器的滑动, 这个方法是 private 方法,不便于重写。
- 综上: 两点,转换 MotionEvent 更加方便
package android.support.design.widget;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
public class SwipeDismissBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
private final AsyncPLogTag tag = AsyncPLogTag.build("SwipeDismissBottomSheetBehavior", this.hashCode() + "");
public SwipeDismissBottomSheetBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
private float lastX;
private float lastY;
private float mInitialMotionX;
private float mInitialMotionY;
@Nullable
private Boolean isHSwipeMode;
private final float flexRatio = 2;
private int lastPrintInterceptTouchEventCode = -100;
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
int action = event.getActionMasked();
if (lastPrintInterceptTouchEventCode != action) {
AsyncPLog.i(tag, "onInterceptTouchEvent, actionCode=%d", action);
lastPrintInterceptTouchEventCode = action;
}
if (action == MotionEvent.ACTION_MOVE) {
if (isHSwipeMode != null) {
if (isHSwipeMode) {
return true;
} else {
return super.onInterceptTouchEvent(parent, child, event);
}
} else {
int dx = (int) (event.getX() - lastX);
int dy = (int) (event.getY() - lastY);
if (Math.abs(dx) > Math.abs(dy)) {
isHSwipeMode = true;
AsyncPLog.i(tag, "onInterceptTouchEvent move, dx=%d, dy=%d, isHSwipeMode=true", dx, dy);
return true;
} else if (Math.abs(dx) < Math.abs(dy)) {
isHSwipeMode = false;
AsyncPLog.i(tag, "onInterceptTouchEvent move, set isHSwipeMode false");
}
}
} else if (action == MotionEvent.ACTION_DOWN) {
isHSwipeMode = null;
setInitialMotion(event.getX(), event.getY());
}
return super.onInterceptTouchEvent(parent, child, event);
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
AsyncPLog.i(tag, "onTouchEvent, child=%s, event_code=%d", child.getClass().getSimpleName(), event.getAction());
MotionEvent tempEvent = event;
MotionEvent obtain = null;
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
if (isHSwipeMode == null || !isHSwipeMode) {
return super.onTouchEvent(parent, child, tempEvent);
} else {
int dx = (int) (event.getX() - lastX);
int dy = (int) (event.getY() - lastY);
float newX = event.getX();
float newY = event.getY();
boolean isNeedToConvertEvent = Math.abs(dx) >= Math.abs(dy);
if (isNeedToConvertEvent) {
obtain = MotionEvent.obtain(event);
tempEvent = obtain;
newX = mInitialMotionX + (event.getY() - mInitialMotionY) * flexRatio;
newY = mInitialMotionY + (event.getX() - mInitialMotionX) * flexRatio;
obtain.setLocation(newX, newY);
}
AsyncPLog.i(tag, "onTouchEvent move, dx=%d, dy=%d, [%.1f, %.1f] [%.1f, %.1f], isNeedToConvertEvent:%s", dx, dy, event.getX(), event.getY(), newX, newY, isNeedToConvertEvent);
setMoveXY(event.getX(), event.getY());
if (!isNeedToConvertEvent) {
return true;
}
}
}
boolean result = super.onTouchEvent(parent, child, tempEvent);
if (obtain != null) {
obtain.recycle();
}
return result;
}
private void setMoveXY(float x, float y) {
lastX = x;
lastY = y;
}
private void setInitialMotion(float x, float y) {
AsyncPLog.i(tag, "setInitialMotion, x=%.1f, y=%.1f", x, y);
this.mInitialMotionX = this.lastX = x;
this.mInitialMotionY = this.lastY = y;
}
@Nullable
public static <V extends View> SwipeDismissBottomSheetBehavior<V> from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
AsyncPLog.w("SwipeDismissBottomSheetBehavior", "The view is not a child of CoordinatorLayout");
return null;
// throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
} else {
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams)params).getBehavior();
if (!(behavior instanceof SwipeDismissBottomSheetBehavior)) {
AsyncPLog.w("SwipeDismissBottomSheetBehavior", "The view is not associated with BottomSheetBehavior");
// throw new IllegalArgumentException("The view is not associated with BottomSheetBehavior");
return null;
} else {
return (SwipeDismissBottomSheetBehavior)behavior;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
上次更新: 2025/04/01, 16:51:44