Builder设计模式构建自定义万能的Dialog

1. 概述


基于上节课我们对AlertDialog源码的分析,那么这节课我们就一起来写一个 万能的 Dialog。

2. 分析


其实主要就是三个类,分别是AlertController、AlertDialog(万能的Dialog)、DialogViewHelper(Dialog的辅助处理类),下边就是这3个类的具体的实现方式
AlertController代码如下:

/**
 * Email: [email protected]
 * Created by JackChen 2018/4/5 17:04
 * Version 1.0
 * Params:
 * Description:
*/
class AlertController {

    private AlertDialog mDialog;
    private Window mWindow;

    private DialogViewHelper mViewHelper;

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

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

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

    public  T getView(int viewId) {
        return mViewHelper.getView(viewId);
    }

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

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

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

    public static class AlertParams {
        public Context mContext;
        public int mThemeResId;
        // 点击空白是否能够取消  默认点击阴影可以取消
        public boolean mCancelable = true;
        // dialog Cancel监听
        public DialogInterface.OnCancelListener mOnCancelListener;
        // dialog Dismiss监听
        public DialogInterface.OnDismissListener mOnDismissListener;
        // dialog Key监听
        public DialogInterface.OnKeyListener mOnKeyListener;
        // 布局View
        public View mView;
        // 布局layout id
        public int mViewLayoutResId;
        // 存放字体的修改
        public SparseArray mTextArray = new SparseArray<>();
        // 存放点击事件
        public SparseArray mClickArray = new SparseArray<>();
        // 宽度
        public int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
        // 动画
        public int mAnimations = 0;
        // 位置
        public int mGravity = Gravity.CENTER;
        // 高度
        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);
        }
    }
}

万能的Dialog,即就是AlertDialog,代码如下:

/**
 * Email: [email protected]
 * Created by JackChen 2018/4/5 17:24
 * Version 1.0
 * Params:
 * Description:  自定义万能的  Dialog
*/

public class AlertDialog extends Dialog {

    private AlertController mAlert;


    public AlertDialog(Context context, 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 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.
         * 

* 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. *

* 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. *

* 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. *

* 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. *

* When using a pre-Holo theme, if the supplied view is an instance of * a {@link ListView} then the light background will be used. *

* Note: 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 setContentView(View view) { P.mView = view; P.mViewLayoutResId = 0; return this; } // 设置布局内容的layoutId public Builder setContentView(int layoutId) { P.mView = null; P.mViewLayoutResId = layoutId; return this; } // 设置文本 public Builder setText(int viewId,CharSequence text){ P.mTextArray.put(viewId,text); return this; } // 设置点击事件 public Builder setOnClickListener(int view , View.OnClickListener listener){ P.mClickArray.put(view,listener); return this; } // 配置一些万能的参数 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; } /** * 设置Dialog的宽高 * @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; } /** * Sets whether the dialog is cancelable or not. Default is true. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setCancelable(boolean cancelable) { P.mCancelable = cancelable; return this; } /** * Sets the callback that will be called if the dialog is canceled. * *

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}.

* @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; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder. *

* 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. *

* Calling this method is functionally identical to: *

         *     AlertDialog dialog = builder.create();
         *     dialog.show();
         * 
*/ public AlertDialog show() { final AlertDialog dialog = create(); dialog.show(); return dialog; } } }

DialogViewHelper,是Dialog的一个辅助处理类,代码如下:

class DialogViewHelper {

    private View mContentView = null;
    // 防止霸气侧漏
    private SparseArray> mViews;

    public DialogViewHelper(Context context, int layoutResId) {
        this();
        mContentView = LayoutInflater.from(context).inflate(layoutResId, null);
    }


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

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

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

    public  T getView(int viewId) {
        WeakReference viewReference = mViews.get(viewId);
        // 侧漏的问题  到时候到这个系统优化的时候再去介绍
        View view = null;
        if (viewReference != null) {
            view = viewReference.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;
    }
}

3. 具体使用


万能的 Dialog的具体使用如下:

/**
 * Email: [email protected]
 * Created by JackChen 2018/4/5 18:05
 * Version 1.0
 * Params:
 * Description:  万能Dialog 的具体使用场景
*/
public class MainActivity extends BaseSkinActivity implements View.OnClickListener{


    @ViewById(R.id.btn_dialog)
    Button btn_dialog ;


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

    @Override
    protected void initTitle() {

    }

    @Override
    protected void initView() {
        btn_dialog.setOnClickListener(this);
    }

    @Override
    protected void initData() {

    }


    @Override
    public void onClick(View v) {
        showDialogFromBottom() ;
    }

    private void showDialogFromBottom() {
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setContentView(R.layout.detail_comment_dialog)
                .formBottom(true).fullWidth().show();

        // 获取EditText的输入框的内容
        final EditText commentEt = dialog.getView(R.id.comment_editor) ;
        dialog.setOnclickListener(R.id.submit_btn , new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this , "输入的内容为:" + commentEt.getText().toString() , Toast.LENGTH_SHORT).show();
            }
        });


        dialog.setOnclickListener(R.id.account_icon_weibo , new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this , "点击微博分享了" + commentEt.getText().toString() , Toast.LENGTH_SHORT).show();
            }
        });


        dialog.setOnclickListener(R.id.account_icon_tencent, new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this , "点击qq分享了" + commentEt.getText().toString() , Toast.LENGTH_SHORT).show();
            }
        });
    }

}

具体代码已上传至github:
https://github.com/shuai999/EssayJoke_day_05.git

你可能感兴趣的:(Builder设计模式构建自定义万能的Dialog)