Dialog源码分析

article/2025/8/27 22:35:28

1, 概述

Dialog(对话框)不仅可以显示信息,还可以和Activity界面进行交互,这种交互是阻塞式的. 继承Dialog的类有好几种,主要以AlertDialog为例来分析一下具体的原理。

2 实现

Dialog依附于Activity来实现,一般在acitivty中显示,因为Dialog的交互是阻塞式的,所以最好另开一线程,

1,首先调用showDialog方法
showDialog(R.id.dialog_export_confirmation); 
// 因为一个activity中可以显示多个dialog,所以利用id对dialog进行标记。
2,在onCreateDialog方法中new一个dialog对象。
protected Dialog onCreateDialog(int id, Bundle bundle) {switch (id) {case R.id.dialog_export_confirmation: {return new AlertDialog.Builder(this).setTitle(R.string.confirm_export_title).setMessage(getString(R.string.confirm_export_message, mTargetFileName)).setPositiveButton(android.R.string.ok,this) .setNegativeButton(android.R.string.cancel, this).setOnCancelListener(this).create();}~~~
return super.onCreateDialog(id, bundle);
}
3,选择性的实现onPrepareDialog方法,
protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {if (id == R.id.dialog_export_confirmation) {((AlertDialog)dialog).setMessage(getString(R.string.confirm_export_message, mTargetFileName));} else {super.onPrepareDialog(id, dialog, args);}}4,实现2个接口,当然也可以在创建dialog时实现。
class ~~~Activity extends Activity implementsDialogInterface.OnClickListener, DialogInterface.OnCancelListener{public void onClick(DialogInterface dialog, int which) {~~~}@Overridepublic void onCancel(DialogInterface dialog) {~~~}

3 源码解析

3.1 对象创建

创建流程如下,


首先看Activity中的showDialog方法,

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

ManagedDialog数组是为了便于管理各个dialog,不用每次显示的时候就创建,这样可以节约资源,提高效率。

调用AlertDialog内部静态Builder的oncreate方法创建AlertDialog对象,

public AlertDialog create() {// Context has already been wrapped with the appropriate theme.final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);P.apply(dialog.mAlert);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;}

AlertDialog的构造方法如下,

AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,createContextThemeWrapper);mWindow.alwaysReadCloseOnTouchAttr();mAlert = new AlertController(getContext(), this, getWindow()); // 构造AlertController}

 父类Dialog的构造方法如下,

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {if (createContextThemeWrapper) {if (themeResId == 0) {final TypedValue outValue = new TypedValue();context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);themeResId = outValue.resourceId;}mContext = new ContextThemeWrapper(context, themeResId);} else {mContext = context;}mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);final Window w = new PhoneWindow(mContext); // 新建一个PhoneWindow对象。mWindow = w;w.setCallback(this);w.setOnWindowDismissedCallback(this);w.setWindowManager(mWindowManager, null, null);w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);}

3.2 显示


调用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;}mCanceled = false;if (!mCreated) { // 第一次为falsedispatchOnCreate(null); // 加载view}onStart();mDecor = mWindow.getDecorView();if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {final ApplicationInfo info = mContext.getApplicationInfo();mWindow.setDefaultIcon(info.icon);mWindow.setDefaultLogo(info.logo);mActionBar = new WindowDecorActionBar(this);}WindowManager.LayoutParams l = mWindow.getAttributes();// type和activity完全一样if ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {WindowManager.LayoutParams nl = new WindowManager.LayoutParams();nl.copyFrom(l);nl.softInputMode |=WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;l = nl;}try {mWindowManager.addView(mDecor, l); // 直接调用WindowManagerImpl的addView方法显示// 测量,确定位置,绘制ViewmShowing = true;sendShowMessage();} finally {}}
void dispatchOnCreate(Bundle savedInstanceState) {if (!mCreated) {onCreate(savedInstanceState);mCreated = true; // 这才将mCreated置为true,加载一次就不再加载了。}}
public void installContent() {/* We use a custom title so never request a window title */mWindow.requestFeature(Window.FEATURE_NO_TITLE);int contentView = selectContentView(); // 获取资源 mAlertDialogLayoutmWindow.setContentView(contentView); // 加载资源setupView();setupDecor();
}

在AlertController的构造方法中为mAlertDialogLayout赋值,

public AlertController(Context context, DialogInterface di, Window window) {mContext = context;mDialogInterface = di;mWindow = window;mHandler = new ButtonHandler(di);TypedArray a = context.obtainStyledAttributes(null,com.android.internal.R.styleable.AlertDialog,com.android.internal.R.attr.alertDialogStyle, 0);mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout,com.android.internal.R.layout.alert_dialog);mButtonPanelSideLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0);mListLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_listLayout,com.android.internal.R.layout.select_dialog);mMultiChoiceItemLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout,com.android.internal.R.layout.select_dialog_multichoice);mSingleChoiceItemLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,com.android.internal.R.layout.select_dialog_singlechoice);mListItemLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_listItemLayout,com.android.internal.R.layout.select_dialog_item);a.recycle();}

Dialog几乎和activity一样,都是调用WindowManagerImpl的setContentView方法来加载解析资源,利用addView方法测量,确定位置,绘制View,最后将Dialog显示出来。

3.3 单击事件

1,调用AlertDialog 内部类Builder 的setPositiveButton/ setNegativeButton方法设置监听事件,

public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {P.mPositiveButtonText = P.mContext.getText(textId);P.mPositiveButtonListener = listener; // 为mPositiveButtonListener赋值return this;}

P的定义如下,

private final AlertController.AlertParams P;
2 , 创建 AlertDialog



public void apply(AlertController dialog) {
•••
if (mPositiveButtonText != null) {dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,mPositiveButtonListener, null);}if (mNegativeButtonText != null) {dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,mNegativeButtonListener, null);}
••••
}

setButton方法构造单击事件的Message对象,

public void setButton(int whichButton, CharSequence text,DialogInterface.OnClickListener listener, Message msg) {if (msg == null && listener != null) {msg = mHandler.obtainMessage(whichButton, listener);}switch (whichButton) {case DialogInterface.BUTTON_POSITIVE:mButtonPositiveText = text;mButtonPositiveMessage = msg;break;case DialogInterface.BUTTON_NEGATIVE:mButtonNegativeText = text;mButtonNegativeMessage = msg;break;case DialogInterface.BUTTON_NEUTRAL:mButtonNeutralText = text;mButtonNeutralMessage = msg;break;default:throw new IllegalArgumentException("Button does not exist");}}

3,分发单击事件

AlertController的View.OnClickListener匿名内部类实现的onClick方法如下,

private final View.OnClickListener mButtonHandler = new View.OnClickListener() {@Overridepublic void onClick(View v) {final Message m;if (v == mButtonPositive && mButtonPositiveMessage != null) {m = Message.obtain(mButtonPositiveMessage);} else if (v == mButtonNegative && mButtonNegativeMessage != null) {m = Message.obtain(mButtonNegativeMessage);} else if (v == mButtonNeutral && mButtonNeutralMessage != null) {m = Message.obtain(mButtonNeutralMessage);} else {m = null;}if (m != null) {m.sendToTarget(); // 发送Message消息}// Post a message so we dismiss after the above handlers are executedmHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface).sendToTarget();}};

处理消息如下,

private static final class ButtonHandler extends Handler {// Button clicks have Message.what as the BUTTON{1,2,3} constantprivate static final int MSG_DISMISS_DIALOG = 1;private WeakReference<DialogInterface> mDialog;public ButtonHandler(DialogInterface dialog) {mDialog = new WeakReference<DialogInterface>(dialog);}@Overridepublic 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(); // 关闭Dialog}}}

调用DialogInterface.OnClickListener的onClick方法。

3.4 关闭

Acitivity中有个dismissDialog方法,来关闭dialog,

public final void dismissDialog(int id) {if (mManagedDialogs == null) {throw missingDialog(id);}final ManagedDialog md = mManagedDialogs.get(id);if (md == null) {throw missingDialog(id);}md.mDialog.dismiss();}
public void dismiss() {if (Looper.myLooper() == mHandler.getLooper()) {dismissDialog();} else {mHandler.post(mDismissAction);}}void dismissDialog() {if (mDecor == null || !mShowing) {return;}if (mWindow.isDestroyed()) {Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");return;}try {mWindowManager.removeViewImmediate(mDecor); // 直接移除view} finally {if (mActionMode != null) {mActionMode.finish();}mDecor = null;mWindow.closeAllPanels();onStop();mShowing = false;sendDismissMessage();}}

当然,可以直接调用dialog的cancel方法来关闭。

4 Dialog

当然,除了系统的Dialog风格之外,也可以自己定义dialog,可以通过set~~~方法来实现,

setContentView

设置自定义View

setTitle

设置标题

~~~

 

show

显示

cancel

关闭

 
















http://chatgpt.dhexx.cn/article/9oWux13o.shtml

相关文章

爬虫为什么会使用到HTTP代理?

在进行网页爬虫的时候使用HTTP代理&#xff0c;可以进行匿名抓取网页信息&#xff0c;爬取大数据等使用方向。HTTP代理我们很了解&#xff0c;但是你有了解过HTTP协议是什么吗&#xff1f; HTTP协议即超文本传输协议&#xff0c;是Internet上信息传输时使用最为广泛的一种简单…

爬虫笔记——全局代理下的requests写法

当PC挂了全局代理的时候&#xff0c;如果你的requests还像下面写的话&#xff0c;大概率会报错&#xff1a; 正确的写法是&#xff1a;

抓包:Android不走代理的请求

测试用例 测试应用有两个按钮&#xff0c;分别用 HttpURLConnection 和 Okhttp3 请求 https://www.baidu.com/。注意&#xff1a;两个请求都加入了 Proxy.NO_PROXY。 //HttpURLConnection请求https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel13812371237 public …

python爬虫——selenium+chrome使用代理

先看下本文中的知识点&#xff1a; python selenium库安装chrome webdirver的下载安装seleniumchrome使用代理进阶学习 搭建开发环境&#xff1a; selenium库chrome webdirver谷歌浏览器 >7.9 PS&#xff1a;安装了的同学可以跳过了接着下一步&#xff0c;没安装的同学跟…

python 爬虫 客户端_python爬虫

爬虫简介 什么是爬虫&#xff1f; 爬虫&#xff1a;就是抓取网页数据的程序。 HTTP和HTTPS HTTP协议(HyperText Transfer Protocol&#xff0c;超文本传输协议)&#xff1a;是一种发布和接收 HTML页面的方法。 HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)简单…

爬虫从入门到精通(7) | 常见反爬-代理IP的使用

目录 一、 为什么要使用代理IP&#xff1f; 二、代理IP的原理 三、代理IP的作用 四、代理IP的分类 ​ 1.根据代理的协议区分 2.根据匿名程度区分 五、在requests模块中如何设置代理 一、 为什么要使用代理IP&#xff1f; 使用自己本地的IP 利用爬虫技术获取某个网站信息的时…

爬虫手册06 代理池和代理服务器搭建

免费代理池的搭建 参考资料&#xff1a;Python3 网络爬虫开发实战&#xff08;第二版&#xff09; 一. 学习目标 &#xff1a; 参考《第9章 代理的使用》9.2节搭建自己私有的代理IP池。 二. 实验环境&#xff1a; 阿里云或腾讯云的服务器&#xff0c;我是在打折和新用户优…

我裂开了,教给他如何搭建和使用代理服务器,他居然用来做这么不正经的事(爬虫,代理ip)

代码是正经代码&#xff0c;但是程序员正不正经就不知道了。 ​ 前言 在使用爬虫对某些网站进行爬取时&#xff0c;为了不让网站发现我们的ip&#xff0c;模拟其他用户ip地址去访问网站。也就相当于间接的去访问网站&#xff0c;流程如图&#xff1a; 我们使用到代理服务器&…

Python爬虫进阶 - win和linux下selenium使用代理

目录 Windows selenium配置 下载地址 Chrome Chromedriver 版本对应关系 实践测试 操作元素 浏览器操作 获取元素信息 鼠标操作 实战demo selenium添加代理 Linux selenium配置 检查服务器环境 下载安装第三方库&#xff08;最简单版&#xff09; 实践测试 代码…

Python自助爬虫代理ip模块

短小无比的前言&#xff1a; 代理对于爬虫来说可是很重要的一环&#xff0c;尤其在对于大量数据的时候&#xff0c;一不小心自己ip挂了&#xff0c;要么你换网&#xff0c;要么你等个几小时恢复 之后你上网查阅了种种办法&#xff0c;跨越种种艰难险阻&#xff0c;数以堆计的bu…

python爬虫之requests库使用代理方式

在看这篇文章之前&#xff0c;需要大家掌握的知识技能&#xff1a; python基础html基础http状态码 让我们看看这篇文章中有哪些知识点&#xff1a; get方法post方法header参数&#xff0c;模拟用户data参数&#xff0c;提交数据proxies参数&#xff0c;使用代理进阶学习 安装…

爬虫代理IP哪家好?

前言 随着大数据时代的到来,爬虫已经成了获取数据的必不可少的方式,做过爬虫的想必都深有体会,爬取的时候莫名其妙 IP 就被网站封掉了,毕竟各大网站也不想自己的数据被轻易地爬走。 对于爬虫来说,为了解决封禁 IP 的问题,一个有效的方式就是使用代理,使用代理之后可以让…

Python爬虫基础-使用代理

为什么需要代理&#xff1f; 我们爬取数据的时候&#xff0c;开始可以正常爬取&#xff0c;但是过了一段时间&#xff0c;网站可能就会提示“您的IP访问频率过高”&#xff0c;然后就无法正常访问网站。 这是因为网站采取了反爬策略&#xff0c;某个ip访问频率超过一个阈值后&…

Python爬取代理IP

在一些网页的内容爬取过程中&#xff0c;有时候在单位时间内如果我们发送的请求次数过多&#xff0c;网站就可能会封掉我们的IP地址&#xff0c;这时候为了保证我们的爬虫的正常运行&#xff0c;我们就要使用代理IP。 下面来介绍如何构建自己的IP池。 我们用快代理来获取代理i…

流量数据分析的方法学习

1、看数字和趋势&#xff08;以电商网站为例&#xff09; 2、维度分解 3、用户分群&#xff08;又叫用户画像&#xff09; 4、转化漏斗 5、行为轨迹 关注行为轨迹&#xff0c;是为了真实了解用户行为。通过大数据手段&#xff0c;还原用户的行为轨迹&#xff0c;有助于增长团队…

恶意流量分析(一)

在分析恶意样本时&#xff0c;样本可能包含网络行为&#xff0c;比如样本从C2服务器上请求下载后续病毒文件。所以对于在病毒分析的角度&#xff0c;对恶意流量的分析也是不可避免的。在这里通过恶意流量习题&#xff08;malware-traffic-analysis&#xff09;对恶意流量进行入…

APP流量分析

分析参数 发送流量、接收流量 流量统计方法 抓包&#xff1a;干扰因素多&#xff0c;其他APP、安卓自带TCP收发长度统计功能 #找到包名 adb shell pm list packsges -3 #找到UID adb shell ps | grep <包名> #找到目录 adb shell cat /proc/9045<uid>/status #…

如何进行网站流量分析(一)

如何进行网站流量分析&#xff08;一&#xff09; 流量分析整体来说是一个内涵非常丰富的体系&#xff0c;整体过程是一个金字塔结构&#xff1a; 金字塔的顶部是网站的目标&#xff1a;投资回报率&#xff08;ROI&#xff09;。 网站流量分析模型举例 1 网站流量质量分析&a…

流量与日志分析

文章目录 1.流量与日志分析1.1系统日志分析1.1.1window系统日志与分析方法1.1.2linux 系统日志与分析方法 1.2 web日志分析iis 日志分析方法apache日志分析**access_log****error_log** nginx日志分析tomcat 日志分析主流日志分析工具使用 1.流量与日志分析 日志&#xff0c;是…

攻防世界—流量分析1

题目&#xff1a;流量分析&#xff0c;你知道这堆流量做了什么事情吗&#xff0c;你能恢复出来flag吗&#xff1f; 拿到附件以后&#xff0c;解压出一个抓包文件&#xff0c;通过wireshark打开&#xff0c;发现大量HTTP报文。 随机查看一个HTTP访问的URL&#xff0c;经过URL解…