博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android进阶系列之源码分析AlertDialog建造者模式
阅读量:4166 次
发布时间:2019-05-26

本文共 12395 字,大约阅读时间需要 41 分钟。

 *本篇文章已授权微信公众号 hongyangAndroid (鸿洋)独家发布

建造者模式之前也写了一篇学习笔记,不过那只是很简单的运用,要去看源码,要去看源码还是得在撸一遍设计模式才行啊。不能怂就是干。

建造者模式,在于分工明确,一个抽象建造者,一个指挥者,一个具体的建造者,当然还需要具体的产品。那么我们以一个软件产品为例。技术主管就是抽象建造者,他和产品经理沟通,知道要做一个什么样的产品。而程序猿就是苦逼的体力劳动者,技术主管说咋做你就咋做。而指挥者就是公司的产品经理,负责和用户沟通,了解客户的需求。

那么基本的套路有了,开始撸一个简单的例子出来。

public class Product {    public static final int ANDROID = 0;    public static final int IOS = 1;    private String appName;    private String appFuction;    private int appSystem;    public String getAppName() {        return appName;    }    public void setAppName(String appName) {        this.appName = appName;    }    public String getAppFuction() {        return appFuction;    }    public void setAppFuction(String appFuction) {        this.appFuction = appFuction;    }    public int getAppSystem() {        return appSystem;    }    public void setAppSystem(int appSystem) {        this.appSystem = appSystem;    }}
这是产品,有app名称,app的功能,和app的操作系统。在来一个技术主管

/** * 技术主管 * Created by Administrator on 2016/12/29. */public abstract class TechManager {    public abstract TechManager setAppName(@NonNull String appName);    public abstract TechManager setAppFuction(@NonNull String appFuction);    public abstract TechManager setAppSystem(@AppSystem int appSystem);    public abstract Product build();}
在传入的系统时,我用了一个自定义注解,只能传入规定的参数,详情见:
/** *程序猿。IOS和ANDROID,后台都会的全栈选手 * Created by Administrator on 2016/12/29. */public class Progremer extends TechManager{    private Product product;    private InnerProduct innerProduct = new InnerProduct();    @Override    public TechManager setAppName(@NonNull String appName) {        innerProduct.setAppName(appName);        return this;    }    @Override    public TechManager setAppFuction(@NonNull String appFuction) {        innerProduct.setAppFuction(appFuction);        return this;    }    @Override    public TechManager setAppSystem(@AppSystem int appSystem) {        innerProduct.setAppSystem(appSystem);        return this;    }    private class InnerProduct{        private String appName;        private String appFuction;        private int appSystem;        public String getAppName() {            return appName;        }        public void setAppName(String appName) {            this.appName = appName;        }        public String getAppFuction() {            return appFuction;        }        public void setAppFuction(String appFuction) {            this.appFuction = appFuction;        }        public int getAppSystem() {            return appSystem;        }        public void setAppSystem(int appSystem) {            this.appSystem = appSystem;        }    }    @Override    public Product build() {        product = new Product();        product.setAppName(innerProduct.getAppName());        product.setAppFuction(innerProduct.getAppFuction());        product.setAppSystem(innerProduct.getAppSystem());        return product;    }}
这里采用了一下建造者模式的变异,这样其实更有利于使用。至于为什么,待会再产品经理那使用就知道了。

/** * 产品经理 * Created by Administrator on 2016/12/29. */public class ProductManager {    public static Product create(@AppSystem int system){      return new Progremer().setAppSystem(system).setAppName("探探").setAppFuction("划一划,找妹子。").build();    }}
在这里,采用链式调用,非常方便。这也是Android在源码经常使用的一种模式。

在客户类里,只需要持有产品经理类之后,就可以得到产品了。

public class Client {    public void main(String[] args){        //客户:我需要一个可以摇一摇找妹子的软件        //产品经理:分析得出那就做一个探探吧        //技术主管:appName:探探  系统:ios,android 功能:摇一摇,找妹子        Product android = ProductManager.create(Product.ANDROID);        Product ios = ProductManager.create(Product.IOS);    }}
建造者模式也就这么多了。其实变异来的建造者可以只需要具体建造者,抽象的不要了。指挥者也可以不要了。

public class Client {    public void main(String[] args){        //客户:我需要一个可以摇一摇找妹子的软件        //产品经理:分析得出那就做一个探探吧        //技术主管:appName:探探  系统:ios,android 功能:摇一摇,找妹子        Product android = ProductManager.create(Product.ANDROID);        Product ios = ProductManager.create(Product.IOS);        //程序猿觉得太累了,工资又少,干的最多。最后决定自己出去单干。        Progremer niubiProgremer = new Progremer();                Product androidBest = niubiProgremer.setAppName("探探").setAppSystem(Product.ANDROID).setAppFuction("摇一摇,找妹子").build();        Product iosBest = niubiProgremer.setAppName("探探").setAppSystem(Product.IOS).setAppFuction("摇一摇,找妹子").build();    }}
到这里,有没有觉得眼熟啊,AlertDialog调用也是这样一大串,链式调度。非常方便。那么我开始撸AlertDialog的源码吧。

Android源码这里我给个百度云的连接,大家需要的自行下载。链接: 密码:5mfz

只用AlertDialog的时候,都知道。

AlertDialog.Builder builder = new AlertDialog.Builder(this);
一看就知道创建AlertDialog时,需要通过内部类Builder来创建。那么我们来看一下Builder里有什么呢。AlertDailog就是建造者模式中指挥者的角色。

public static class Builder {        private final AlertController.AlertParams P;        private final int mTheme;               public Builder(@NonNull Context context) {            this(context, resolveDialogTheme(context, 0));        }               public Builder(@NonNull Context context, @StyleRes int themeResId) {            P = new AlertController.AlertParams(new ContextThemeWrapper(                    context, resolveDialogTheme(context, themeResId)));            mTheme = themeResId;        }        @NonNull        public Context getContext() {            return P.mContext;        }        public Builder setTitle(@StringRes int titleId) {            P.mTitle = P.mContext.getText(titleId);            return this;        }        public Builder setTitle(CharSequence title) {            P.mTitle = title;            return this;        }       ........             public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {            P.mPositiveButtonText = P.mContext.getText(textId);            P.mPositiveButtonListener = listener;            return this;        }                   public AlertDialog create() {                       final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);            P.apply(dialog.mAlert);//这里调用了apply真正创建了需要显示的dialog,也就是说之前的设置都是以P做一个数据缓存            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;        }              public AlertDialog show() {            final AlertDialog dialog = create();            dialog.show();            return dialog;        }    }
我把源码整理了一下,觉得有用的展示出来。在Builder创建了一个AlertController.AlertParams P;P是后面所有设置方法所调用的对象。那么我们再看看AlertController.AlertParams这个内部类有什么参数。

public static class AlertParams {        public final Context mContext;        public final LayoutInflater mInflater;        public int mIconId = 0;        public Drawable mIcon;        public int mIconAttrId = 0;        public CharSequence mTitle;        public View mCustomTitleView;        public CharSequence mMessage;        public CharSequence mPositiveButtonText;      	  ........               public AlertParams(Context context) {            mContext = context;            mCancelable = true;            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        }        public void apply(AlertController dialog) {//传入一个dialog,获取AlertParams缓存的数据。            if (mCustomTitleView != null) {                dialog.setCustomTitle(mCustomTitleView);            } else {                if (mTitle != null) {                    dialog.setTitle(mTitle);                }                if (mIcon != null) {                    dialog.setIcon(mIcon);                }                if (mIconId != 0) {                    dialog.setIcon(mIconId);                }                if (mIconAttrId != 0) {                    dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));                }            }            if (mMessage != null) {                dialog.setMessage(mMessage);            }            if (mPositiveButtonText != null) {                dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,                        mPositiveButtonListener, null);            }            if (mNegativeButtonText != null) {                dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,                        mNegativeButtonListener, null);            }            if (mNeutralButtonText != null) {                dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,                        mNeutralButtonListener, null);            }            // For a list, the client can either supply an array of items or an            // adapter or a cursor            if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {                createListView(dialog);            }            if (mView != null) {                if (mViewSpacingSpecified) {                    dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,                            mViewSpacingBottom);                } else {                    dialog.setView(mView);                }            } else if (mViewLayoutResId != 0) {                dialog.setView(mViewLayoutResId);            }            /*            dialog.setCancelable(mCancelable);            dialog.setOnCancelListener(mOnCancelListener);            if (mOnKeyListener != null) {                dialog.setOnKeyListener(mOnKeyListener);            }            */        }        ......    }

这里最主要的方法时apply(),在这里AlertParams 的所有属性转给了一个AlertController dialog。那么我们看看AlertController 是什么。

class AlertController {    private final Context mContext;    final AppCompatDialog mDialog;    private final Window mWindow;    private CharSequence mTitle;    private CharSequence mMessage;    ListView mListView;    private View mView;    private int mViewLayoutResId;    private int mViewSpacingLeft;    private int mViewSpacingTop;    private int mViewSpacingRight;    private int mViewSpacingBottom;    private boolean mViewSpacingSpecified = false;    Button mButtonPositive;    private CharSequence mButtonPositiveText;    Message mButtonPositiveMessage;    Button mButtonNegative;    private CharSequence mButtonNegativeText;    Message mButtonNegativeMessage;    Button mButtonNeutral;    private CharSequence mButtonNeutralText;    Message mButtonNeutralMessage;    NestedScrollView mScrollView;    private int mIconId = 0;    private Drawable mIcon;    private ImageView mIconView;    private TextView mTitleView;    private TextView mMessageView;    private View mCustomTitleView;    ListAdapter mAdapter;    int mCheckedItem = -1;    private int mAlertDialogLayout;    private int mButtonPanelSideLayout;    int mListLayout;    int mMultiChoiceItemLayout;    int mSingleChoiceItemLayout;    int mListItemLayout;    private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;    Handler mHandler;    ..........  }

 

AlertController.AlertParams 持有AlertController的所有属性,在调用builder里的设置属性方法时,就是给AlertController.AlertParams做一个缓存。在调用了builder 的show方法之后。里面在调用具体dialog的show方法显示弹窗。

那么AlertDialog在建造者模式中担任的是指挥者,Bilder就是具体的建造者。采用了链式调用。AlertController是产品,而AlertController.AlertParams是产品的缓存。比如我调用了两次setTitle(),在缓存时后一次会覆盖前一次,这样就解决了开发者冲动调用的问题。最后不论是调用Builder的show方法,还是调用调用AlertDialog的show方法。都是允许的。

比如:

new AlertDialog.Builder(this).setTitle("标题").setIcon(R.mipmap.ic_launcher).setMessage("测试").show();

这样是可以的,最后调用的是Builder的show方法,在Builder的show方法中,调用了AlertDialog的create()得到缓存数据的AlertDialog进行显示。

new AlertDialog.Builder(this).setTitle("标题").setIcon(R.mipmap.ic_launcher).setMessage("测试").create().show();

这样也是可以的,手动调用create()方法,之后在调用AlertDialog的show方法进行显示。

那么这样呢?

new AlertDialog.Builder(this).setTitle("标题").setIcon(R.mipmap.ic_launcher).setMessage("测试").show().show();

第一个show是builder的show,里面创建了AlertDialog并且调用了AlertDialog的show方法。那么第二个show也是AlertDialog的show方法,会重复调用么?答案肯定是不可能的。看看Dialog的show方法源码、

public void show() {        if (mShowing) {            if (mDecor != null) {                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);                }                mDecor.setVisibility(View.VISIBLE);            }            return;        }       .....        try {            mWindowManager.addView(mDecor, l);            mShowing = true;                sendShowMessage();        } finally {        }    }

在调用底层的show方法时,会先进行一次判断,第一次show之后mShowing已经设为true。那么第二次调用时,判断到已经显示,就不会再次调用绘制逻辑(省略号部分)。

那么建造者模式就到这儿了,源码的博大精深真是令人向往。

你可能感兴趣的文章
java中super 的两种用法
查看>>
bdb及其在php下扩展的安装
查看>>
bdb及其在php下扩展的安装
查看>>
android 小问题
查看>>
BerkeleyDB安装及配置
查看>>
标准的Activity Actions
查看>>
关于Android requires .class compatibility set to 5.0. Please fix project properties.的错误
查看>>
JAVA中implements实现多接口
查看>>
android中导入低版本project可能会遇到的编译问题
查看>>
showDialog
查看>>
Flex 拖拽范例
查看>>
flash builder 4 编译器参数
查看>>
包含库的问题
查看>>
flex 中 bindable的意义
查看>>
flex常用网站
查看>>
flex 页面跳转
查看>>
flex 3.0中关于两个.mxml文件之间链接的简单方法
查看>>
使用viewstack
查看>>
Flex 学习站点汇总
查看>>
12个flex常用代码
查看>>