建造者模式打造随心所欲的Android对话框

可能有人要说不就是自定义dialog吗,网上一搜案例demo多得是,而且也不难,没什么好讲的。确实百度一下自定义的dialog数不胜数。但是大多数文章都是单一的布局实现单一的样式。假如说项目中有多个不同布局的dialog,比如三种、五种甚至十种,当然我只说假如。如果你的项目只有一种样式的对话框。那么也没有必要再看再往下看了。
如上所述,碰到多种样式的对话框应该怎么办呢?总不能项目需要几种就去写几种自定义样式吧?很显然我们不会那样做!所以这就是写这篇文章的用意。接下来我们将通过建造者模式来自定义dialog,并对自定义的dialog做下简单封装,最后实现的需求是只需要写dialog的布局文件即可实现任意样式的对话框!有木有很心动?闲话不多扯了,开撸!
国际惯例,开撸前先看效果图。图中使用同一个自定义dialog实现了两种不同样式的对话框。
这里写图片描述
一、用建造者模式实现自定义Dialog
1.首先我们要做的是去自定义Dialog,创建CustomDialog并继承Dialog。
2.明确Dialog所需要的属性,宽度、高度、Dialog的布局所对应的View、点击外部是否取消dialog、以及Dialog的主题(Theme),其中主题(Theme)我们没有定义在CustomDialog内部,而是定义到了Builder中。因此需要在CustomDialog内定义如下成员变量:

    //  dialog高度
    private int height;
    //  dialog宽度
    private int width;
    //  点击外部是否可以取消
    private boolean cancelTouchOutside;
    //  对话框布局对应的View
    private View dialogView;

3.接下来我们在CustomDialog内部创建静态内部类Builder,根据Dialog布局计算dialog宽高,并给定默认的对话框布局样式custom_dialog2。如下图(页面布局代码不再贴出):

这里写图片描述
Builder代码如下:

public static final class Builder {

        private Context context;
        private int height, width;
        private boolean cancelTouchOutside;
        private View mDialogView;
        private int resStyle = -1;

        public Builder(Context context) {
            this.context = context;

            mDialogView = LayoutInflater.from(context).inflate(R.layout.custom_dialog2, null);
            //  计算dialog宽高
            int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            mDialogView.measure(measureSpec, measureSpec);
            height = mDialogView.getMeasuredHeight();
            width = mDialogView.getMeasuredWidth();
        }

        /**
         * @param dialogView 关联dialog布局文件的View
         * @return
         */
        public Builder setDialogLayout(View dialogView) {
            this.mDialogView = dialogView;
            return this;
        }

        public Builder setHeightPx(int val) {
            height = val;
            return this;
        }

        public Builder setWidthPx(int val) {
            width = val;
            return this;
        }

        public Builder setHeightDp(int val) {
            height = ScreenUtils.dp2px(context, val);
            return this;
        }

        public Builder setWidthDp(int val) {
            width = ScreenUtils.dp2px(context, val);
            return this;
        }

        /**
         * 设置主题
         *
         * @param resStyle
         * @return
         */
        public Builder setTheme(int resStyle) {
            this.resStyle = resStyle;
            return this;
        }

        /**
         * 设置点击dialog外部是否取消dialog
         *
         * @param val
         * @return
         */
        public Builder cancelTouchOutside(boolean val) {
            cancelTouchOutside = val;
            return this;
        }

        /**
         * 给dialog中的view添加点击事件
         *
         * @param viewResId 被点击view的id
         * @param listener
         * @return
         */
        public Builder addViewOnclick(int viewResId, View.OnClickListener listener) {
            mDialogView.findViewById(viewResId).setOnClickListener(listener);
            return this;
        }

        /**
         * 确定键监听
         * @param confirm
         * @param listener
         * @return
         */
        public Builder addConfirmClickListener(String confirm, View.OnClickListener listener) {
            TextView tvConfirm = (TextView) mDialogView.findViewById(R.id.tv_confirm);
            tvConfirm.setText(confirm);
            tvConfirm.setOnClickListener(listener);
            return this;
        }

        /**
         * 取消键监听
         * @param cancel
         * @param listener
         * @return
         */
        public Builder addCancelClickListener(String cancel, View.OnClickListener listener) {
            TextView tvCancel = (TextView) mDialogView.findViewById(R.id.tv_cancel);
            tvCancel.setText(cancel);
            tvCancel.setOnClickListener(listener);
            return this;
        }

        /**
         * 设置内容
         * @param content
         * @return
         */
        public Builder setContent(String content) {
            TextView tvTitle = (TextView) mDialogView.findViewById(R.id.tv_dialog_content);
            tvTitle.setText(content);
            return this;
        }

        /**
         * 设置取消键颜色
         * @param color 颜色
         * @return
         */
        public Builder setCancelColor(int color){
            TextView tvCancel= (TextView) mDialogView.findViewById(R.id.tv_cancel);
            tvCancel.setTextColor(color);
            return this;
        }
        /**
         * 设置确定键颜色
         * @param color 颜色
         * @return
         */
        public Builder setConfirmColor(int color){
            TextView tvCancel= (TextView) mDialogView.findViewById(R.id.tv_confirm);
            tvCancel.setTextColor(color);
            return this;
        }

        /**
         * 显示一个按钮的弹窗
         * @return
         */
        public Builder showOneButton() {
            mDialogView.findViewById(R.id.tv_cancel).setVisibility(View.GONE);
            mDialogView.findViewById(R.id.view_dialog).setVisibility(View.GONE);
            return this;
        }

        public CustomDialog build() {
            if (resStyle != -1) {
                return new CustomDialog(this, resStyle);
            } else {
                return new CustomDialog(this);
            }
        }
    }

5.上面代码在build()方法中实例化了CustomDialog,因此我们不要忘了CustomDialog中的构造方法。在构造方法中获取Builder中的属性。构造方法有两个,如下:

private CustomDialog(Builder builder) {
        super(builder.context);
        height = builder.height;
        width = builder.width;
        cancelTouchOutside = builder.cancelTouchOutside;
        dialogView = builder.mDialogView;
    }


    private CustomDialog(Builder builder, int theme) {
        super(builder.context, theme);
        height = builder.height;
        width = builder.width;
        cancelTouchOutside = builder.cancelTouchOutside;
        dialogView = builder.mDialogView;
    }

6.最后还要在onCreate()中为dialog设置布局和一些其他属性。代码如下:

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

        setContentView(dialogView);
        //  设置Touch的时候是否取消Dialog
        setCanceledOnTouchOutside(cancelTouchOutside);

        Window window = getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.gravity = Gravity.CENTER;
        params.height = height;
        params.width = width;
        window.setAttributes(params);
    }

这样自定义的Dialog就完成了。

二、对自定义的dialog简单封装。
完成自定义dialog之后接下来我们在项目的BaseActivity中对自定义的dialog做下封装。由于在构建DIalog的建造者时已经事先给定了默认布局,因此先对默认布局做下封装。
1.在BaseActivity中封装只有一个Button的对话框,并指定半透明背景样式,代码如下:

/**
     * @param content         内容
     * @param confirm         按钮文字
     * @param confirmListener 按钮监听
     */
    public void showOneButtonDialog(String content, String confirm, View.OnClickListener confirmListener) {
        dialog = new CustomDialog.Builder(this)
                .setTheme(R.style.CustomDialog1)
                .setContent(content)
                .addConfirmClickListener(confirm, confirmListener)
                .showOneButton()
                .build();
        dialog.show();
    }

2.封装两个按钮的dialog,指定半透明背景。代码如下:

/**
     * @param content         内容
     * @param confirm         确定键文字
     * @param cancel          取消键文字
     * @param confirmListener 确定键监听
     * @param cancelListener  取消键监听
     */
    public void showTwoButtonDialog(String content, String confirm, String cancel,
                                    View.OnClickListener confirmListener,
                                    View.OnClickListener cancelListener) {
        dialog = new CustomDialog.Builder(this)
                .setTheme(R.style.CustomDialog1)
                .setContent(content)
                .addConfirmClickListener(confirm, confirmListener)
                .addCancelClickListener(cancel, cancelListener)
                .build();
        dialog.show();
    }

3.封装两个按钮切能改变按钮字体颜色的对话框,背景指定全透明,代码如下:

/**
     * @param content         内容
     * @param confirm         确定键文字
     * @param cancel          取消键文字
     * @param confirmColor    确定键颜色
     * @param cancelColor     取消键颜色
     * @param confirmListener 确定键监听
     * @param cancelListener  取消键监听
     */
    public void showTwoButtonDialog(String content, String confirm, String cancel,
                                    @ColorInt int confirmColor, @ColorInt int cancelColor,
                                    View.OnClickListener confirmListener,
                                    View.OnClickListener cancelListener) {
        dialog = new CustomDialog.Builder(this)
                .setTheme(R.style.CustomDialog2)
                .setContent(content)
                .setConfirmColor(confirmColor)
                .setCancelColor(cancelColor)
                .addConfirmClickListener(confirm, confirmListener)
                .addCancelClickListener(cancel, cancelListener)
                .build();
        dialog.show();
    }

4.封装自定义样式的对话框,需要自行定义对话框布局文件。代码如下:

/**
     * create custom dialog
     * 可以定制任意的dialog样式
     *
     * @param dialogLayoutRes    dialog布局资源文件
     * @param cancelTouchOutside 点击外部是否可以取消
     * @return
     */
    public View createCustomDialog(@LayoutRes int dialogLayoutRes, boolean cancelTouchOutside) {
        dialogView = LayoutInflater.from(this).inflate(dialogLayoutRes, null);
        //  计算dialog宽高
        int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        dialogView.measure(measureSpec, measureSpec);
        int height = dialogView.getMeasuredHeight();
        int width = dialogView.getMeasuredWidth();

        dialog = new CustomDialog.Builder(this)
                .setTheme(R.style.CustomDialog1)
                .setHeightPx(height)
                .setWidthPx(width)
                .cancelTouchOutside(cancelTouchOutside)
                .setDialogLayout(dialogView).build();
        dialog.show();
        return dialogView;
    }

自定义样式的dialog由于没有给定点击事件,因此需要在Activity中添加dismiss dialog的方法,可以在子Activity中自定义点击事件的时候调用,如下:

    /**
     * 隐藏dialog
     */
    public void dismissDialog() {
        if (dialog != null && dialog.isShowing()) {
            dialog.dismiss();
        }
    }

三、在子Activity中根据需求显示不同的对话框。
MainActivity继承BaseActivity,并在MainActivity中调用BaseActivity中的不同对话框的方法显示对话框。代码如下:

//  显示一个按钮的对话框
    private void showOneButton(){
        showOneButtonDialog("一个Button的Dialog", "确定", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismissDialog();
            }
        });
    }

    //  显示两个按钮的对话框
    private void showTwoButton(){
        showTwoButtonDialog("两个Button的Dialog", "确定", "取消",
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dismissDialog();
                    }
                }, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dismissDialog();
                    }
                });
    }

    //  显示两个按钮且可以改变按钮颜色且背景全透明的对话框
    public void showTowButtonWithColor() {
        showTwoButtonDialog("改变Button颜色的Dialog\n背景全透明", "确定", "取消",
                Color.parseColor("#ff0000"), Color.parseColor("#00ff00"),
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dismissDialog();
                    }
                }, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dismissDialog();
                    }
                });
    }

    //  显示自定义样式的对话框
    private void showCustomDialog() {
        mDialogView1 = createCustomDialog(R.layout.custom_dialog1, false);
        //  为自定义的dialog设置内容、添加点击事件
        TextView viewById1 = (TextView) mDialogView1.findViewById(R.id.tv_dialog_content);
        viewById1.setText("自定义样式的Dialog");
        mDialogView1.findViewById(R.id.btn_dialog).setOnClickListener(this);
    }

上面代码自定义对话框createCustomDialog()方法的返回值是自定义布局文件对应的View,因此可以通过该View获取到布局中的所有子View,然后为其设置内容或者监听事件。

项目链接

猜你喜欢

转载自blog.csdn.net/qq_20521573/article/details/70175442