一个自定义的bottomsheet

Android 自带的bottomsheet满足不了项目中的需求,所以自己写了一个,其实就是一个自定义的RelativeLayout

主要的功能有:

  • 支持同一个界面插入多个弹窗
  • 可以设置弹窗的默认高度和某一个弹窗的高度
  • 可以设置遮罩效果的透明度跟颜色
  • 设置弹窗的弹出时间和插值器
  • 点击弹窗遮盖层自动收起弹窗

不足的地方:

  • 不能拉住弹窗边缘改变弹窗高度
  • 每次设置弹窗时要同时设置高度,否则使用默认高度,而不能自动读取xml布局中的高度

调用方法:

1.首先要在xml布局的根布局中使用CustomBottomSheetLayout

<?xml version="1.0" encoding="utf-8"?>
<com.example.shui.custombottomsheet.CustomBottomSheetLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/bottomSheetLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.shui.custombottomsheet.MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="pushView" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn2"
        android:text="pushView2"
        android:layout_below ="@+id/btn"
        android:layout_centerHorizontal="true"/>


</com.example.shui.custombottomsheet.CustomBottomSheetLayout>

2.获取CustomBottomSheetLayout实例,同时进行默认设置

CustomBottomSheetLayout bsLayout= (CustomBottomSheetLayout) findViewById(R.id.bottomSheetLayout);
bsLayout.setPopBottomSheetCustomHeight(600);  //设置弹窗默认高度
bsLayout.setSuperstratumAlpha((float) 0.5);  //设置遮盖层透明度
bsLayout.setSuperStratumColor(Color.BLUE);   //设置遮盖层颜色
bsLayout.setBottomSheetInterpolator(new AccelerateInterpolator());   //设置弹窗插值器
bsLayout.setShowBottomSheetTime(800);    //设置弹窗时间

3.添加弹窗到CustomBottomSheetLayout 中,同时返回来弹窗的view实例,可以使用该view实例进行界面交互

//第一个参数,view的id
//第二个参数,hight为0是默认的弹窗高度高度
//第三个参数,用于记录弹窗时弹出跟flag所匹配的view 
View pushView = bsLayout.createBottomSheetLayout(R.layout.bottomsheet1, 0, 0);
Button bsBtn1 = (Button) pushView.findViewById(R.id.bs_btn1);
bsBtn1.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
            Toast.makeText(MainActivity.this, "pushView", Toast.LENGTH_SHORT).show();
       }
});

4.弹出窗口

//参数是弹窗的标志位,在createBottomSheetLayout方法中设置的
bsLayout.popBottomSheet(0);

5.收起窗口

bsLayout.hideBottomSheet();

6.需要支持弹出不同弹窗时,只需要多次调用createBottomSheetLayout(int BottomSheetLayoutId, int hight, int flag)方法就可以了,不过需要注意是该方法的第三个参数需要每次都传入不同的flag作为区分弹窗的标志位,否则报错。最后想显示那个弹窗时调用方法popBottomSheet(int flag)就可以了,参数是之前每个弹窗的标志位。


代码核心

1.生成view同时添加到根布局中且隐藏

/**
     * 创建bottomsheet
     *
     * @param BottomSheetLayoutId    view的id
     * @param hight     设置弹窗的高度,hight为0是默认的弹窗高度高度
     * @param flag      view的标记,用于记录弹窗时弹出跟flag所匹配的view
     * @return
     */
    public View createBottomSheetLayout(int BottomSheetLayoutId, int hight, int flag) {
        for (int key:bottomSheetLayoutMap.keySet()){//检查view的flag是否已经被设置,如果被设置抛出异常
            if (key==flag)throw new RuntimeException("the BottomSheetLayout flag is in use,so use other flag!");
        }
        View layout = View.inflate(mContext.getApplicationContext(), BottomSheetLayoutId, null);
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, hight == 0 ? popBottomSheetCustomHeight : hight);
        lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
        layout.setLayoutParams(lp);
        layout.setTag(flag);
        layout.setVisibility(GONE);//设置view为不可见
        layout.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //设置空的点击事件,拦截事件,防止点击事件传到遮盖层superstratum;从而点击弹窗空白部分时不会收起弹窗
            }
        });
        bottomSheetLayoutMap.put(flag,layout);//把view添加到map中,当弹窗时弹出key为flag的弹窗
        addView(layout);//添加view到当前根布局中
        return layout;
    }

2.弹出窗口与收起窗口核心是遮盖效果与弹窗动画同时执行

/**
     * 弹出bottomsheet
     *
     * @param flag   view的标志位
     * @param height bottomsheet内容物的高度  0为view自身高度
     * @param time   弹出所需时间  0为默认的时间
     * @param value  插值器
     */
    public void popBottomSheet(int flag, int height, int time, TimeInterpolator value) {
        final View pushView = getBottomSheet(flag);
        if (pushView == null) {
            Log.e("log","根据flag没有找到view,或者view丢失");
            return;
        }
        if (height != 0) {//高度不等于0时,重新设置弹窗的LayoutParams值,
            height = setBSLViewHight(pushView, height);
        } else {
            height = getViewHeight(pushView);
        }
        if (height <= 0) {
            Log.e("log","弹出高度小于等于0,弹窗失败");
            return;
        }
        AnimatorListenerAdapter adapter = new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                //动画开始时弹窗和遮盖层均可点击和显示
                pushView.setVisibility(VISIBLE);
                superStratum.setVisibility(VISIBLE);
                pushView.setClickable(true);
                superStratum.setClickable(true);
            }
        };
        //标志弹出的是哪个窗口,收起窗口时使用
        popBottomSheetFlag = flag;
        startAnimator(pushView, time, value, adapter, height, 0, 0, superstratumAlpha);
    }


  /**
     * 运行动画
     *
     * @param pushView     弹出的view
     * @param time         动画时间
     * @param value        插值器
     * @param adapter      动画执行状态监听器
     * @param BSStartHight bottomsheet动画开始高度
     * @param BSEndHight   bottomsheet动画结束高度
     * @param SSStartAlpha superStratum动画开始透明度
     * @param SSEndAlpha   superStratum动画结束透明度
     */
    private void startAnimator(View pushView, int time, TimeInterpolator value, AnimatorListenerAdapter adapter, int BSStartHight, int BSEndHight, float SSStartAlpha, float SSEndAlpha) {

        ObjectAnimator animator = ObjectAnimator.ofFloat(pushView, "translationY", BSStartHight, BSEndHight);
        animator.addListener(adapter);
        ObjectAnimator superstratumAnimator = ObjectAnimator.ofFloat(superStratum, "alpha", SSStartAlpha, SSEndAlpha);
        AnimatorSet set =new AnimatorSet();
        set.setDuration(time == 0 ? showBottomSheetTime : time);
        set.setInterpolator(value != null ? value : bottomSheetInterpolator);
        set.playTogether(animator,superstratumAnimator);
        set.start();

    }

/**
     * 收起bottomsheet
     *
     * @param time  动画时间
     * @param value 动画的插值器
     */
    public void hideBottomSheet(int time, TimeInterpolator value) {
        final View pushView = getBottomSheet(popBottomSheetFlag);
        popBottomSheetFlag = -1;
        if (pushView == null) {
            Log.e("log","弹出层丢失,收起bottomsheet失败");
            return;
        }
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) pushView.getLayoutParams();
        AnimatorListenerAdapter adapter = new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                //动画开始时设置不能点击
                pushView.setClickable(false);
                superStratum.setClickable(false);
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                //动画结束时设置为不可见
                pushView.setVisibility(GONE);
                superStratum.setVisibility(GONE);
            }
        };
        startAnimator(pushView, time, value, adapter, 0, layoutParams.height, superstratumAlpha, 0);
    }

下载demo地址为:
http://download.csdn.net/detail/u011959456/9843957

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter提供了一个名为`BottomSheet`的小部件,用于在页面底部显示一个可滚动的内容面板。以下是一个简单的示例,演示如何使用`BottomSheet`小部件: ```dart void _showBottomSheet(BuildContext context) { showModalBottomSheet( context: context, builder: (BuildContext context) { return Container( height: 200, color: Colors.grey[200], child: Center( child: Text( 'This is a bottom sheet', style: TextStyle(fontSize: 24), ), ), ); }, ); } ``` 在上面的示例中,我们使用`showModalBottomSheet`方法显示一个带有文本的容器。您可以根据需要自定义容器的内容和样式。 您还可以使用`BottomSheet`小部件自定义底部面板并将其附加到页面的底部。以下是一个示例: ```dart void _showBottomSheet(BuildContext context) { showModalBottomSheet( context: context, builder: (BuildContext context) { return BottomSheet( onClosing: () {}, builder: (BuildContext context) { return Container( height: 200, color: Colors.grey[200], child: Center( child: Text( 'This is a custom bottom sheet', style: TextStyle(fontSize: 24), ), ), ); }, ); }, ); } ``` 在上面的示例中,我们使用`BottomSheet`小部件创建一个带有文本的容器,并将其附加到页面的底部。`onClosing`回调可用于在用户关闭底部面板时执行操作。您可以根据需要自定义容器的内容和样式。 需要注意的是,`showModalBottomSheet`方法返回一个`Future`对象,该对象在底部面板被关闭时解析。可以使用`Navigator.pop`方法从底部面板中返回数据。例如: ```dart void _showBottomSheet(BuildContext context) async { var result = await showModalBottomSheet( context: context, builder: (BuildContext context) { return Container( height: 200, color: Colors.grey[200], child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Select an option', style: TextStyle(fontSize: 24), ), SizedBox(height: 16), ElevatedButton( onPressed: () => Navigator.of(context).pop('Option 1'), child: Text('Option 1'), ), ElevatedButton( onPressed: () => Navigator.of(context).pop('Option 2'), child: Text('Option 2'), ), ], ), ), ); }, ); print(result); } ``` 在上面的示例中,我们在底部面板中显示两个按钮,并使用`Navigator.pop`方法将选定的选项返回给调用方。返回的选项将解析为`result`变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值