使用BottomSheet和BottomSheetDialog实现嵌套滑动效果

本文介绍如何使用BottomSheet和NestedScrolling机制实现嵌套滑动效果,通过实例展示了BottomSheetBehavior的配置和BottomSheetDialog的使用,包括布局设置、事件处理及高度控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看效果:

 这就是我们要实现的嵌套滑动效果,即在同一个事件序列中,先滑动外部控件,当外部控件滑到设定的高度时,滑动内部空间。我们都知道,在传统的时间分发机制中,某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话)并且它的onInterceptTouchEvent不会再被调用。所以上面的效果用传统的事件分发是无法实现的。

这时候就要用到我们的NestedScrolling机制了,原理大概是子view拦截了事件后,但是在滑动之前,将滑动的细节信息dx,dy传递给父View,父View决定是否消耗滑动值dx,dy,然后子View再利用剩余的值来滑动。

上面这种效果我们可以使用NestedScrolling机制来实现,也可以使用相对简单的BottomSheet来实现,其实BottomSheet内部还是使用NestedScrolling机制来实现的,这篇文章我们使用BottomSheet来实现,想使用NestedScrolling机制实现的可以参考下面的文章。

NestedScrolling实现嵌套滑动:https://blog.youkuaiyun.com/XG1057415595/article/details/81155391

NestedScrolling机制推荐这篇文章:https://www.jianshu.com/p/f09762df81a5

我们开始吧!

BottomSheetBehavior

首先,BottomSheetBehavior需要作为CoordinatorLayout的子View,所以CoordinatorLayout是必不可少的。

这里先看MainActivity的布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/cl"
    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:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <Button
            android:id="@+id/button_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Show_Or_Hide"/>
        <Button
            android:id="@+id/next"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="next_page"/>
    </LinearLayout>
    
    <android.support.v4.widget.NestedScrollView
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        app:layout_behavior="@string/bottom_sheet_behavior"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"
                android:scaleType="fitXY"/>
            <ImageView
                android:layout_width="@dimen/img_size"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"
                android:scaleType="fitXY"/>
            <ImageView
                android:layout_width="@dimen/img_size"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"
                android:scaleType="fitXY"/>
            <ImageView
                android:layout_width="@dimen/img_size"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="@dimen/img_size"
                android:src="@drawable/ic_launcher_background"
                android:scaleType="fitXY"/>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

这里CoordinatorLayout有一个LinearLayout和一个NestedScrollView。LinearLayout包含两个Button;NestedScrollview是一个实现了NestedScrollingParent的可以滑动的FrameLayout,NestedScrollingParent是NestedScrolling机制的重要部分。这里不做过多赘述,可以通过上面的文章进行了解。

这里NestedScrollview加了一个app:layout_behavior属性,值为“@string/bottom_sheet_behavior",加了这个属性后,这个布局就会自动移动到页面底部的外边,所以页面上是看不到android:id="@+id/bottom_sheet"这个布局的。我们在bottom_sheet布局里面放了几个ImageView。注意:这里NestedScrollview的layout_height高度就是BottomSheet弹出来的最大高度。

接下来就是Activity中的代码了:

public class MainActivity extends AppCompatActivity {
    BottomSheetBehavior behavior;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View bottomSheet = findViewById(R.id.bottom_sheet);
        behavior = BottomSheetBehavior.from(bottomSheet);
        behavior.setPeekHeight(200);
        behavior.setHideable(true);
        behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View view, int i) {
            }
            @Override
            public void onSlide(@NonNull View view, float v) {
            }
        });

        Button button = findViewById(R.id.button_show);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(behavior.getState() == BottomSheetBehavior.STATE_EXPANDED){
                    Log.e("clidk", "onClick: collapsed");
                    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }else{
                    Log.e("clidk", "onClick: expanded");
                    behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                }
            }
        });
        Button next = findViewById(R.id.next);
        next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

在onCreate方法中通过BottomSheetBehavior.from方法获取布局的behavior,然后调用behavior.setPeekHeight(200)设置bottom_sheet状态为collapsed时出现在手机屏幕的高度,setHideable方法设置是否可以隐藏,我们还可以为behavior设置监听。接着我们设置按钮的监听,收缩和展开BottomSheet。

就是这么简单,就可以实现文章开头的效果。如果采用NestedScrolling机制实现需要自己写很多逻辑。

BottomSheetDialog

BottomSheetDialog也差不多,我们大概看一下。

先是activity_second布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/cl"
    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:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <Button
            android:id="@+id/button_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Show_Or_Hide"/>
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

只有一个按钮,根布局还是CoordinatorLayout

还有BottomSheetDialog的布局文件dialog_layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    app:layout_behavior="@string/bottom_sheet_behavior"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"
            android:scaleType="fitXY"/>
        <ImageView
            android:layout_width="@dimen/img_size"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"/>
        <ImageView
            android:layout_width="@dimen/img_size"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"/>
        <ImageView
            android:layout_width="@dimen/img_size"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"/>
        <ImageView
            android:layout_width="@dimen/img_size"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"/>
        <ImageView
            android:layout_width="@dimen/img_size"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"/>
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="@dimen/img_size"
            android:src="@drawable/ic_launcher_background"
            android:scaleType="fitXY"/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

同样采用NestedScrollview,内部还是几个ImageView。
注意:这里NestedScrollview的layout_height高度无法指定BottomSheetDialog弹出来的最大高度。BottomSheetDialog的高度默认是内部内容的高度,内容过多会覆盖整个屏幕。后面会讲解怎么设置高度!

接下来看SecondActivity:

public class SecondActivity extends AppCompatActivity {
    BottomSheetDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        View view = getLayoutInflater().inflate(R.layout.dialog_layout,null);
        dialog = new BottomSheetDialog(this);
        dialog.setContentView(view);
        View parent = (View) view.getParent();
        BottomSheetBehavior behavior = BottomSheetBehavior.from(parent);
        behavior.setPeekHeight(300);//首次弹出的高度
        dialog.show();
        dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,600);
        dialog.getWindow().setGravity(Gravity.BOTTOM);


        Button button = findViewById(R.id.button_show);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(dialog.isShowing()){
                    Log.e("clidk", "onClick: collapsed");
                    dialog.dismiss();
                }else{
                    Log.e("clidk", "onClick: expanded");
                    dialog.show();
                }
            }
        });
    }
}

这里通过LayoutInflater获取dialog_layout的布局view,然后创建BottomSheetDialog对象dialog;setContentView设置dialog的布局;

View parent = (View) view.getParent();
BottomSheetBehavior behavior = BottomSheetBehavior.from(parent);
behavior.setPeekHeight(300);//首次弹出的高度

上面三行设置首次弹出的高度(这里和前面BottomSheetBehavior有点不同);

dialog.show();
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,600);
dialog.getWindow().setGravity(Gravity.BOTTOM);

上面三行设置弹出的最大高度,且需要在dialog.show()方法之后调用,否则无效果。

 

以上就是BottomSheet的实现嵌套滑动的过程啦!喜欢点个赞吧~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值