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

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);
    }
}

关于作者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 前言:这个过程中遇到了两个问题,都比较基础,第一个问题是:系统无法识别图片资源,不过还好,被我删了之后就很好的运行...
    九尾74阅读 2,979评论 0 6
  • 设计模式之二:Builder模式目录介绍0.关于Builder模式案例下载1.Builder模式介绍2.Build...
    杨充211阅读 1,004评论 0 1
  • 面向对象的六大原则 单一职责原则 所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于...
    JxMY阅读 884评论 1 3
  • 今日事件: 1.转帐开通香港民生 操作步骤: 用电脑登录民生网银,购汇并完成转帐(时间也很宝贵,为几百美元跑银行太...
    龙卷风227阅读 181评论 0 0