我们在使用 AlertDialog 的时候,如果想改变 POSITIVE_BUTTON 或者 NEGATIVE_BUTTON 的字体颜色、大小时,可能会注意到 AlertDialog.getButton(DialogInterface.BUTTON_POSITIVE) 这个方法,但是当我们在创建了AlertDialog,想要拿到这 Button 的时候会发现返回的结果是个null
尝试之后会发现,在AlertDialogshow()执行过之后,这个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所有的属性。返回的buttonnull,说明这个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);}

buttonAlertController类的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()时,mCreatedfalse,会执行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());
}

mWindowDialoggetWindow()返回的对象,查看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),那我们应该也能通过获取DialogWindow来获取其他控件。利用这个方法得到AlertDialog上面的控件来设置各种属性,包括titlemessage都可以设置颜色、大小等属性,可以看下我的上篇博客 Builder模式设置AlertDialog字体大小、颜色等属性

回顾一下AlertDialog的创建流程:

首先,通过AlertDialog的内部类Builder,设置了各种属性,然后通过create()方法实例化了AlertDialog,在这个过程中创建了DialogPhoneWindow,再然后是执行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相关推荐

  1. linux内核第一个函数,通过内核源码看函数调用之前世今生 - 极光 - CSDN博客

    通过内核源码看函数调用之前世今生 作者:杨小华 栈(Stack):一个有序的积累或堆积 韦氏词典 对每一位孜孜不倦的程序员来说,栈已深深的烙在其脑海中,甚至已经发生变异.栈可以用来传递函数参数.存储局 ...

  2. codeblock socket 编译错误_从Linux源码看Socket(TCP)Client端的Connect

    从Linux源码看Socket(TCP)Client端的Connect 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的 ...

  3. linux内核线程socket,从Linux源码看Socket(TCP)的accept

    从Linux源码看Socket(TCP)的accept 前言 笔者一直以为若是能知道从应用到框架再到操做系统的每一处代码,是一件Exciting的事情. 今天笔者就从Linux源码的角度看下Serve ...

  4. linux源码_从linux源码看epoll及epoll实战揭秘

    从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...

  5. 从 Linux 源码看 Socket 的阻塞和非阻塞

    转载自 从 Linux 源码看 Socket 的阻塞和非阻塞 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 大部分高性能网络框架采用的是非阻塞模式.笔者这 ...

  6. java linux 调用32位so_从linux源码看socket(tcp)的timeout

    从linux源码看socket(tcp)的timeout 前言 网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌.在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp ...

  7. addeventlistener事件参数_从Chrome源码看浏览器的事件机制

    在上一篇<从Chrome源码看浏览器如何构建DOM树>介绍了blink如何创建一棵DOM树,在这一篇将介绍事件机制. 上一篇还有一个地方未提及,那就是在构建完DOM之后,浏览器将会触发DO ...

  8. ip受限 linux_从linux源码看epoll及epoll实战揭秘

    从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...

  9. 从JDK源码看关闭钩子

    关闭钩子 Java提供了Shutdown Hook机制,它让我们在程序正常退出或者发生异常时能有机会做一些清场工作.使用的方法也很简单,Java.Runtime.addShutdownHook(Thr ...

最新文章

  1. PTA 03-树1 树的同构 (25分)
  2. [51nod1376] 最长递增子序列的数量
  3. javascript中的setTimeout() 方法和clearInterval() 方法和setInterval() 方法
  4. 激活函数:sigmoid、Tanh、ReLU
  5. 【数学】礼物(jzoj 2129)
  6. leetcode 341. 扁平化嵌套列表迭代器(dfs)
  7. 大数据怎样帮助运维工程师实现无死角监控?
  8. 重庆中职高考计算机专业试题,职业高中高考计算机专业试卷3答案
  9. English Learning from research paper
  10. Spring3.0_调试错误集
  11. 如果发现CSDN资源有问题,可以进行举报
  12. python分析财务报表
  13. ArduinoUNO实战-第五章-有源蜂鸣器实验
  14. 用MATLAB敲qda,LOMO_XQDA
  15. 谷歌浏览器android插件开发工具,ARC Welder:在谷歌浏览器运行安卓APK
  16. 计算机硬盘管理 3t,为什么劝你3T硬盘也别分区?我来治好你的强迫症
  17. 【计算机组成原理-chapter1】计算机组成原理概述
  18. [日常训练]article
  19. Waymo获得加州首个完全自动驾驶汽车许可证
  20. host切换工具——SwitchHosts

热门文章

  1. 仿B站简单版播放器带有弹幕,并支持解析
  2. 2023热门抖音权重查询小程序源码
  3. c/c++ float和double的表示范围详细解析
  4. MongoDB集群相关操作命令
  5. 火影几点服务器维护完,火影忍者手游7.8更新到几点?7月8日停机更新内容一览...
  6. 3d渲染时显示计算机渲染过程,【技巧分享】如何解决3d渲染时出现错误
  7. C# 倍福ADS带数组成员的结构体数组通信
  8. Cesium+Echarts 人员迁徙
  9. python爬取网店数据_Python爬虫实现抓取京东店铺信息及下载图片功能示例
  10. 嘻嘛香—冷知识!在兰州根本没有拉面!