如何从屏幕上的任意位置拖动底页?

2024-05-01

我正在尝试实现类似 YouTube 的体验,并允许用户从屏幕中的任何位置拖动底部工作表。我尝试了很多解决方案没有任何帮助。


我终于从这个解决方案中找到了解决方案在外部区域达到阈值后允许 BottomSheet 向上滑动 https://stackoverflow.com/questions/55068560/allow-bottomsheet-to-slide-up-after-threshold-is-reached-on-an-area-outside

首先,创建自定义 CoordinatorLayout:

public class CustomCoordinatorLayout extends CoordinatorLayout {

private View proxyView;

public CustomCoordinatorLayout(@NonNull Context context) {
    super(context);
}

public CustomCoordinatorLayout(
    @NonNull Context context,
    @Nullable AttributeSet attrs
) {
    super(context, attrs);
}

public CustomCoordinatorLayout(
    @NonNull Context context,
    @Nullable AttributeSet attrs,
    int defStyleAttr
) {
    super(context, attrs, defStyleAttr);
}

@Override
public boolean isPointInChildBounds(
    @NonNull View child,
    int x,
    int y
) {

    if (super.isPointInChildBounds(child, x, y)) {
        return true;
    }

    // we want to intercept touch events if they are
    // within the proxy view bounds, for this reason
    // we instruct the coordinator layout to check
    // if this is true and let the touch delegation
    // respond to that result
    if (proxyView != null) {
        return super.isPointInChildBounds(proxyView, x, y);
    }

    return false;

}

// for this example we are only interested in intercepting
// touch events for a single view, if more are needed use
// a List<View> viewList instead and iterate in 
// isPointInChildBounds
public void setProxyView(View proxyView) {
    this.proxyView = proxyView;
}

}

然后创建 CustomBottomSheetBehavior:

    public class CustomBottomSheetBehavior<V extends View> extends 
    BottomSheetBehavior<V> {

    // we'll use the device's touch slop value to find out when a tap
    // becomes a scroll by checking how far the finger moved to be
    // considered a scroll. if the finger moves more than the touch
    // slop then it's a scroll, otherwise it is just a tap and we
    // ignore the touch events
    private int touchSlop;
    private float initialY;
    private boolean ignoreUntilClose;

    public CustomBottomSheetBehavior(
        @NonNull Context context,
        @Nullable AttributeSet attrs
    ) {
        super(context, attrs);
        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    public boolean onInterceptTouchEvent(
        @NonNull CoordinatorLayout parent,
        @NonNull V child,
        @NonNull MotionEvent event
    ) {

        // touch events are ignored if the bottom sheet is already
        // open and we save that state for further processing
        if (getState() == STATE_EXPANDED) {

            ignoreUntilClose = true;
            return super.onInterceptTouchEvent(parent, child, event);

        }

        switch (event.getAction()) {

            // this is the first event we want to begin observing
            // so we set the initial value for further processing
            // as a positive value to make things easier
            case MotionEvent.ACTION_DOWN:
                initialY = Math.abs(event.getRawY());
                return super.onInterceptTouchEvent(parent, child, event);

            // if the last bottom sheet state was not open then
            // we check if the current finger movement has exceed
            // the touch slop in which case we return true to tell
            // the system we are consuming the touch event
            // otherwise we let the default handling behavior
            // since we don't care about the direction of the
            // movement we ensure its difference is a positive
            // integer to simplify the condition check
            case MotionEvent.ACTION_MOVE:
                return !ignoreUntilClose
                    && Math.abs(initialY - Math.abs(event.getRawY())) > touchSlop
                    || super.onInterceptTouchEvent(parent, child, event);

            // once the tap or movement is completed we reset
            // the initial values to restore normal behavior
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                initialY = 0;
                ignoreUntilClose = false;
                return super.onInterceptTouchEvent(parent, child, event);

        }

        return super.onInterceptTouchEvent(parent, child, event);

    }

}`

然后更改布局以使用自定义协调器:

<?xml version="1.0" encoding="utf-8"?>
<com.example.CustomCoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:id="@+id/container"
    android:layout_height="match_parent">
    <FrameLayout
        android:id="@+id/player_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:fitsSystemWindows="true">

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/exo_player"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"
        android:fitsSystemWindows="true"
        app:controller_layout_id="@layout/playback_control_view"
        app:fastforward_increment="10000"
        app:rewind_increment="10000" />

      </FrameLayout>
    <include layout="@layout/bottom_sheet" />
</com.example.CustomCoordinatorLayout>

将相应的活动更改为:

 public class PlayerActivity extends AppCompatActivity{

    private CustomCoordinatorLayout customCoordinatorLayout;
    private FrameLayout playerContainer;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_player);

        customCoordinatorLayout = findViewById(R.id.container);
        playerContainer = findViewById(R.id.player_container);
        customCoordinatorLayout.setProxyView(playerContainer);

}}

就是这样!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从屏幕上的任意位置拖动底页? 的相关文章

随机推荐

  • 如何在 Ubuntu 9.10 上安装 R 软件包 rgl? [复制]

    这个问题在这里已经有答案了 可能的重复 安装 rgl 时出现问题 https stackoverflow com questions 7473067 problem installing rgl 我正在尝试在 Ubuntu 9 10 上安装
  • 可以在 Vue 模板中渲染 VNode 吗?

    我遇到的情况是 我有一个渲染函数将一些数据传递到作用域槽 作为此数据的一部分 我想包含一些由渲染函数构造的 VNode 这些 VNode 可以选择由作用域插槽使用 无论如何 在模板中编写作用域槽以输出收到的原始 VNode 时是否存在 Vu
  • Git checkout 分支从其他分支留下目录树

    我有一个branch2 其源是branch1 并重命名了源自branch1 的目录 当签出branch1时 重命名的目录仍然存在 但没有标记为未暂存的修改 重命名的目录当然不会作为branch1 上的提交出现 为什么会出现这种情况 上述目录
  • 如何在代码中设置 DataGridTextColumn 的绑定?

    我正在使用 CodePlex 的工具包 DataGrid 我正在代码中生成列 我怎样才能设置相当于 绑定名字 在代码中 或者 我怎样才能设置值 这就是我需要做的 不一定要绑定它 我只想要数据网格单元格中模型属性的值 DataGridText
  • IE9 SmartScreen 过滤器警告

    我的公司开发了一款 IT 基础设施管理软件 我们主要在乌克兰使用该软件 我们的客户端应用程序安装程序可以从我们的 FTP 服务器下载 每当有人从我们的 FTP 下载 Install msi 时 IE9 都会告诉用户该软件包不常被下载 因此可
  • 具体5-如何在php中通过ajax获取上传的文件

    我正在尝试通过ajax上传多个文件 但我不知道如何在PHP中获取上传的文件 我发送给他们 var attachments attachment file var post data new FormData if attachments l
  • .Net Framework 4.8 和 .Net 5 之间的垃圾收集行为差异

    为了在已经发生很多次的地方检测潜在的内存泄漏 我使用了如下所示构建的测试 主要思想是拥有一个实例 不再引用它并让垃圾收集器收集它 我不想关注这是否是一种好的技术 在我的具体情况下它做得非常出色 但我想关注以下问题 下面的代码在 Net Fr
  • size_t 总是无符号吗?

    如标题 size t 始终无符号 即size t x is x always gt 0 Yes It s usually定义如下 在 32 位系统上 typedef unsigned int size t 参考 C 标准第 18 1 节定义
  • Code First 从 int 迁移到 Guid 主键问题

    我正在尝试将代码第一个 ID 列从 int 更改为 Guid 当尝试运行迁移时 我收到以下消息 Identity column CustomFieldId must be of data type int bigint smallint t
  • 将 Flask_login 会话与 jinja2 模板一起使用

    我有简单的 jinja2 模板 带有注册 登录链接 我应该在用户登录时隐藏它们 我还使用flask login 模块来处理这个东西 问题是 我应该如何识别用户是否已登录 jinja2 模板 Flask Login 添加了current us
  • Flex DataGrid:根据另一个值更改值?

    我在 Flex 中有一个 DataGrid 其中一列是复选框 另一列是数值 单击该复选框时 数值应更改 如果未选中该复选框 则数值应更改为 0 如果选中该复选框 则应更改为预定义的最小值 这是我的代码
  • 如何对 SQL 查询进行单元测试?

    我有课DBHandler它接受一个查询 通过 SQL Server 运行它 检查错误并返回结果 我如何对这个类进行单元测试 Edit 我会尽量说得更准确 DBHandler负责将查询传递到服务器 为了测试它是否确实做到了这一点 抛出正确的异
  • 如何测试 Windows (10) 上是否安装了 Python,如果未安装则运行 exe 来安装它?

    仅当第一个命令失败时 我才需要在 Windows cmd 上运行第二个命令 在另一个场景中 我想在检查 python setup 是否安装后打开它 我用了这个命令 python version path to python install
  • 在 Mac 上查找(并杀死)锁定端口 3000 的进程 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何找到 并终止 侦听 使用我的 TCP 端口的进程 我在 macOS 上 有时 在崩溃或出现一些错误后 我的 Rails 应用程序会锁定
  • 如何使用 Google 表格中多个选项卡的值创建电子表格下拉列表

    想象一下电子表格 Google Spreadsheet 鉴于我有多个选项卡 每个选项卡都有一个名称 可能有 60 80 个选项卡 我想在首页上创建一个下拉菜单 在其中可以看到工作簿中每个选项卡的 它可能看起来像这样 Selected
  • 如何仅将列表中每个字符串的标题大写?

    整个问题 编写一个函数 该函数将字符串列表作为参数 并返回一个包含每个大写字符串作为标题的列表 也就是说 如果输入参数是 apple pie brownies chocolate dulce de leche eclairs 你的函数应该返
  • 在外部 div 悬停时将样式应用于内部 div [重复]

    这个问题在这里已经有答案了 我有一些看起来像这样的 HTML div class TheOuterClass div class TheInnerClass some text div div 使用以下 CSS TheOuterClass
  • 将鼠标悬停在 R 中的传单时弹出?

    我的传单地图看起来像这样 library sp library leaflet circleFun lt function center c 0 0 diameter 1 npoints 100 r diameter 2 tt lt seq
  • 如何以非交互方式指定“psql”的密码?

    我正在尝试使用 shell 脚本自动执行数据库创建过程 但我遇到了向其传递密码的障碍psql https en wikipedia org wiki PostgreSQL Database administration 下面是 shell
  • 如何从屏幕上的任意位置拖动底页?

    我正在尝试实现类似 YouTube 的体验 并允许用户从屏幕中的任何位置拖动底部工作表 我尝试了很多解决方案没有任何帮助 我终于从这个解决方案中找到了解决方案在外部区域达到阈值后允许 BottomSheet 向上滑动 https stack