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

来源:互联网 发布:java thread join 编辑:程序博客网 时间:2024/05/21 14:04

可能有人要说不就是自定义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,然后为其设置内容或者监听事件。

项目链接

1 0