从源码看 AlertDialog.getButton(DialogInterface.BUTTON_POSITIVE) 为什么是 null
我们在使用 AlertDialog
的时候,如果想改变 POSITIVE_BUTTON
或者 NEGATIVE_BUTTON
的字体颜色、大小时,可能会注意到 AlertDialog.getButton(DialogInterface.BUTTON_POSITIVE)
这个方法,但是当我们在创建了AlertDialog
,想要拿到这 Button
的时候会发现返回的结果是个null
。
尝试之后会发现,在AlertDialog
的show()
执行过之后,这个Button
返回的就不再是null
了,此时就可以对Button
的属性进行设置:
final AlertDialog alertDialog = new AlertDialog.Builder(this).setTitle("Title").setMessage("meaasge").setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}}).setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}}).create();alertDialog.show();Button button = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);button.setTextColor(Color.parseColor("#00c8aa"));button.setTextSize(10);
或者在onShow(DialogInterface dialog)
的回调中设置:
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {@Overridepublic void onShow(DialogInterface dialog) {Button button = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);button.setTextColor(Color.parseColor("#00c8aa"));button.setTextSize(10);}});
alertDialog.show();
接下来我们就看一下为什么AlertDialog.Builder.create()
已经构建了一个AlertDialog
对象,再去getButton(int which)
会是null
?
有了AlertDialog
实体而getButton(int which)
为null
,那我们就从getButton(int which)
入手看一下源码的处理。点进去getButton(int which)
我们会发现返回的是AlertController
类中的成员变量 Button mButtonPositive
:
//AlertDialog中的getButton(int which)
public Button getButton(int whichButton) {return mAlert.getButton(whichButton);
}//AlertController类中的getButton(int which)
public Button getButton(int whichButton) {switch (whichButton) {case DialogInterface.BUTTON_POSITIVE:return mButtonPositive;case DialogInterface.BUTTON_NEGATIVE:return mButtonNegative;case DialogInterface.BUTTON_NEUTRAL:return mButtonNeutral;default:return null;}
}
AlertController
,这个类几乎包含了AlertDialog
所有的属性。返回的button
是null
,说明这个button
还没有初始化,我们来看一下这个button
是在哪里被初始化的:
private void setupButtons(ViewGroup buttonPanel) {int BIT_BUTTON_POSITIVE = 1;int BIT_BUTTON_NEGATIVE = 2;int BIT_BUTTON_NEUTRAL = 4;int whichButtons = 0;mButtonPositive = (Button) buttonPanel.findViewById(android.R.id.button1);mButtonPositive.setOnClickListener(mButtonHandler);if (TextUtils.isEmpty(mButtonPositiveText)) {mButtonPositive.setVisibility(View.GONE);} else {mButtonPositive.setText(mButtonPositiveText);mButtonPositive.setVisibility(View.VISIBLE);whichButtons = whichButtons | BIT_BUTTON_POSITIVE;}mButtonNegative = (Button) buttonPanel.findViewById(android.R.id.button2);mButtonNegative.setOnClickListener(mButtonHandler);}
button
在AlertController
类的setupButtons(ViewGroup buttonPanel)
方法中被初始化,通过追踪,setupButtons(ViewGroup buttonPanel)
方法在setupView()
中被执行,setupView()
在installContent()
中被执行:
public void installContent() {final int contentView = selectContentView();mDialog.setContentView(contentView);setupView();}private void setupView() {final View parentPanel = mWindow.findViewById(R.id.parentPanel);final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);setupCustomContent(customPanel);final View customTopPanel = customPanel.findViewById(R.id.topPanel);final View customContentPanel = customPanel.findViewById(R.id.contentPanel);final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);setupContent(contentPanel);setupButtons(buttonPanel);setupTitle(topPanel);
}
这个installContent()
方法是公有的且在AlertController
中没有被执行,那必定是在其它类中被AlertController
的实例执行了,AlertController
类是AlertDialog
的一个辅助类且AlertDialog
也持有一个AlertController
类的实例,所以我们在AlertController
类中找这个installContent()
方法:
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mAlert.installContent();
}
通过查找,在AlertDialog
中的onCreate(Bundle savedInstanceState)
方法中找到了这个方法。onCreate(Bundle savedInstanceState)
方法是重写了父类Dialog
的方法,那具体的执行过程就要去Dialog
类中找了。
在Dialog
类中能看到onCreate(Bundle savedInstanceState)
在dispatchOnCreate(Bundle savedInstanceState)
中执行:
void dispatchOnCreate(Bundle savedInstanceState) {if (!mCreated) {onCreate(savedInstanceState);mCreated = true;}
}
dispatchOnCreate(Bundle savedInstanceState)
方法在Dialog
类中被多次执行,而与AlertDialog
直接相关的则是show()
方法:
public void show() {if (!mCreated) {dispatchOnCreate(null);} else {final Configuration config = mContext.getResources().getConfiguration();mWindow.getDecorView().dispatchConfigurationChanged(config);}onStart();mDecor = mWindow.getDecorView();WindowManager.LayoutParams l = mWindow.getAttributes();mWindowManager.addView(mDecor, l);mShowing = true;sendShowMessage();}
简化之后的show()
方法,mCreated
是一个布尔变量,标记是否被创建,首次调用show()
时,mCreated
为false
,会执行dispatchOnCreate(null)
,就会执行onCreate(Bundle savedInstanceState)
。到此,为什么AlertDialog.getButton(DialogInterface.BUTTON_POSITIVE)
为null
,就很清楚了。AlertDialog
的逻辑是show()
方法中初始化了AlertDialog
上面的控件,然后展示出来,所以在show()
方法执行之后才能得到button
。
那我们也来看一下AlertDialog.Builder.create()
中都干了什么:
public AlertDialog create() {final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);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
的构造方法中初始化了AlertController mAlert
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {super(context, resolveDialogTheme(context, themeResId));mAlert = new AlertController(getContext(), this, getWindow());
}
在AlertController
的构造方法中也只是传参和初始化布局id
,并没有做初始化控件的操作。
public AlertController(Context context, AppCompatDialog di, Window window) {mContext = context;mDialog = di;mWindow = window;mHandler = new ButtonHandler(di);final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,R.attr.alertDialogStyle, 0);mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);mSingleChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);a.recycle();di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);}
所以在AlertDialog.Builder.create()
之后是得不到具体的控件实例的。
在show()
之后我们能得到Button
,那我们能不能得到dialog
上面其他的控件呢?
我们看AlertController
的源码会发现,有多处使用findViewById()
这个方法,使用的是mWindow
,比如:
private void setupView() {final View parentPanel = mWindow.findViewById(R.id.parentPanel);final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);setupCustomContent(customPanel);final View customTopPanel = customPanel.findViewById(R.id.topPanel);final View customContentPanel = customPanel.findViewById(R.id.contentPanel);final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);setupContent(contentPanel);setupButtons(buttonPanel);setupTitle(topPanel);}
这个mWindow
变量是通过构造方法传进来的,我们在看下AlertDialog
的构造方法:
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {super(context, resolveDialogTheme(context, themeResId));mAlert = new AlertController(getContext(), this, getWindow());
}
mWindow
是Dialog
的getWindow()
返回的对象,查看Dialog
的构造方法,会发现这个mWindow
是一个PhoneWindow
实例:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);final Window w = new PhoneWindow(mContext);mWindow = w;w.setCallback(this);w.setOnWindowDismissedCallback(this);w.setWindowManager(mWindowManager, null, null);w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);}public @Nullable Window getWindow() {return mWindow;}
源码使用Window.findViewById(int id)
,那我们应该也能通过获取Dialog
的Window
来获取其他控件。利用这个方法得到AlertDialog
上面的控件来设置各种属性,包括title
、message
都可以设置颜色、大小等属性,可以看下我的上篇博客 Builder模式设置AlertDialog字体大小、颜色等属性
回顾一下AlertDialog的创建流程:
首先,通过AlertDialog
的内部类Builder
,设置了各种属性,然后通过create()
方法实例化了AlertDialog
,在这个过程中创建了Dialog
的PhoneWindow
,再然后是执行show()
方法,在这个方法中,执行了onCreate(Bundle savedInstanceState)
方法,接着是执行AlertController.installContent()
,在这个方法中初始化了各种控件,并设置属性。最后才是show()
方法中的WindowManager.addView(PhoneWindow.getDecorView, WindowManager.LayoutParams)
展示出来。
看一下AlertController.installContent()
这个方法:
public void installContent() {final int contentView = selectContentView();mDialog.setContentView(contentView);setupView();}
Dialog
类中的setContentView(contentView)
方法:
public void setContentView(@LayoutRes int layoutResID) {mWindow.setContentView(layoutResID);
}
所以在mDialog.setContentView(contentView)
这句话的执行之后才能在后面使用mWindow.findViewById(int id)
,也就是这样我们才能通过Dialog.getWindow().findViewById(int id)
得到具体的控件。
从源码看 AlertDialog.getButton(DialogInterface.BUTTON_POSITIVE) 为什么是 null相关推荐
- linux内核第一个函数,通过内核源码看函数调用之前世今生 - 极光 - CSDN博客
通过内核源码看函数调用之前世今生 作者:杨小华 栈(Stack):一个有序的积累或堆积 韦氏词典 对每一位孜孜不倦的程序员来说,栈已深深的烙在其脑海中,甚至已经发生变异.栈可以用来传递函数参数.存储局 ...
- codeblock socket 编译错误_从Linux源码看Socket(TCP)Client端的Connect
从Linux源码看Socket(TCP)Client端的Connect 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的 ...
- linux内核线程socket,从Linux源码看Socket(TCP)的accept
从Linux源码看Socket(TCP)的accept 前言 笔者一直以为若是能知道从应用到框架再到操做系统的每一处代码,是一件Exciting的事情. 今天笔者就从Linux源码的角度看下Serve ...
- linux源码_从linux源码看epoll及epoll实战揭秘
从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...
- 从 Linux 源码看 Socket 的阻塞和非阻塞
转载自 从 Linux 源码看 Socket 的阻塞和非阻塞 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 大部分高性能网络框架采用的是非阻塞模式.笔者这 ...
- java linux 调用32位so_从linux源码看socket(tcp)的timeout
从linux源码看socket(tcp)的timeout 前言 网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌.在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp ...
- addeventlistener事件参数_从Chrome源码看浏览器的事件机制
在上一篇<从Chrome源码看浏览器如何构建DOM树>介绍了blink如何创建一棵DOM树,在这一篇将介绍事件机制. 上一篇还有一个地方未提及,那就是在构建完DOM之后,浏览器将会触发DO ...
- ip受限 linux_从linux源码看epoll及epoll实战揭秘
从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...
- 从JDK源码看关闭钩子
关闭钩子 Java提供了Shutdown Hook机制,它让我们在程序正常退出或者发生异常时能有机会做一些清场工作.使用的方法也很简单,Java.Runtime.addShutdownHook(Thr ...
最新文章
- PTA 03-树1 树的同构 (25分)
- [51nod1376] 最长递增子序列的数量
- javascript中的setTimeout() 方法和clearInterval() 方法和setInterval() 方法
- 激活函数:sigmoid、Tanh、ReLU
- 【数学】礼物(jzoj 2129)
- leetcode 341. 扁平化嵌套列表迭代器(dfs)
- 大数据怎样帮助运维工程师实现无死角监控?
- 重庆中职高考计算机专业试题,职业高中高考计算机专业试卷3答案
- English Learning from research paper
- Spring3.0_调试错误集
- 如果发现CSDN资源有问题,可以进行举报
- python分析财务报表
- ArduinoUNO实战-第五章-有源蜂鸣器实验
- 用MATLAB敲qda,LOMO_XQDA
- 谷歌浏览器android插件开发工具,ARC Welder:在谷歌浏览器运行安卓APK
- 计算机硬盘管理 3t,为什么劝你3T硬盘也别分区?我来治好你的强迫症
- 【计算机组成原理-chapter1】计算机组成原理概述
- [日常训练]article
- Waymo获得加州首个完全自动驾驶汽车许可证
- host切换工具——SwitchHosts
热门文章
- 仿B站简单版播放器带有弹幕,并支持解析
- 2023热门抖音权重查询小程序源码
- c/c++ float和double的表示范围详细解析
- MongoDB集群相关操作命令
- 火影几点服务器维护完,火影忍者手游7.8更新到几点?7月8日停机更新内容一览...
- 3d渲染时显示计算机渲染过程,【技巧分享】如何解决3d渲染时出现错误
- C# 倍福ADS带数组成员的结构体数组通信
- Cesium+Echarts 人员迁徙
- python爬取网店数据_Python爬虫实现抓取京东店铺信息及下载图片功能示例
- 嘻嘛香—冷知识!在兰州根本没有拉面!