Jacky's blog
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)
  • tutorial
  • jetpack

  • components

  • androidx

  • 动态化
  • apm

  • module

  • harmony

  • tool

  • other

    • Flutter 高频面试问答
    • 生产环境Message分发处理设计
    • 事件分发机制
    • 调研抖音对harmonyOS4的优化
    • Android 评论at功能的实现
    • 探索抖音禁止录屏
    • 对32位手机崩溃的优化记录
    • GradientDrawable
    • android window
    • color
    • webview白屏检测
    • android Resource
    • deeplink技术
    • android-xml
    • ANDROID IPC
    • BottomSheetBehavior研究与思考
    • viewPager
    • Android密钥系统
    • compiler
    • 提升UI加载速度的几点思考
    • Android零耗时首帧体验
    • jsbridge
    • retrofit动态代理设计
    • gif与属性动画的对比
  • kotlin

  • 《android》
  • other
Jacky
2025-03-06

BottomSheetBehavior研究与思考

扩展BottomSheetBehavior实现横向手势,控制Dialog的显示隐藏

  1. 重写 onInterceptTouchEvent, 在发现横划及时拦截,事件到 onTouchEvent
  2. 转换 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
上次更新: 2025/04/01, 16:51:44
ANDROID IPC
viewPager

← ANDROID IPC viewPager→

最近更新
01
npx 使用指南
10-12
02
cursor
09-28
03
inspect
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式