在昨天的每日一问中, 我们有这么一行代码:
private void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Title")
.setMessage("Message")
.setPositiveButton("OK", (dialog, which) -> Toast.makeText(this, "OK", Toast.LENGTH_SHORT).show())
.setNegativeButton("Cancel", (dialog, which) -> Toast.makeText(this, "Cancel", Toast.LENGTH_SHORT).show())
.setNeutralButton("Emmm", (dialog, which) -> Toast.makeText(this, "Emmm", Toast.LENGTH_SHORT).show());
AlertDialog dialog = builder.create();
dialog.show();
//必须先调用show才能getButton(), 否则会空指针异常
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(getResources().getColor(R.color.colorPrimary));
dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.colorPrimaryDark));
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.colorAccent));
}
那么, 为什么要先调用 show()
方法才能 getButton()
呢, 我们看下 getButton()
源码:
//android.support.v7.app.AlertDialog#getButton
public Button getButton(int whichButton) {
return this.mAlert.getButton(whichButton);
}
//android.support.v7.app.AlertController#getButton
public Button getButton(int whichButton) {
switch(whichButton) {
case -3:
return this.mButtonNeutral;
case -2:
return this.mButtonNegative;
case -1:
return this.mButtonPositive;
default:
return null;
}
}
//android.support.v7.app.AlertController#mButtonPositive
//android.support.v7.app.AlertController#mButtonNegative
//android.support.v7.app.AlertController#mButtonNeutral
Button mButtonPositive;
Button mButtonNegative;
Button mButtonNeutral;
这里可以看出是获取的 AlertController
的 Button, 这三个Button 什么时候初始化的呢?
//android.support.v7.app.AlertController#setupButtons
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);
if (TextUtils.isEmpty(mButtonNegativeText)) {
mButtonNegative.setVisibility(View.GONE);
} else {
mButtonNegative.setText(mButtonNegativeText);
mButtonNegative.setVisibility(View.VISIBLE);
whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
}
mButtonNeutral = (Button) buttonPanel.findViewById(android.R.id.button3);
mButtonNeutral.setOnClickListener(mButtonHandler);
if (TextUtils.isEmpty(mButtonNeutralText)) {
mButtonNeutral.setVisibility(View.GONE);
} else {
mButtonNeutral.setText(mButtonNeutralText);
mButtonNeutral.setVisibility(View.VISIBLE);
whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
}
if (shouldCenterSingleButton(mContext)) {
/*
* If we only have 1 button it should be centered on the layout and
* expand to fill 50% of the available space.
*/
if (whichButtons == BIT_BUTTON_POSITIVE) {
centerButton(mButtonPositive);
} else if (whichButtons == BIT_BUTTON_NEGATIVE) {
centerButton(mButtonNegative);
} else if (whichButtons == BIT_BUTTON_NEUTRAL) {
centerButton(mButtonNeutral);
}
}
final boolean hasButtons = whichButtons != 0;
if (!hasButtons) {
buttonPanel.setVisibility(View.GONE);
}
}
setupButtons
的调用处为:
private void setupView() {
//...
setupContent(contentPanel);
setupButtons(buttonPanel);
setupTitle(topPanel);
//....
}
setupView
的调用处为:
public void installContent() {
final int contentView = selectContentView();
mDialog.setContentView(contentView);
setupView();
}
//android.support.v7.app.AlertDialog#onCreate
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mAlert.installContent();
}
//android.app.Dialog#onCreate
protected void onCreate(Bundle savedInstanceState) {
}
//android.app.Dialog#dispatchOnCreate(Bundle savedInstanceState)
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true;
}
}
此处 dispatchOnCreate
有三处调用:
//android.app.Dialog#create()
public void create() {
if (!mCreated) {
dispatchOnCreate(null);
}
}
//android.app.Dialog#show()
public void show() {
//...
if (!mCreated) {
dispatchOnCreate(null);
} else {
// Fill the DecorView in on any configuration changes that
// may have occured while it was removed from the WindowManager.
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
//...
}
//android.app.Dialog#onRestoreInstanceState
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
if (dialogHierarchyState == null) {
// dialog has never been shown, or onCreated, nothing to restore.
return;
}
dispatchOnCreate(savedInstanceState);
mWindow.restoreHierarchyState(dialogHierarchyState);
if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
show();
}
}
以上代码说明, 只有在调用
- Dialog.create()
- Dialog.show()
- Dialog.onRestoreInstanceState()
时才会初始化 Button
现在回头看看我们创建 AlertDialog 过程:
- 创建 AlertDialog.Builder
- 调用buider.create()
我们看下这两步的源码:
//1. 创建 AlertDialog.Builder
public Builder(@NonNull Context context) {
this(context, AlertDialog.resolveDialogTheme(context, 0));
}
public Builder(@NonNull Context context, @StyleRes int themeResId) {
this.P = new AlertParams(new ContextThemeWrapper(context, AlertDialog.resolveDialogTheme(context, themeResId)));
this.mTheme = themeResId;
}
// 没有调用 create()
//2. 调用buider.create()
public AlertDialog create() {
AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
this.P.apply(dialog.mAlert);
dialog.setCancelable(this.P.mCancelable);
if (this.P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(this.P.mOnCancelListener);
dialog.setOnDismissListener(this.P.mOnDismissListener);
if (this.P.mOnKeyListener != null) {
dialog.setOnKeyListener(this.P.mOnKeyListener);
}
return dialog;
}
// 此步调用了 AlertDialog的构造方法, 查看构造方法和其父类方法, 并没有调用 create 方法.
那么, 按照最前面创建 AlertDialog 时, 必须先调用 show()
方法初始化 Button, 才能调用 AlertDialog 的 getButton()
方法, 否则会报返回 null, 如果没有判 null 直接设置 Button, 就会引发空指针异常`
评论区