欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

修改AlertDialog的button的字体颜色

程序员文章站 2022-06-01 11:33:23
...

AlertDialog修改Button的字体颜色

去网上搜了一下,大部分是通过修改主题中的属性值。详见通过源码分析,修改AlertDialog按钮的颜色,看了之后,确实是一步一步查找,一步一步深入,找到了系统设置颜色的方法,也可实现需求。
如果用面向对象的思想 思考一下,应该可以.getButton()得到Button对象,然后,直接对这Button对象设置颜色属性值就好了。

修改AlertDialog的button的字体颜色
修改AlertDialog的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。所以学会看源码对于我们开发者来说,还是很重要的。

如果错误,欢迎指正。











相关标签: AlterDialog