Builder设计模式之构建万能Dialog

一. Builder设计模式
定义

将一个复杂对象的构建与它的表示分离,使得不同的构建过程可以创建不同的显示,但其根本还是不变。

使用场景

1、相同的方法,不同的执行顺序,产生不同的事件结果时;
2、多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
3、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。

UML图
这里写图片描述
介绍

Product 产品类 : 产品的抽象类;
Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程;
ConcreteBuilder : 具体的构建器;
Director : 统一组装过程(可省略)。

Builder设计模式的工作流程
添加参数 (P) –> 组装参数 –> 显示

二. 使用Builder设计模式构建万能Dialog

主要的对象
AlertDialog (电脑对象)
AlertDialog.Builder 规范一系列的组装过程
AlertController 具体的构建器
AlertController.AlertParams 存放参数 , 一部分设置参数的功能
DialogViewHelper Dialog View 的辅助处理类

效果视图
这里写图片描述这里写图片描述

实现的功能,基本囊括一般开发的需求

  • 自定义布局
  • 自定义弹窗宽高
  • 设置弹窗位置
  • 根据id设置控件的文本
  • 根据id设置控件的点击事件
  • 设置弹出动画效果,有默认动画

具体实现
DialogViewHelper.java

/**
 * @creation_time: 2017/6/26
 * @author: Vegen
 * @e-mail: vegenhu@163.com
 * @description: Dialog View 的辅助处理类
 */

class DialogViewHelper {

    private View mContentView = null;

    // WeakReference软引用,防止霸气侧漏
    private SparseArray<WeakReference<View>> mViews;

    public DialogViewHelper(Context mContext, int mViewLayoutResId) {
        this();         // 很重要,没有就报mViews空指针
        mContentView = LayoutInflater.from(mContext).inflate(mViewLayoutResId, null);
    }

    public DialogViewHelper() {
        mViews = new SparseArray<>();
    }

    /**
     * 设置布局
     * @param contentView
     */
    public void setContentView(View contentView) {
        this.mContentView = contentView;
    }

    /**
     * 设置文本
     * @param viewId
     * @param text
     */
    public void setText(int viewId, CharSequence text) {
        // 优化,减少findViewById的次数,做缓存
        TextView tv = getView(viewId);
        if (tv != null){
            tv.setText(text);
        }
    }

    public  <T extends View> T getView(int viewId) {
        WeakReference<View> viewWeakReference = mViews.get(viewId);
        View view = null;
        if (viewWeakReference != null){
            view = viewWeakReference.get();
        }
        if (view == null){
            view = mContentView.findViewById(viewId);
            if (view != null) {
                mViews.put(viewId, new WeakReference<>(view));
            }
        }
        return (T) view;
    }

    /**
     * 设置点击事件
     * @param viewId
     * @param listener
     */
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        View view = getView(viewId);
        if (view != null){
            view.setOnClickListener(listener);
        }
    }

    /**
     * 获取ContentView
     * @return
     */
    public View getContentView() {
        return mContentView;
    }
}

AlertController.java

/**
 * @creation_time: 2017/6/26
 * @author: Vegen
 * @e-mail: vegenhu@163.com
 * @description: 具体的构建器
 */

class AlertController {

    private AlertDialog mDialog;
    private Window mWindow;
    private DialogViewHelper mViewHelper;

    /**
     * 获取Dialog
     * @return
     */
    public AlertDialog getDialog() {
        return mDialog;
    }

    /**
     * 获取Dialog的Window
     * @return
     */
    public Window getWindow() {
        return mWindow;
    }

    public AlertController(AlertDialog dialog, Window window) {
        this.mDialog = dialog;
        this.mWindow = window;
    }

    /**
     * 设置文本
     * @param viewId
     * @param text
     */
    public void setText(int viewId, CharSequence text) {
        mViewHelper.setText(viewId, text);
    }

    public  <T extends View> T getView(int viewId) {
        return mViewHelper.getView(viewId);
    }

    /**
     * 设置点击事件
     * @param viewId
     * @param listener
     */
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        mViewHelper.setOnClickListener(viewId, listener);
    }

    public void setViewHelper(DialogViewHelper viewHelper) {
        this.mViewHelper = viewHelper;
    }

    public static class AlertParams{

        public Context mContext;
        public int mThemeResId;
        // 点击空白地方是否能够取消,默认能
        public boolean mCancelable = true;
        // dialog Cancel监听
        public DialogInterface.OnCancelListener mOnCancelListener;
        // dialog 消失监听
        public DialogInterface.OnDismissListener mOnDismissListener;
        // dialog 按键监听
        public DialogInterface.OnKeyListener mOnKeyListener;
        // 布局 View
        public View mView;
        // 布局 layout id
        public int mViewLayoutResId;
        // 存放字体的修改
        public SparseArray<CharSequence> mTextArray = new SparseArray<>();
        // 存放点击时间 WeakReference软引用,优化,防止内存泄漏
        public SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
        // Dialog 的宽度
        public int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
        // 动画
        public int mAnimations = 0;
        // 位置
        public int mGravity = Gravity.CENTER;
        // Dialog 的高度
        public int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;

        public AlertParams(Context context, int themeResId) {
            this.mContext = context;
            this.mThemeResId = themeResId;
        }

        /**
         * 绑定和设置参数
         * @param mAlert
         */
        public void apply(AlertController mAlert) {
            // 设置参数

            // 1.设置Dialog布局 DialogViewHelper
            DialogViewHelper viewHelper = null;
            if (mViewLayoutResId != 0){
                viewHelper = new DialogViewHelper(mContext, mViewLayoutResId);
            }

            if (mView != null){
                viewHelper = new DialogViewHelper();
                viewHelper.setContentView(mView);
            }

            if (viewHelper == null){
                throw new IllegalArgumentException("请设置布局setContentView()");
            }

            // 给Dialog 设置布局
            mAlert.getDialog().setContentView(viewHelper.getContentView());

            // 设置 Controller的辅助类
            mAlert.setViewHelper(viewHelper);

            // 2.设置文本
            int textArraySize = mTextArray.size();
            for (int i = 0; i < textArraySize; i ++){
                mAlert.setText(mTextArray.keyAt(i), mTextArray.valueAt(i));
            }

            // 3.设置点击
            int clickArraySize = mClickArray.size();
            for (int i = 0; i < clickArraySize; i ++){
                mAlert.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i));
            }

            // 4.配置自定义的效果 全屏 从底部弹出  默认动画
            Window window = mAlert.getWindow();
            // 设置位置
            window.setGravity(mGravity);
            // 设置动画
            if (mAnimations != 0) {
                window.setWindowAnimations(mAnimations);
            }
            // 设置宽高
            WindowManager.LayoutParams params = window.getAttributes();
            params.width = mWidth;
            params.height = mHeight;
            window.setAttributes(params);
        }
    }
}

AlertDialog.java,自定义的万能Dialog(参考系统的AlertDialog)

/**
 * @creation_time: 2017/6/26
 * @author: Vegen
 * @e-mail: vegenhu@163.com
 * @description: 自定义的万能Dialog
 */

public class AlertDialog extends Dialog {

    private AlertController mAlert;

    public AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, themeResId);
        mAlert = new AlertController(this, getWindow());
    }

    /**
     * 设置文本
     * @param viewId
     * @param text
     */
    public void setText(int viewId, CharSequence text) {
        mAlert.setText(viewId, text);
    }

    public  <T extends View> T getView(int viewId) {
        return mAlert.getView(viewId);
    }

    /**
     * 设置点击事件
     * @param viewId
     * @param listener
     */
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        mAlert.setOnClickListener(viewId, listener);
    }

    public static class Builder{


        private final AlertController.AlertParams P;
        /**
         * Creates a builder for an alert dialog that uses the default alert
         * dialog theme.
         * <p>
         * The default alert dialog theme is defined by
         * {@link android.R.attr#alertDialogTheme} within the parent
         * {@code context}'s theme.
         *
         * @param context the parent context
         */
        public Builder(Context context) {
            this(context, R.style.dialog);
        }

        /**
         * Creates a builder for an alert dialog that uses an explicit theme
         * resource.
         * <p>
         * The specified theme resource ({@code themeResId}) is applied on top
         * of the parent {@code context}'s theme. It may be specified as a
         * style resource containing a fully-populated theme, such as
         * {@link android.R.style#Theme_Material_Dialog}, to replace all
         * attributes in the parent {@code context}'s theme including primary
         * and accent colors.
         * <p>
         * To preserve attributes such as primary and accent colors, the
         * {@code themeResId} may instead be specified as an overlay theme such
         * as {@link android.R.style#ThemeOverlay_Material_Dialog}. This will
         * override only the window attributes necessary to style the alert
         * window as a dialog.
         * <p>
         * Alternatively, the {@code themeResId} may be specified as {@code 0}
         * to use the parent {@code context}'s resolved value for
         * {@link android.R.attr#alertDialogTheme}.
         *
         * @param context the parent context
         * @param themeResId the resource ID of the theme against which to inflate
         *                   this dialog, or {@code 0} to use the parent
         *                   {@code context}'s default alert dialog theme
         */
        public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(context, themeResId);
        }

        /**
         * Sets a custom view to be the contents of the alert dialog.
         * <p>
         * When using a pre-Holo theme, if the supplied view is an instance of
         * a {@link } then the light background will be used.
         * <p>
         * <strong>Note:</strong> To ensure consistent styling, the custom view
         * should be inflated or constructed using the alert dialog's themed
         * context obtained via {@link #getContext()}.
         *
         * @param view the view to use as the contents of the alert dialog
         * @return this Builder object to allow for chaining of calls to set
         *         methods
         */
        public Builder setView(View view) {
            P.mView = view;
            P.mViewLayoutResId = 0;
            return this;
        }

        /**
         * 设置布局内容的layout id
         * @param layoutResId
         * @return
         */
        public Builder setContentView(int layoutResId) {
            P.mView = null;
            P.mViewLayoutResId = layoutResId;
            return this;
        }

        /**
         * 设置文本
         * @param viewId
         * @param text
         * @return
         */
        public Builder setText(int viewId, CharSequence text){
            P.mTextArray.put(viewId, text);
            return this;
        }

        /**
         * 设置点击事件
         * @param view
         * @param listener
         * @return
         */
        public Builder setOnClickListener(int view, View.OnClickListener listener){
            P.mClickArray.put(view, listener);
            return this;
        }

        /**
         * Sets the callback that will be called if the dialog is canceled.
         *
         * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
         * being canceled or one of the supplied choices being selected.
         * If you are interested in listening for all cases where the dialog is dismissed
         * and not just when it is canceled, see
         * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener) setOnDismissListener}.</p>
         * @see #setCancelable(boolean)
         * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
            P.mOnCancelListener = onCancelListener;
            return this;
        }

        /**
         * Sets the callback that will be called when the dialog is dismissed for any reason.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
            P.mOnDismissListener = onDismissListener;
            return this;
        }

        /**
         * Sets the callback that will be called if a key is dispatched to the dialog.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setOnKeyListener(OnKeyListener onKeyListener) {
            P.mOnKeyListener = onKeyListener;
            return this;
        }

        /**
         * 全屏
         * @return
         */
        public Builder fullWidth(){
            P.mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
            return this;
        }

        /**
         * 从底部弹出
         * @param isAnimation 是否添加动画
         * @return
         */
        public Builder formBottom(boolean isAnimation){
            if (isAnimation){
                P.mAnimations = R.style.dialog_from_bottom_anim;
            }
            P.mGravity = Gravity.BOTTOM;
            return this;
        }

        /**
         * 设置宽高
         * @param width
         * @param height
         * @return
         */
        public Builder setWidthAndHeight(int width, int height){
            P.mWidth = width;
            P.mHeight = height;
            return this;
        }

        /**
         * 添加默认动画
         * @return
         */
        public Builder addDefaultAnimation(){
            P.mAnimations = R.style.dialog_scale_anim;
            return this;
        }

        /**
         * 添加动画
         * @param styleAnimation
         * @return
         */
        public Builder setAnimations(int styleAnimation){
            P.mAnimations = styleAnimation;
            return this;
        }


        /**
         * Creates an {@link AlertDialog} with the arguments supplied to this
         * builder.
         * <p>
         * Calling this method does not display the dialog. If no additional
         * processing is needed, {@link #show()} may be called instead to both
         * create and display the dialog.
         */
        public AlertDialog create() {
            // Context has already been wrapped with the appropriate theme.
            final AlertDialog dialog = new AlertDialog(P.mContext, P.mThemeResId);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }

        /**
         * Creates an {@link AlertDialog} with the arguments supplied to this
         * builder and immediately displays the dialog.
         * <p>
         * Calling this method is functionally identical to:
         * <pre>
         *     AlertDialog dialog = builder.create();
         *     dialog.show();
         * </pre>
         */
        public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }

    }
}

示例的弹窗布局:detail_comment_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_view"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/comment_dialog_bg"
        android:orientation="vertical"
        android:padding="10.0dip">

        <EditText
            android:id="@+id/comment_editor"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/bg_detail_comment_editor"
            android:hint="@string/ss_share_hint"
            android:maxHeight="120.0dip"
            android:padding="9.0dip"
            android:textColor="@color/comment_dialog_content_text"
            android:textSize="16.0sp" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10.0dip"
            android:gravity="center_vertical">

            <TextView
                android:id="@+id/share_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="10.0dip"
                android:text="@string/comment_dialog_share_label"
                android:textColor="@color/comment_dialog_share_text"
                android:textSize="13.0sp" />

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">

                <ImageView
                    android:id="@+id/account_icon_weibo"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@mipmap/account_icon_weibo" />

                <ImageView
                    android:id="@+id/account_icon_tencent"
                    android:layout_width="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:layout_height="wrap_content"
                    android:src="@mipmap/account_icon_tencent" />

            </LinearLayout>

            <LinearLayout
                android:id="@+id/platform_layout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toRightOf="@id/share_label"
                android:orientation="horizontal" />

            <TextView
                android:id="@+id/submit_btn"
                android:layout_width="50.0dip"
                android:layout_height="25.0dip"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:background="@drawable/bg_comment_submit"
                android:gravity="center"
                android:text="@string/comment_dialog_send"
                android:textColor="@color/comment_dialog_submit_text"
                android:textSize="13.0sp" />

            <TextView
                android:id="@+id/text_limit"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="10.0dip"
                android:layout_toLeftOf="@id/submit_btn"
                android:textSize="13.0sp"
                android:visibility="invisible" />
        </LinearLayout>
    </LinearLayout>
</FrameLayout>

MainActivity.java

public class MainActivity extends BaseActivity {

    @ViewById(R.id.test)
    private Button mTest;

    @Override
    protected void initData() {

        mTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 一行代码就可以搞掂!
                AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
                        .setContentView(R.layout.detail_comment_dialog)
                        .setText(R.id.submit_btn, "发送")
                        .setOnClickListener(R.id.account_icon_weibo, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "微博分享", Toast.LENGTH_SHORT).show();
                    }
                }).show();
            }
        });

    }

    @Override
    protected void initView() {

    }

    @Override
    protected void initTitle() {

    }

    @Override
    protected void setContentView() {
        setContentView(R.layout.activity_main);
    }

}

代码简单的就不贴了,注释写得很详细,也不作讲解。失恋了,很伤心,决定好好学习,天天向上~~刷一大波代码安慰一下自己!
放暑假了,求实习!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值