[ApiDemos] AlertDialog 使用和源码分析
来源:互联网 发布:翟欣欣最新进展 知乎 编辑:程序博客网 时间:2024/06/08 05:54
本文主要讲解 ApiDemos 中的 AlertDialog 章,介绍 AlertDialog 的使用和部分方法实现原理。本文是 ApiDemos 分析系列的第一篇,也是笔者第一次尝试以自己的角度分析源码。
AlertDialogSamples 所在 ApiDemos 位置:
Java: src/com.example.android.apis/app/AlertDialogSamples.java
.
Xml: /res/any/layout/alert_dialog.xml
建造者模式
AlertDialog 最明显的特点就是使用了建造者模式,一般来说建造者模式适合于一个具有较多的零件(属性)的产品(对象)的创建过程。根据产品创建过程中零件的构造是否具有一致的先后顺序,可以将其分为”有设计者“ 和 ”无设计者“,两种形式。
UML图:
有设计者
在现实生活中,建造一个房子,但我们不知道怎么造,就要请负责总体设计的设计师和负责具体施工的工人,设计师只设计图纸、命令工人干活,不参与施工。工人负责具体细节(窗户、地板的构建)。最后,我们要从工人手中接过建造好的房子。
对建造者(工人)的规范:
package cn.house;public interface Builder { /** * 建造窗户 */ public void mkWindow(); /** * 建造房屋 */ public void mkFloor(); /** * 获取房间 */ public Room getRoom();}
实现了 Builder 接口的工人:
package cn.house;public class RoomBuilder implements Builder{ private Room room = new Room(); /** 具体创建窗户 */ public void mkWindow() { Window window = new Window(); room.setWindow(window); } /** 具体创建地板 */ public void mkFloor() { Floor floor = new Floor(); room.setFloor(floor); } /** 交付以创建好的房子 */ public Room getRoom() { return room; }}
设计师:
package cn.house;public class Designer { /** * 命令 Builder * @param builder */ public void command(Builder builder){ // 建造房屋 builder.mkWindow(); // 建造地板 builder.mkFloor(); }}
测试用例:
public static void main(String[] args) { Builder builder = new RoomBuilder(); Designer design = new Designer(); design.command(builder); Room room = builder.getRoom(); Window window = room.getWindow(); Floor floor = room.getFloor(); System.out.println(window); System.out.println(floor); }
无设计者
今天的主角 AlertDialog 就属于无设计者 的形式,下面是 AlertDialog 的简单模式:
package cn;public class AlertDialog { private String title; private String message; private int buttonCount; private AlertDialog() { // empty } /** 获取标题 */ public String getTitle() { return title; } /** 获取信息 */ public String getMessage() { return message; } /** 获取按钮数 */ public int getButtonCount() { return buttonCount; } /** 显示 */ public void show(){ System.out.println("show"); } /** 建造者 */ public static class Builder{ private AlertDialog entity = new AlertDialog(); public Builder(boolean isContext){ if (!isContext){ throw new RuntimeException("必须有上下文"); } } /** 设置标题 */ public Builder setTitle(String title) { entity.title = title; return this; } /** 设置内容 */ public Builder setMessage(String message) { entity.message = message; return this; } /** 设置按钮数 */ public Builder setButtonCount(int buttonCount) { entity.buttonCount = buttonCount; return this; } /** 交付结果 */ public AlertDialog build(){ return entity; } }}
测试用例:
public static void main(String[] args) { new AlertDialog.Builder(true) .setTitle("Title") .setMessage("Message") .setButtonCount(2) .build() .show();}
可以看出,AlertDialog 直接命令 Builder ,并没有涉及到 Designer,所以它是无序的。
showDialog 显示对话框的原理
showDialog 是什么,它是 Activity 提供的显示 Dialog 的简便方法,封装在 Activity 中。showDialog() 方法 Api level 1 中被添加, Api level 13(Honeycomb 3.0)被废弃。
我们可以推测:
1. AlertDialog 是 Activity 的一部分
2. Android 代码逐渐朝着低耦合发展
简单用法
public class MainActivity extends Activity { protected static final int DIALOG_YES_NO_MESSAGE = 0; @Override @Deprecated protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_YES_NO_MESSAGE: return new AlertDialog.Builder(MainActivity.this) .setIconAttribute(android.R.attr.alertDialogIcon) .setTitle("标题") .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { /* User clicked OK so do some stuff */ } }) .setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { /* User clicked Cancel so do some stuff */ } }) .create(); default: return null; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_open_dialog).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showDialog(DIALOG_YES_NO_MESSAGE); } }); }}
原理分析
下图是调用过程图
如图所示,上一层为 Activity,下一层为自己定义的 Activity 的派生类。
其中:
第1步:给按钮设置点击事件,开始执行 showDialog(id) 方法
第3步:showDialog(id, args) 方法维护了一个 mManagedDialogs 缓存池。
public final boolean showDialog(int id, Bundle args) { if (mManagedDialogs == null) { mManagedDialogs = new SparseArray<ManagedDialog>(); } ManagedDialog md = mManagedDialogs.get(id); if (md == null) { md = new ManagedDialog(); md.mDialog = createDialog(id, null, args); if (md.mDialog == null) { return false; } mManagedDialogs.put(id, md); } md.mArgs = args; onPrepareDialog(id, md.mDialog, args); md.mDialog.show(); return true; }
每一个 Activity 都会创建一个 Dialog 缓存池,同一个 Activity 只创建一份不同 ID 类型的 Dialog,目的是提高效率。其中 onPrepareDialog(id, md.mDialog, args) 的作用是绑定 Dialog和Activity 。
也就是说,AndroidSDK 封装了 Dialog 对象的 维护 ,把 创建 和 使用 这两步交给使用者去实现。只有当使用者实现了这两部,整个流程才算完结。这无疑是高度耦合的。所以在高版本,这种做法已经被废弃。
AlertController 和 AlertParams
AlertController 和 AlertParams 这两个类是 AlertDialog 和核心,AlertDialog 的大部分功能都通过 AlertController 实现,而 AlertParams 包含了对话框所需的各种元素(Title、 Message、 CheckedItems、 是否多选、四个 Padding 、各种监听…)
由于这两个类是 Android 内部类,无法用 Eclipse 跟踪,所以用 source insight 打开,在 com.android.internal.app 包下。
按钮事件处理器: ButtonHandler
private static final class ButtonHandler extends Handler { // Button clicks have Message.what as the BUTTON{1,2,3} constant private static final int MSG_DISMISS_DIALOG = 1; // 静态内部类使用外部成员变量,一般使用弱引用 private WeakReference<DialogInterface> mDialog; public ButtonHandler(DialogInterface dialog) { mDialog = new WeakReference<DialogInterface>(dialog); } // 处理各种按钮事件(确定,取消),销毁 @Override public void handleMessage(Message msg) { switch (msg.what) { case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEUTRAL: ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what); break; case MSG_DISMISS_DIALOG: ((DialogInterface) msg.obj).dismiss(); } } }
以上可以看出:
1. Dialog 内部通过 Handler+Message 实现了事件的处理,
2. 内部类 ButtonHandler 持有外部对象成员 mDialog 的弱引用,以避免内存泄露。
AlertParams 设置 View 和 Title、Message 过程
public void apply(AlertController dialog) { 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 (mView != null) { if (mViewSpacingSpecified) { dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); } else { dialog.setView(mView); } } }
不论设置 Title 还是 Content 之前,都判断是否存在 CustomXXView (自定义的 View ),没有自定义 View 才进步设置 Title 、Message
其他方法均为设置背景、设置 ICON 以及各种解析资源。就不再一一列举。
setIconAttr 的原理分析
不知是否记得 AlertDialog 提供了 setIconAttr 方法方便设置对话框的图标。
示例代码如下:
protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_YES_NO_MESSAGE: return new AlertDialog.Builder(MainActivity.this) .setIconAttribute(android.R.attr.alertDialogIcon) .setTitle("标题") .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { /* User clicked OK so do some stuff */ } }) .setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { /* User clicked Cancel so do some stuff */ } }) .create(); default: return null; } }
Eclipse 中按住 Ctrl+左键 跟踪后发现,该属性定义在:
SDK_DIR/platforms/android-19/data/res/values/attrs.xml
内容为:
<attr name=”alertDialogIcon” format=”reference” />
表示该属性指向的内容为引用类型,可以是:图片、文本、XML 文件
跟踪后可看到:
public Builder setIconAttribute(int attrId) { TypedValue out = new TypedValue(); P.mContext.getTheme().resolveAttribute(attrId, out, true); P.mIconId = out.resourceId; return this;}
P.mContext 指向 Activity 实例
以上代码可知:
1. 该 AttrsID 和 Android 系统当前 Theme 下的图片资源存在映射关系。
2. ContextThemeWrapper 父类负责根据 AttrsID 找到当前 Theme 中对应的 ICON。
3. TypedValue 对象存放解析后的图片资源。
技巧总结:
SparseArray
showDialog() 使用维护 Dialog , 我们可以借鉴,并用在自己的项目中。
如:
1. BaseActivity 中使用 SparseArray 维护 Activity 集合,以便统一退出。
2. BaseViewHolder 中,用 SparseArray 保存 findViewById 的结果
低耦合的思想
在开发过程中,我们尽量降低组件之间的耦合度,方便代码的维护和升级。(需要不断的积累、总结)
参考
Java之建造者模式(Builder
Pattern)设计模式之建造者模式(Builder)
设计模式总结篇系列:建造者模式(Builder)
- [ApiDemos] AlertDialog 使用和源码分析
- AlertDialog源码分析
- AlertDialog源码分析
- Builder设计模式和AlertDialog的源码分析
- Android源码中的AlertDialog分析
- AlertDialog使用和自定义
- AlertDialog.Builder和Dialog分析
- AlertDialog源码分析(建造者模式)
- Android开发,源码分析Dialog/AlertDialog的dismiss()和hide()的区别
- ThreadLocal使用和源码分析
- greendao3 使用和源码分析
- NumberUtils源码分析和使用
- Optional源码分析和使用
- jedis源码分析和使用
- DialogFragment和AlertDialog的使用
- ApiDemos – BouncingBalls分析
- ApiDemos—FragmentCustomAnimations分析
- ApiDemos—FragmentCustomAnimations分析
- Android下微信、微博、qq、百度第三方登陆
- java导出excel表
- 使用Pyunit执行测试并生成HTML报告
- Android 平台语言对照表
- mac配置adb
- [ApiDemos] AlertDialog 使用和源码分析
- (js跨域)说说JSON和JSONP,也许你会豁然开朗,含jQuery用例
- BigDecimal不整除的一个异常java.lang.ArithmeticException
- CSS3阴影 box-shadow的使用和技巧总结
- 将一个字符串复制到另一个字符串(用指针)
- 如何理解和使用Java package包
- x265探索与研究(五):如何用VS调试x265?
- Python retrying模块
- 倒计时效果与Date对象