修改AlertDialog的button的字体颜色
AlertDialog修改Button的字体颜色
去网上搜了一下,大部分是通过修改主题中的属性值。详见通过源码分析,修改AlertDialog按钮的颜色,看了之后,确实是一步一步查找,一步一步深入,找到了系统设置颜色的方法,也可实现需求。
如果用面向对象的思想 思考一下,应该可以.getButton()得到Button对象,然后,直接对这Button对象设置颜色属性值就好了。
结果如上图所示,直接GG了。并且通过debug我们发现,dialog.getButton() 获取到的button对象竟然为null,这个确实让我们很费解。
public Button getButton(int whichButton) {
return mAlert.getButton(whichButton);
}
我们看到getButton为null是mAlert.getButton()为null,这个mAlert是AlertController的实例。ok,找到原因之后,我们分析下源码吧。
首先57行之前的代码应该不用多说,是用Builder模式,给AlertDialog设置各种值。直接看下dialog=builder.create()
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
//new 一个AlertDialog对象
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;
}
大概做了两个事情,一个是new一个AlertDialog对象,然后,给dialog设置了各种事件的监听。我们开看下new AlertDialog的时候做了什么事情。
protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
this(context, 0);
setCancelable(cancelable);
setOnCancelListener(cancelListener);
}
protected AlertDialog(Context context, @StyleRes int themeResId) {
this(context, themeResId, true);
}
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = AlertController.create(getContext(), this, getWindow());
}
我们可以看到,AlertDialog的构造方法最终是调用了super.(xx,xx,xx)也就是他的父类Dialog的构造方法。我们接着看。
public Dialog(@NonNull Context context, @StyleRes int themeResId) {
this(context, themeResId, true);
}
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == ResourceId.ID_NULL) {
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);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
可以看到,重要的是先获取了一个WindowManager,然后new了一个phoneWindow。至此,dialog =builder.create()方法,重要的代码分析完毕。
接下来分析dialog.show()方法。
由于AlertDialog里面没有show()方法,我们直接去他的父类Dialog去看。
public void show() {
//mShowing这个我们知道,就是当前dialog是否显示出来了。现在还没出来,所以false
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;
//这个mCreated 默认是false。所以执行了if语句里面的代码。
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);
}
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();
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;
}
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
在源码的注释里面我们知道了,重点是dispatchOnCreate()方法。
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true;
}
}
protected void onCreate(Bundle savedInstanceState) {
}
在这个方法我们很清楚的看到,mCreate为false执行了onCreate()方法,然后设置mCreate为true。我们继续看onCreate()方法。发现Dialog的onCreate()方法是空方法。所以我们知道他肯定在AlertDialog里面重写了,我们接着看。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
public void installContent() {
final int contentView = selectContentView();
mDialog.setContentView(contentView);
setupView();
}
private int selectContentView() {
if (mButtonPanelSideLayout == 0) {
return mAlertDialogLayout;
}
if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
return mButtonPanelSideLayout;
}
return mAlertDialogLayout;
}
这个onCreate()方法调用了mAlert.installContent()方法。而且这个mAlert是AlertController的实例。所以我们去AlertController再找到installContent()源码。
selectContentView()方法返回的是加载AlertDialog需要的布局问价的Id,然后是调用了Window的setContentView(id)方法。这个方法我相信大家一定很熟悉。因为我们最长用的Activity就是这样创建布局的。
setupView()方法很长,大概就是初始化各种控件,其中就是有关于 button初始化的代码。
private void setupView() {
...
setupButtons(buttonPanel);
...
}
protected void setupButtons(ViewGroup buttonPanel) {
...
//给AlertController的mBbuttonPositive赋值
mButtonPositive = (Button) buttonPanel.findViewById(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;
}
//给AlertController的mButtonNegative 赋值
mButtonNegative = (Button) buttonPanel.findViewById(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; } //给AlertController的mButtonNeutral赋值 mButtonNeutral = (Button) buttonPanel.findViewById(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; } ... }至此,我们知道了,AlertController里面的button属性,是在AlertDialog的show()方法里面的dispatchOnCreate()方法里面完成的。所以,我们在show()方法之前通过getButton()获取的值必然是null。所以只需要这样修改我们的代码,就可以解决bug。
dialog.show();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.BLUE);
我们的源码也分析的差不多了,show()方法,通过dispatchOnCreate()方法初始化了各种控件和View只有,通过下面的代码把DecorView添加到WindowManager里面。
mWindowManager.addView(mDecor, l);
然后最后执行这行代码,把AlertDialog展示出来。这行代码实质也是通过Handler来发送一个消息,来展示AlertDialog。sendShowMessage();
通过源码来慢慢分析一个bug,不紧能让我们深入了解其实现原理,还能从根本上解决这个bug。所以学会看源码对于我们开发者来说,还是很重要的。
如果错误,欢迎指正。
下一篇: 图片轮播带小圆点选择左右切换
推荐阅读
-
Numpy进阶(一) — 修改图片中的指定颜色
-
Android TextView 在java代码中改变字体的颜色的方法
-
【UITextView】修改加载的html片段中的a标签颜色
-
css修改输入框的placeholder颜色_html/css_WEB-ITnose
-
iOS中 UIActionSheet字体的修改
-
UITableViewCell在编辑状态下背景颜色的修改方法
-
详解IOS 利用storyboard修改UITextField的placeholder文字颜色
-
UIWebView控件中字体大小和字体样式的修改
-
Android使用selector修改TextView中字体颜色和背景色的方法
-
iOS中Label实现显示不同颜色与字体的方法