Android设计模式 Builder模式的分析与实践

之前实现了一个Demo底部弹出框,是用DialogFragment实现的一个Dialog,虽然实现了链式调用,但是没有使用Builder模式,所以想试试Builder模式,写博客记录一下。

这篇博客谈论的Android中的Builder模式没有Java传统的使用那么复杂,只是一种编程思路,使需要复杂的参数构建的对象变得简单,而且内部封装构建过程可以方便变换和复用,更是降低了耦合性。

分析 AlertDialog

AlertDialog是谷歌原生的对话框,在V7包中提供了Material Design设计风格的Dialog,使用非常广泛。但是,在这篇博客里,我们只讨论分析它的编程实现模式,它使用的就是Builder模式。

1.先来看看 AlertDialog 源码

AlertDialog源码中可以发现成员变量中有一个AlertController,从它的命名
可以了解它大概是个组织类,即是所谓的”控制层“;再从AlertDialog的构造方法中发现,它没做什么太多的操作,主要就是初始化了AlertController,那我们可以进一步了解到
AlertController可能是担任组织数据逻辑的作用。

那我们就进一步的看看

往下阅读它的源码,AlertDialogonCreate中与AlertController建立了联系,从它的方法命名installContent()可以了解它是初始化AlertDialog内容实体的,那我们可以确定刚刚的推测,AlertController是负责AlertDialog组织内容逻辑的,而AlertDialog只是简单的”UI层“。

再往下就是AlertDialog中静态内部类,也是我们要说的重点Builder

Builder类中发现了一个眼熟的东西——AlertController.AlertParams,从它的命名(命名果然好重要)可以发现它可能是逻辑类,也就是”模型层“(是不是有点像MVC = =),负责处理数据逻辑的。

再看一看源码

跟推测的一样,直接把数据赋予给了AlertController.AlertParams,而且都是return Builder 对象,保证了链式调用;源码里面大部分都是类似的代码,这里只贴出部分。

在源码的最后,发现了重点

create()方法中的apply()是将AlertDialog中的AlertControllerAlertController.AlertParams建立联系,其实就是控制层与逻辑层相通,最后会由
AlertController控制要显示的视图内容。

AlertDialog的源码基本上我们过了一遍,了解它的模式思路,那我们再从apply()进去,看看AlertControllerAlertController.AlertParams是怎么建立联系的。

2.AlertController.AlertParams源码

从代码中可以看见,AlertController获得了AlertController.AlertParams中保存的数据,其他代码不在详述,无非就是转换数据,最后还是要赋予AlertController

3.AlertController 源码

在构造方法中获得相对应的视图。

这是之前介绍过的初始化实体内容的方法,显然是负责构建视图的。

构建视图的过程,通过获得的数据构建相应的视图内容。
所以说AlertController是整个模式中负责组织建造的,这也是Builder模式的核心。

通过分析AlertDialog的源码,我们了解谷歌原生组件的Builder的模式,通过分层将逻辑简化,代码简洁。

Builder模式 实践

根据以上,我使用Builder模式重构了之前的小项目BottomPopUpDialog

BottomPopUpDialog


public class BottomPopUpDialog extends DialogFragment {


    private TextView mCancel;

    private LinearLayout mContentLayout;

    private Builder mBuilder;

    private static BottomPopUpDialog getInstance(Builder builder) {
        BottomPopUpDialog dialog = new BottomPopUpDialog();
        dialog.mBuilder = builder;
        return dialog;

    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //该方法需要放在onViewCreated比较合适, 若在 onStart 在部分机型(如:小米3)会出现闪烁的情况
        getDialog().getWindow().setBackgroundDrawableResource(mBuilder.mBackgroundShadowColor);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Holo_Light_NoActionBar);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.bottom_pop_up_dialog, null);
        initView(view);
        registerListener(view);
        setCancelable(true);
        return view;
    }


    private void initView(View view) {
        mContentLayout = (LinearLayout) view.findViewById(R.id.pop_dialog_content_layout);
        mCancel = (TextView) view.findViewById(R.id.cancel);
        initItemView();
    }


    private void registerListener(View view) {

        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    dismiss();
                }
                return false;
            }
        });

        mCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
    }


    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            super.show(manager, tag);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void initItemView() {
        //循环添加item
        for (int i = 0; i < mBuilder.mDataArray.length; i++) {
            final PopupDialogItem dialogItem = new PopupDialogItem(getContext());
            dialogItem.refreshData(mBuilder.mDataArray[i]);

            //最后一项隐藏分割线
            if (i == mBuilder.mDataArray.length - 1) {
                dialogItem.hideLine();
            }

            //设置字体颜色
            if (mBuilder.mColorArray.size() != 0 && mBuilder.mColorArray.get(i) != 0) {
                dialogItem.setTextColor(mBuilder.mColorArray.get(i));
            }

            if (mBuilder.mLineColor != 0) {
                dialogItem.setLineColor(mBuilder.mLineColor);
            }

            mContentLayout.addView(dialogItem);

            dialogItem.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mBuilder.mListener.onDialogClick(dialogItem.getItemContent());
                    if (mBuilder.mIsCallBackDismiss) dismiss();
                }
            });
        }
    }



    public static class Builder {

        private String[] mDataArray;

        private SparseIntArray mColorArray = new SparseIntArray();

        private BottomPopDialogOnClickListener mListener;

        private int mLineColor;

        private boolean mIsCallBackDismiss = false;

        private int mBackgroundShadowColor = R.color.transparent_70;


        /**
         * 设置item数据
         */
        public Builder setDialogData(String[] dataArray) {
            mDataArray = dataArray;
            return this;
        }

        /**
         * 设置监听item监听器
         */
        public Builder setItemOnListener(BottomPopDialogOnClickListener listener) {
            mListener = listener;
            return this;
        }


        /**
         * 设置字体颜色
         *
         * @param index item的索引
         * @param color res color
         */
        public Builder setItemTextColor(int index, int color) {
            mColorArray.put(index, color);
            return this;
        }

        /**
         * 设置item分隔线颜色
         */
        public Builder setItemLineColor(int color) {
            mLineColor = color;
            return this;
        }

        /**
         * 设置是否点击回调取消dialog
         */
        public Builder setCallBackDismiss(boolean dismiss) {
            mIsCallBackDismiss = dismiss;
            return this;
        }


        /**
         * 设置dialog背景阴影颜色
         */
        public Builder setBackgroundShadowColor(int color) {
            mBackgroundShadowColor = color;
            return this;
        }


        public BottomPopUpDialog create() {
            return BottomPopUpDialog.getInstance(this);
        }


        public BottomPopUpDialog show(FragmentManager manager, String tag) {
            BottomPopUpDialog dialog = create();
            dialog.show(manager, tag);
            return dialog;
        }


    }


    public interface BottomPopDialogOnClickListener {
        /**
         * item点击事件回调
         *
         * @param tag item字符串 用于识别item
         */
        void onDialogClick(String tag);
    }

}



这是我结合前面的Builder模式写的BottomPopUpDialogBuildr模式是将一个对象的构建与显示分离,将不同的参数一个一个添加进去,也是对于外部隐藏实现细节,更是降低了耦合度,方便以后的自由扩展。还有,我的实现省去了Controller层代码,我把控制层和UI层放在一起,这样实现是为了简单,我觉得Controller层是在可以复用的场景下,使用起来更有价值,而小组件可以更简单的使用Builder模式。编程是简单实用

重构之后的调用


new BottomPopUpDialog.Builder()
      .setDialogData(getResources().getStringArray(R.array.popup_array))
      .setItemTextColor(2, R.color.colorAccent)
      .setItemTextColor(4, R.color.colorAccent)
      .setCallBackDismiss(true)
      .setItemLineColor(R.color.line_color)
      .setItemOnListener(new BottomPopUpDialog.BottomPopDialogOnClickListener() {
                                @Override
                                public void onDialogClick(String tag) {
                                    Snackbar.make(view, tag, Snackbar.LENGTH_LONG)
                                            .setAction("Action", null).show();
                                }
                            })
      .show(getSupportFragmentManager(), "tag");

最后

这是一个简单的编程模式,记录一下思路。

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

推荐阅读更多精彩内容