Android 基于BottomSheetDialog 自定义底部弹出Dialog


前言

BottomSheetDialog 是 Android 6.0 推出的新控件,即

Base class for Dialogs styled as a bottom sheet

基于Dialog样式的一个底部对话框


但原生控件可扩展性往往满足不了日常开发,于是自己基于它自定义一个Dialog,使用方便,可扩展性高,源码在文章最底部,不妨 Star 或 Fork


一、效果(图一 基础样式  图二 列表样式)

         

                                                          

                                                    


二、使用步骤


1.引入库

implementation 'androidx.recyclerview:recyclerview:1.1.0'


2.代码

 基础样式布局 bottom_dialog_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/top_corners_shape"
    android:gravity="center_vertical"
    android:orientation="vertical"
    android:paddingHorizontal="30dp"
    android:paddingVertical="25dp">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#2B2B2B"
        android:textSize="18dp"
        android:textStyle="bold"
        android:visibility="gone" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:textColor="#2B2B2B"
        android:textSize="24sp"
        android:textStyle="bold"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="25dp"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_leftBtn"
            android:layout_width="0dp"
            android:layout_height="45dp"
            android:layout_weight="1"
            android:background="@drawable/dialog_left_btn_shape"
            android:gravity="center"
            android:textColor="#2B2B2B"
            android:textSize="15dp"
            android:textStyle="bold"
            android:visibility="gone" />

        <TextView
            android:id="@+id/tv_rightBtn"
            android:layout_width="0dp"
            android:layout_height="45dp"
            android:layout_marginStart="14dp"
            android:layout_weight="1"
            android:background="@drawable/dialog_right_btn_shape"
            android:gravity="center"
            android:textColor="#2984FF"
            android:textSize="15sp"
            android:textStyle="bold"
            android:visibility="gone" />

    </LinearLayout>

</LinearLayout>

列表样式布局 btn_bottom_dialog_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/top_corners_shape"
    android:orientation="vertical"
    android:paddingHorizontal="15dp">

    <TextView
        android:id="@+id/tv_bottom_dialog_title"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:gravity="center"
        android:textColor="#2B2B2B"
        android:textSize="15sp"
        android:textStyle="bold"
        android:visibility="gone" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="#0d000000" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rcv_btn_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

选项列表 btns_item_layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="45dp">

    <TextView
        android:id="@+id/tv_dialog_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="#2B2B2B"
        android:textSize="15dp"
        android:textStyle="bold" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_alignParentBottom="true"
        android:background="#0d000000" />

</RelativeLayout>

列表样式实体类 DialogBtnData

public class DialogBtnData {

    private int id;
    private String title;

    public DialogBtnData() {
    }

    public DialogBtnData(int id, String title) {
        this.id = id;
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

列表适配器 BtnsBottomDialogAdapter

public class BtnsBottomDialogAdapter extends RecyclerView.Adapter<BtnsBottomDialogAdapter.VH> {
    private static final String TAG = "BtnsBottomDialogAdapter";

    private Context context;
    private List<DialogBtnData> btnList;
    private OnItemClickListener itemClickListener;

    public BtnsBottomDialogAdapter(Context context, List<DialogBtnData> btnList) {
        this.context = context;
        this.btnList = btnList;
    }

    @NonNull
    @Override
    public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.btns_item_layout, parent, false);
        return new VH(v);
    }

    @Override
    public void onBindViewHolder(@NonNull VH holder, int position) {
        DialogBtnData curData = btnList.get(position);
        if (curData == null || TextUtils.isEmpty(curData.getTitle())) {
            return;
        }
        holder.itemView.setOnClickListener(v -> {
            //item 点击事件
            Log.d(TAG, "onClick position:" + position + ", data:" + curData);
            if (itemClickListener != null) {
                itemClickListener.onItemClick(v, position);
            }
        });
        holder.titleTv.setText(curData.getTitle());
    }

    @Override
    public int getItemCount() {
        return btnList.size();
    }

    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }

    public static class VH extends RecyclerView.ViewHolder {

        private TextView titleTv;

        public VH(@NonNull View view) {
            super(view);
            titleTv = view.findViewById(R.id.tv_dialog_btn);
        }
    }

    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }
}

自定义 BottomDialog (此处有一个坑,当我在平板上运用的时候 横屏下 Dialog 会显示不全。需要手动上拉下,很是头痛 ,但问题不大,最后还是完美解决,代码中有记录)

/**
 * 自定义底部弹出dialog
 */
public class BottomDialog implements View.OnClickListener, BtnsBottomDialogAdapter.OnItemClickListener {
    private static final String TAG = "BottomDialog";

    private Context context;
    private BottomSheetDialog dialog;
    private TextView titleTv;
    private TextView contentTv;
    private TextView leftBtnTv;
    private TextView rightBtnTv;

    private OnBtnClickListener listener;
    private RecyclerView recyclerView;
    private BtnsBottomDialogAdapter adapter;
    private BtnsBottomDialogAdapter.OnItemClickListener listener1;

    /**
     * 基本样式
     *
     * @param context
     */
    public BottomDialog(Context context) {
        this.context = context;
        dialog = new BottomSheetDialog(context, R.style.BottomSheetEdit);
        View view = LayoutInflater.from(context).inflate(R.layout.bottom_dialog_layout, null);
        dialog.setContentView(view);
        // 将BottomSheetDialog背景设为透明
        FrameLayout bottom = dialog.findViewById(R.id.design_bottom_sheet);
        if (bottom != null) {
            bottom.setBackgroundResource(android.R.color.transparent);
            //解决BottomSheetDialog底部弹出框 横屏显示不全的问题
            BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottom);
            /**
             * 控制展开跟收缩
             *
             * STATE_EXPANDED 展开状态
             * STATE_COLLAPSED 收缩状态
             * STATE_DRAGGING 正在拖动状态
             * STATE_HIDDEN 隐藏状态
             */
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }

        titleTv = view.findViewById(R.id.tv_title);
        contentTv = view.findViewById(R.id.tv_content);
        leftBtnTv = view.findViewById(R.id.tv_leftBtn);
        rightBtnTv = view.findViewById(R.id.tv_rightBtn);
        leftBtnTv.setOnClickListener(this);
        rightBtnTv.setOnClickListener(this);
    }

    /**
     * 列表样式
     *
     * @param context
     * @param btnList
     */
    public BottomDialog(Context context, List<DialogBtnData> btnList) {
        dialog = new BottomSheetDialog(context, R.style.BottomSheetEdit);
        View view = LayoutInflater.from(context).inflate(R.layout.btn_bottom_dialog_layout, null);
        dialog.setContentView(view);
        // 将BottomSheetDialog背景设为透明
        FrameLayout bottom = dialog.findViewById(R.id.design_bottom_sheet);
        if (bottom != null) {
            bottom.setBackgroundResource(android.R.color.transparent);
            //解决BottomSheetDialog底部弹出框 横屏显示不全的问题
            BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottom);
            /**
             * 控制展开跟收缩
             *
             * STATE_EXPANDED 展开状态
             * STATE_COLLAPSED 收缩状态
             * STATE_DRAGGING 正在拖动状态
             * STATE_HIDDEN 隐藏状态
             */
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }


        recyclerView = view.findViewById(R.id.rcv_btn_list);
        recyclerView.setLayoutManager(new LinearLayoutManager(context));
        adapter = new BtnsBottomDialogAdapter(context, btnList);
        adapter.setOnItemClickListener(this);
        recyclerView.setAdapter(adapter);
        titleTv = view.findViewById(R.id.tv_bottom_dialog_title);
    }

    public void show() {
        dialog.show();
    }

    public void dismiss() {
        dialog.dismiss();
    }

    public BottomDialog setDismissListener(DialogInterface.OnDismissListener dismissListener) {
        dialog.setOnDismissListener(dismissListener);
        return this;
    }

    public BottomDialog setTitle(String title) {
        if (!TextUtils.isEmpty(title)) {
            titleTv.setVisibility(View.VISIBLE);
            titleTv.setText(title);
        }
        return this;
    }

    public BottomDialog setContent(String content) {
        if (!TextUtils.isEmpty(content)) {
            contentTv.setVisibility(View.VISIBLE);
            contentTv.setText(content);
        }
        return this;
    }

    public BottomDialog setLeftBtn(String btn) {
        if (!TextUtils.isEmpty(btn)) {
            leftBtnTv.setVisibility(View.VISIBLE);
            leftBtnTv.setText(btn);
        }
        return this;
    }

    public BottomDialog setRightBtn(String btn) {
        if (!TextUtils.isEmpty(btn)) {
            rightBtnTv.setVisibility(View.VISIBLE);
            rightBtnTv.setText(btn);
        }
        return this;
    }

    public BottomDialog setOnBtnClickListener(OnBtnClickListener listener) {
        this.listener = listener;
        return this;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_leftBtn:
                dismiss();
                if (listener != null) {
                    listener.onLeftBtnClick(v);
                }
                break;
            case R.id.tv_rightBtn:
                dismiss();
                if (listener != null) {
                    listener.onRightBtnClick(v);
                }
                break;
            default:
                break;
        }
    }

    public void setOnItemClickListener(BtnsBottomDialogAdapter.OnItemClickListener itemClickListener) {
        this.listener1 = itemClickListener;
    }

    @Override
    public void onItemClick(View view, int position) {
        dismiss();
        if (listener != null) {
            listener1.onItemClick(view, position);
        }
    }

    public interface OnBtnClickListener {
        void onLeftBtnClick(View view);

        void onRightBtnClick(View view);
    }

}

 最后终于要应用了

  //普通底部弹出窗
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String text = false ? "您当前可升级更新到V" + 1.1 : "您当前已是最新版本";
                BottomDialog dialog = new BottomDialog(MainActivity.this);
                dialog.setTitle("检查更新").setContent(text).setLeftBtn("取消");
                if (false) {
                    dialog.setRightBtn("点此下载最新版");
                }
                //自定义Dialog按钮监听
                dialog.setOnBtnClickListener(new BottomDialog.OnBtnClickListener() {
                    @Override
                    public void onLeftBtnClick(View view) {
                    }

                    @Override
                    public void onRightBtnClick(View view) {
                    }
                }).show();

            }
        });
        //列表底部弹出窗
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                List<DialogBtnData> btnList = new ArrayList<>();
                btnList.add(new DialogBtnData(1, "保存"));
                btnList.add(new DialogBtnData(2, "取消"));
                BottomDialog dialog = new BottomDialog(MainActivity.this, btnList);

                dialog.setOnItemClickListener(new BtnsBottomDialogAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int pos) {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                            }
                        }).start();
                    }
                });
                dialog.show();
            }
        });


总结

切记 遇到问题不要慌不要慌,实在没思路就去看源码,尤其自定义的控件,问题千奇百怪,要细心排查。就像这个横屏dialog显示不全的问题,源码上是这样的

@Override

protected void onStart() {

super.onStart();

   if (behavior != null && behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {

    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
  }

}

 如果对你有所帮助的话,不妨 点赞收藏
 如果你有什么疑问的话,不妨 评论私信
 青山不改,绿水长流 ,有缘江湖再见 ~

源码地址:BottomSheetDialog

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值