安卓设计模式(三)Builder模式

作者: uncochen | 来源:发表于2016-09-30 11:05 被阅读942次

    Builder模式也叫建造者模式,属于创建性模式,一般用于复杂对象的创建
    该模式可以将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来

    该系列其他文章:

    Android中的使用场景

    • 复杂对象的创建,内部包含多个部件或者零件,都可以装配到一个对象中.如AlertDialog.Builder()

        new AlertDialog.Builder(this)
                .setPositiveButton("确定", null)
                .setTitle("请求权限")
                .setCancelable(false)
                .setMessage(messageResId)
                .show();
      
    • 用于框架的初始化,初始化之后无法对框架内部数据再做改动.如FileDownloader框架的初始化:

        FileDownloadConfiguration.Builder builder = new FileDownloadConfiguration.Builder(this);
        builder.configFileDownloadDir(StorageUtils.getFilesDirectory(mContext).getAbsolutePath() + File.separator + "pdf");
        builder.configRetryDownloadTimes(2);
        FileDownloadConfiguration configuration = builder.build();
        FileDownloader.init(configuration);
      
    • 创建或初始化对象时,参数多,并且参数都具有默认值.

    用法

    这里通过一个具体需求,一步一步来设计Builder模式

    底部这个弹出框在很多页面会用到,我们就需要封装一下,叫做MenuDialog,这里使用Dialog来做(也可以用Popwindow),分析这个dialog需求,特点如下:

    • 从底部弹出,上面一行为分享组件(ShareMenu),是固定的,用于分享
    • 第二行为一些具体操作的按钮(ActionMenu),并且第二行有时候是不需要的(隐藏)
    • 考虑到扩展性,ActionMenu的个数和每一个的图标和提示语(msg)应该是可定制的
    • ShareMenu的点击事件就是调起分享,可以统一处理,ActionMenu由于具体操作不同,应该暴露给调用者自行处理
    • ActionMenu的图标和msg都应该有默认,"分享到"这个title也应该有默认并且可定制

    数据存放 MenuDiaControl

    在builder模式中,一般会有个存放数据的类Control,这里新建MenuDiaControl用于存放需要用到的参数

    public class MenuDiaControl {
        private Context mContext;
        private String title = "分享到";
        //share
        private String mShareTitle = "";
        private String mShareContent = "";
        private String mShareImageUrl = "";
        private String mShareUrl = "";
        //action
        private boolean mHineActionAll = false;//是否隐藏action操作栏
        private List<Boolean> mBooleanList = new ArrayList<>();//单个action的按钮的图标和msg
        private MoreMenuClickListener mListener;//Action回调
        //geter and seter
    }
    

    内部类 Builder

    Builder类是Builder模式中的主体操作类,接受配置参数并最后生成对象,具体实现如下:

    public static class Builder {
        private final MenuDiaControl mDiaControl;//存放参数
    
        public Builder(Context context) {
            mDiaControl = new MenuDiaControl(context);//初始化Control
        }
    
        /*<====================================公开的配置方法====================================================>*/
    
        /**
         * 设置title 默认="分享到"
         *
         * @param title
         * @return
         */
        public Builder title(String title) {
            mDiaControl.setTitle(title);
            return this;
        }
        //更多...(具体代码在文章最后)
    
        /*<====================================公开的配置方法===================================================>*/
    
        public MenuDialog build() {//生成对象
            return new MenuDialog(mDiaControl);
        }
    
    }
    

    对象 MenuDialog

    即我们需要通过Builder模式创建的对象,最后的产出

    public class MenuDialog extends Dialog {
    
    //ButterKnife.bind..
    
    private ShareUtils mShare;
    private MenuDiaControl mControl;
    private List<TextView> mViewList = new ArrayList<>();
    
    private MenuDialog(MenuDiaControl control) {
        this(control.getContext(), R.style.CustomDialog);
        mControl = control;
        init();
    }
    
    private MenuDialog(Context context, int themeResId) {
        super(context, themeResId);
    }
    
    private void init() {    //setContentView
        View diaView = View.inflate(mControl.getContext(), R.layout.dialog_post_operator, null);
        setContentView(diaView);
        ButterKnife.bind(this);
        initWindow();
        setContentView(diaView);
        initView();
        mShare = new ShareUtils(mControl.getContext());        //分享工具类
    }
    
    private void initView() {        //根据Control中的参数,为对象设置各种属性
        mTvTitle.setText(mControl.getTitle());
        mViewList.add(mTvBackCircleList);
        mViewList.add(mTvCopyUrl);
        mViewList.add(mTvReport);
        mViewList.add(mTvDelete);
        for (int i = 0; i < mViewList.size(); i++) {    //设置单个action
            if (mControl.getBooleanList().get(i)) mViewList.get(i).setVisibility(View.INVISIBLE);
            TextView textView = mViewList.get(i);
            MenuBean menuBean = mControl.getMenuBeanList().get(i);
            textView.setText(menuBean.getMsg());
            Drawable drawable = mControl.getContext().getResources().getDrawable(menuBean.getIconRes());
            drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
            textView.setCompoundDrawables(null, drawable, null, null);
        }
        mLlMore.setVisibility(mControl.isHineActionAll() ? View.GONE : View.VISIBLE);//是否隐藏ActionMenu
    
    }
    
    public static class Builder {
        //Builder内部类...
    }
    
    private void initWindow() {    //设置window的一些属性
        setCancelable(false);
        Window window = getWindow();
        int width = LinearLayout.LayoutParams.MATCH_PARENT;
        window.setLayout(width, LinearLayout.LayoutParams.WRAP_CONTENT);
        window.setGravity(Gravity.BOTTOM);
        setCanceledOnTouchOutside(true);
    }
    
    @OnClick({R.id.tv_wechat, R.id.tv_wechat_circle, R.id.tv_qq, R.id.tv_sina, R.id.tv_back_circle_list, R.id.tv_copy_url, R.id
            .tv_report, R.id.tv_delete, R.id.btn_cancel})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.tv_wechat:
                mShare.initShare(mControl.getShareTitle(), mControl.getShareContent(), mControl.getShareImageUrl(), mControl.getShareUrl
                        (), null);
                mShare.setPlatform(Wechat.NAME);
                mShare.startShare();
            
                break;
            //...执行统一的分享操作
            case R.id.tv_back_circle_list:    //Action具体操作回调给调用者
                if (mControl.getListener() != null)
                    mControl.getListener().menuClick(0, this);
                break;
            case R.id.tv_copy_url:
                if (mControl.getListener() != null)
                    mControl.getListener().menuClick(1, this);
                break;
            case R.id.tv_report:
                if (mControl.getListener() != null)
                    mControl.getListener().menuClick(2, this);
                break;
            case R.id.tv_delete:
                if (mControl.getListener() != null)
                    mControl.getListener().menuClick(3, this);
                break;
            case R.id.btn_cancel:
                break;
        }
        dismiss();
    }
    }  
    

    使用

    ok,一切都搞定后,我们看下使用方法,跟AlerterDialog的使用很像吧.我们使用Builder模式封装Menudialog,使用简单,链式调用,支持定制,在这个项目中是通用的,满足了上面的需求.

        new MenuDialog.Builder(this)
                .hideActionAll(false)//不隐藏ActionMenu
                .shareData(new ShareBean(mInfoItem.getTitle(), mInfoItem.getSummary(), mInfoItem.getTitlePic(), mInfoItem.getUrl()))//设置分享数据
                .setActionMenu(2,R.mipmap.icon,"msg")//制定ActionMenu
                .title("设置title")
                .hideAction4pos(3)//隐藏单个Action
                .build()
                //...
                .show();
    

    总结

    Builder模式设计起来很简单,大家可以大胆的用到自己的项目或者框架中.

    • Control类不是必须的,参数可以直接存放在Builder中,Android中很多Builder模式是省略Control的
    • 上面的R.style.CustomDialog主要是用来使Dialog全屏的
    • 上面的ShareBean是分享需要的参数实体
    • ShareMenu也可以提供分享是否成功的回调,提供定制,等等待完善功能...

    具体:

    <style name="CustomDialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <!-- Dialog的windowFrame框为无 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 是否浮现在activity之上 -->
        <item name="android:windowIsTranslucent">false</item>
        <!-- 是否半透明 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 背景透明-->
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>`
    

    Builder类具体代码:

    public static class Builder {
        private final MenuDiaControl mDiaControl;
    
        public Builder(Context context) {
            mDiaControl = new MenuDiaControl(context);
        }
    
        /*<========================================================================================>*/
        /**
         * 设置title 默认="分享到"
         *
         * @param title
         * @return
         */
        public Builder title(String title) {
            mDiaControl.setTitle(title);
            return this;
        }
    
        /**
         * 是否隐藏第二行的扩展操作按钮
         * 默认不隐藏
         *
         * @param hide
         * @return
         */
        public Builder hideActionAll(boolean hide) {
            mDiaControl.setHineActionAll(hide);
            return this;
        }
    
        public Builder shareTitle(String title) {
            mDiaControl.setShareTitle(title);
            return this;
        }
    
        public Builder shareContent(String shareContent) {
            mDiaControl.setShareContent(shareContent);
            return this;
        }
    
        public Builder shareImageUrl(String shareImageUrl) {
            mDiaControl.setShareImageUrl(shareImageUrl);
            return this;
        }
    
        public Builder ShareUrl(String ShareUrl) {
            mDiaControl.setShareUrl(ShareUrl);
            return this;
        }
    
        /**
         * 一次性设置分享需要的数据
         *
         * @param bean
         * @return
         */
        public Builder shareData(ShareBean bean) {
            mDiaControl.setShareTitle(bean.getTitle());
            mDiaControl.setShareContent(bean.getContent());
            mDiaControl.setShareImageUrl(bean.getImageUrl());
            mDiaControl.setShareUrl(bean.getUrl());
            return this;
        }
    
        /**
         * 设置底部某个menu的图标和msg
         *
         * @param position
         * @param iconRes
         * @param msg
         * @return
         */
        public Builder setActionMenu(int position, int iconRes, String msg) {
            if (position < 0 || position > 3) return this;
            mDiaControl.getMenuBeanList().set(position, new MenuBean(iconRes, msg, false));
            return this;
        }
    
        /**
         * 隐藏底部某个menu
         *
         * @param position
         * @return
         */
        public Builder hideAction4pos(int position) {
            if (position < 0 || position > 3) return this;
            mDiaControl.getBooleanList().set(position, true);
            return this;
        }
    
        /**
         * 底部menu的点击回调
         *
         * @param listener
         * @return
         */
        public Builder addActionMenuClick(MenuDiaControl.MoreMenuClickListener listener) {
            mDiaControl.setListener(listener);
            return this;
        }
    
        /*<========================================================================================>*/
    
        /**
         * 构造器
         *
         * @return
         */
        public MenuDialog build() {
            return new MenuDialog(mDiaControl);
        }
    }
    

    关于作者

    相关文章

      网友评论

        本文标题:安卓设计模式(三)Builder模式

        本文链接:https://www.haomeiwen.com/subject/drorettx.html