Android dialog boxes support custom titles, content, buttons, and click events, which are basically for our daily use. But sometimes we want to change the text of the dialog box, button color, etc., the system does not provide the corresponding method, normally only can customize the layout. Next, we introduce several ways to modify the Dialog style through source code parsing.

Dialog source code parsing

1.1 new AlertDialog. Builder (this). The create ()

    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        // Create an AlertController, which is code related to the Dialog layout
        mAlert = new AlertController(getContext(), this, getWindow());
    }

        @NonNull
        public AlertDialog create(a) {
            // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
            // so we always have to re-set the theme
            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;
        }

        public void apply(AlertController dialog) {
            if(mCustomTitleView ! =null) {
                dialog.setCustomTitle(mCustomTitleView);
            } else {
                if(mTitle ! =null) {
                    dialog.setTitle(mTitle);
                }
                if(mIcon ! =null) {
                    dialog.setIcon(mIcon);
                }
                if(mIconId ! =0) { dialog.setIcon(mIconId); }...Copy the code
  1. The AlertDialog constructor creates an AlertController that controls the layout of the dialog box
  2. P.apply(dialog.mAlert); Assign user-defined configurations to the AlertController

1.2 AlertController

    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);
        mButtonIconDimen = a.getDimensionPixelSize(R.styleable.AlertDialog_buttonIconDimen, 0);

        a.recycle();

        /* We use a custom title so never request a window title */
        di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    }
Copy the code

R.attr.alertDialogStyle is the default style for dialog boxes,

        <item name="alertDialogStyle">@style/AlertDialog.AppCompat</item>
        <style name="AlertDialog.AppCompat" parent="Base.AlertDialog.AppCompat"/>
      	<style name="Base.AlertDialog.AppCompat" parent="android:Widget">
        	<item name="android:layout">@layout/abc_alert_dialog_material</item>
        	<item name="listLayout">@layout/abc_select_dialog_material</item>
       	 	<item name="listItemLayout">@layout/select_dialog_item_material</item>
        	<item name="multiChoiceItemLayout">@layout/select_dialog_multichoice_material</item>
        	<item name="singleChoiceItemLayout">@layout/select_dialog_singlechoice_material</item>
        	<item name="buttonIconDimen">@dimen/abc_alert_dialog_button_dimen</item>
    	</style>      
Copy the code

As you can see from the above code, abc_alert_dialog_Material is the default layout for the dialog.

<androidx.appcompat.widget.AlertDialogLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parentPanel"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="start|left|top"
    android:orientation="vertical">

    <include layout="@layout/abc_alert_dialog_title_material"/>

    <FrameLayout
        android:id="@+id/contentPanel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="48dp">

        <View android:id="@+id/scrollIndicatorUp"
              android:layout_width="match_parent"
              android:layout_height="1dp"
              android:layout_gravity="top"
              android:background="? attr/colorControlHighlight"
              android:visibility="gone"/>

        <androidx.core.widget.NestedScrollView
            android:id="@+id/scrollView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clipToPadding="false">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <android.widget.Space
                    android:id="@+id/textSpacerNoTitle"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/abc_dialog_padding_top_material"
                    android:visibility="gone"/>

                <TextView
                    android:id="@android:id/message"
                    style="@style/TextAppearance.AppCompat.Subhead"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="? attr/dialogPreferredPadding"
                    android:paddingRight="? attr/dialogPreferredPadding"/>

                <android.widget.Space
                    android:id="@+id/textSpacerNoButtons"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/abc_dialog_padding_top_material"
                    android:visibility="gone"/>
            </LinearLayout>
        </androidx.core.widget.NestedScrollView>

        <View android:id="@+id/scrollIndicatorDown"
              android:layout_width="match_parent"
              android:layout_height="1dp"
              android:layout_gravity="bottom"
              android:background="? attr/colorControlHighlight"
              android:visibility="gone"/>

    </FrameLayout>

    <FrameLayout
        android:id="@+id/customPanel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="48dp">

        <FrameLayout
            android:id="@+id/custom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </FrameLayout>

    <include layout="@layout/abc_alert_dialog_button_bar_material"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>

</androidx.appcompat.widget.AlertDialogLayout>
Copy the code

Title layout:


<! -- abc_alert_dialog_title_material: -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/topPanel"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">

    <! -- If the client uses a customTitle, it will be added here. -->

    <LinearLayout
        android:id="@+id/title_template"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical|start|left"
        android:orientation="horizontal"
        android:paddingLeft="? attr/dialogPreferredPadding"
        android:paddingRight="? attr/dialogPreferredPadding"
        android:paddingTop="@dimen/abc_dialog_padding_top_material">

        <ImageView
            android:id="@android:id/icon"
            android:layout_width="32dip"
            android:layout_height="32dip"
            android:layout_marginEnd="8dip"
            android:layout_marginRight="8dip"
            android:scaleType="fitCenter"
            android:src="@null"/>

        <androidx.appcompat.widget.DialogTitle
            android:id="@+id/alertTitle"
            style="? android:attr/windowTitleStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            android:ellipsize="end"
            android:singleLine="true"
            android:textAlignment="viewStart"/>

    </LinearLayout>

    <android.widget.Space
        android:id="@+id/titleDividerNoCustom"
        android:layout_width="match_parent"
        android:layout_height="@dimen/abc_dialog_title_divider_material"
        android:visibility="gone"/>
</LinearLayout>
Copy the code

Button layout:


<! -- abc_alert_dialog_button_bar_material: -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/buttonPanel"
            style="? attr/buttonBarStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="true"
            android:scrollIndicators="top|bottom">

    <androidx.appcompat.widget.ButtonBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="bottom"
        android:layoutDirection="locale"
        android:orientation="horizontal"
        android:paddingBottom="4dp"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        android:paddingTop="4dp">

        <Button
            android:id="@android:id/button3"
            style="? attr/buttonBarNeutralButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <android.widget.Space
            android:id="@+id/spacer"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:visibility="invisible"/>

        <Button
            android:id="@android:id/button2"
            style="? attr/buttonBarNegativeButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@android:id/button1"
            style="? attr/buttonBarPositiveButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </androidx.appcompat.widget.ButtonBarLayout>

</ScrollView>
Copy the code

2. Modify the Dialog style

2.1 through the findViewById

      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setMessage(msg);
      builder.setPositiveButton(getString(R.string.yes), null);
      AlertDialog dialog = builder.create();
      dialog.show();
      // Find the corresponding control directly by id
      Button button = dialog.findViewById(android.R.id.button1);
      // Or it can be obtained by the getButton method
      Button button2 = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
Copy the code

This modification must be called after show(), otherwise a null pointer exception will occur. This is because the layout is initialized by dialog when the show() method is executed. For details, see dialog’s onCreate method.

2.2 Custom style

The styles of the Dialog buttons are as follows:

  1. buttonBarNeutralButtonStyle
  2. buttonBarNegativeButtonStyle
  3. buttonBarPositiveButtonStyle
        <Button
            android:id="@android:id/button3"
            style="? attr/buttonBarNeutralButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <android.widget.Space
            android:id="@+id/spacer"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:visibility="invisible"/>

        <Button
            android:id="@android:id/button2"
            style="? attr/buttonBarNegativeButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@android:id/button1"
            style="? attr/buttonBarPositiveButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
Copy the code

Replace the above style with a custom style to achieve the modification effect.

  1. Add the following code to style.xml:
    <style name="accessPositiveBtnStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
        <item name="android:textColor">@color/test1</item>
    </style>

    <style name="accessNegativeBtnStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
        <item name="android:textColor">@color/test2</item>
    </style>

    <! -- Popup style -->
    <style name="testDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
        <item name="buttonBarPositiveButtonStyle">@style/accessPositiveBtnStyle</item>
        <item name="buttonBarNegativeButtonStyle">@style/accessNegativeBtnStyle</item>
    </style>
Copy the code
  1. Specific use:
    AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.testDialogTheme);
    builder.setMessage("Test");
    builder.setCancelable(false);
    builder.setPositiveButton("Confirm".null);
    builder.setNegativeButton("Cancel".null);
    Dialog dialog = builder.create();
    dialog.show();
Copy the code