30

Does anyone know how to override the default style for AlertDialog buttons? I've looked through the Android source for themes and styles and experimented with different things but I haven't been able to find a way that works.

What I've got below works for changing the backgrounds, but doesn't do anything with the buttons. myTheme is applied to the whole <application> via the manifest. (Some other items were deleted for clarity, but they only relate to the title bar.)

<style name="myTheme" parent="android:Theme">
    <item name="android:buttonStyle">@style/customButtonStyle</item>
    <item name="android:alertDialogStyle">@style/dialogAlertTheme</item>
</style>

<style name="dialogAlertTheme" parent="@android:style/Theme.Dialog.Alert">
    <item name="android:fullDark">@drawable/dialog_loading_background</item>
    <item name="android:topDark">@drawable/dialog_alert_top</item>
    <item name="android:centerDark">@drawable/dialog_alert_center</item>
    <item name="android:bottomDark">@drawable/dialog_alert_bottom</item>
    <!-- this last line makes no difference to the buttons -->
    <item name="android:buttonStyle">@style/customButtonStyle</item> 
</style>

Any ideas?

Steve Haley
  • 55,374
  • 17
  • 77
  • 85

8 Answers8

30

Given that MrSnowflake's reasoning is inaccurate, I'm taking the freedom to provide another answer.

There is something wrong with the markup in Steve Haley's question, and it is mixing up alertDialogStyle and alertDialogTheme, most likely because of the fact that alertDialogTheme was introduced long after alertDialogStyle was around.

So even if @android:style/Theme.Dialog.Alert is available on your Andoid platform, you still can't utilize its expressional power by hooking a cusomized version back into your own theme unless your Android platform supports an android:alertDialogTheme attribute/item for themes. (It may or may not be the case that such inconsistent Android versions exist, I don't know for sure. But the markup used in the question suggests that it does.)

In the question's markup, the parent="@android:style/Theme.Dialog.Alert" will do nothing except creating the illusion that you're customizing the alert dialog theme when you're really only customizing the alert dialog style.

This is how the markup should look like; not all Android versions support all features.

<style name="myTheme" parent="android:Theme">
    <item name="android:buttonStyle">@style/customButtonStyle</item>
    <item name="android:alertDialogStyle">@style/dialogAlertStyle</item>
    <item name="android:alertDialogTheme">@style/dialogAlertTheme</item>
</style>

<style name="dialogAlertStyle" parent="@android:style/AlertDialog">
    <item name="android:fullDark">[...]</item>
    [...]
</style>

<style name="dialogAlertTheme" parent="@android:style/Theme.Dialog.Alert">
    <item name="android:windowBackground">[...]</item>
    [...]
</style>

Customizing the alert dialog style has been around for quite some time but is limited to providing (background) drawables for "fullDark", "topDark" etc.

Customizing the alert dialog theme opens a method to provide attributes such as windowBackground, windowTitleStyle and such, but as stated before, you need an Android version which supports the alertDialogThem attribute/item for themes. I can't figure out exactly when this was introduced but it hasn't been Android 2.2 and Eclipse will tell you anyway...

I don't have the resources to validate MrSnowflake's conclusion that it's impossible to style alert dialog buttons in XML, but unless we're facing one of those somewhat nasty aspects of Android where a feature is really missing, I find it unlikely.

As a matter of fact, what's missing in the question is the most relevant part in this respect, namely

<style name="customButtonStyle" />

so the conclusion that alert dialog buttons do not obey the Widget.Button is not yet proven from my point of view.

Consolidated conclusion: The abilities to style alert dialogs independently of other widgets is limited in Android but getting more powerful as new versions improve in this respect.

class stacker
  • 5,357
  • 2
  • 32
  • 65
  • 1
    Here worked only after I removed the 'android:' part from 'android:alertDialogTheme'. Thanks. – Johny Oct 18 '18 at 06:32
  • @Johny Interesting, that could break compatibility with older Android versions, unless it was optional in the past and is no longer accepted now. Have you made compatibility tests? Even if not: For which Android version does it work like you describe? – class stacker Nov 03 '18 at 09:46
  • I get a build error when I add `parent="@android:style/AlertDialog"` to dialogAlertStyle: `resource android:style/AlertDialog is private.` Same with the theme. – Big McLargeHuge Dec 13 '18 at 16:48
  • @DavidKennedy Thank you for letting me know. And that is for which Android version? – class stacker Dec 21 '18 at 15:54
  • My compile SDK version is 28. Is that what you need to know? – Big McLargeHuge Dec 28 '18 at 19:41
11

Try this:

 <item name="buttonBarStyle">@style/Widget.AppCompat.ButtonBar</item>
    <item name="buttonBarButtonStyle">@style/AppTheme.Button</item>
    <item name="buttonBarPositiveButtonStyle">@style/YoursTheme</item>
    <item name="buttonBarNegativeButtonStyle">@style/YoursTheme</item>
    <item name="buttonBarNeutralButtonStyle">@style/YoursTheme</item>
Witoldio
  • 811
  • 9
  • 12
  • 5
    This would be more useful if you'd explain where these lines should be placed. I sure can't figure it out ... – RenniePet Apr 23 '17 at 02:31
  • 2
    Thank you for that, was looking for it for hours! @RenniePet you should place it into your Dialog theme, a theme that should probably extend `ThemeOverlay.AppCompat.Dialog.Alert` and that you declare in your theme using @style/YourDialogTheme – Benoit Jul 19 '17 at 14:24
7

You can override "buttonBarButtonStyle" instead of "buttonStyle" like this:

<style name="dialogAlertTheme" parent="@android:style/Theme.Dialog.Alert">
  <item name="android:buttonBarButtonStyle">@style/customButtonStyle</item>
</style>
miracula
  • 527
  • 7
  • 10
  • 1
    requires API 11 or higher – Yar Apr 30 '16 at 18:18
  • 1
    @TimCastelijns I feel that's beside the point. A quality answer should provide that piece of information. And there are in fact cases where people have to maintain backwards compatible apps. – class stacker Jan 20 '17 at 06:52
  • I get a build error when I add parent="@android:style/AlertDialog" to dialogAlertTheme: `resource android:style/Theme.Dialog.Alert is private.` Changing the parent to `ThemeOverlay.AppCompat.Dialog.Alert` as suggested by @Benoit seems to work. – Big McLargeHuge Dec 13 '18 at 16:57
5

If you're using Material Components:

<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:alertDialogTheme">@style/AlertDialogTheme.Light</item>
</style>

<style name="AlertDialogTheme.Light" parent="ThemeOverlay.MaterialComponents.Dialog.Alert" >
    <item name="android:buttonBarButtonStyle">@style/InsertCustomButtonStyleHere</item>
</style>

This will keep your theme's colors but replace the style of the buttons, for instance to match Widget.MaterialComponents.Button.TextButton.

Peter Keefe
  • 1,095
  • 14
  • 22
  • With this approach, I was able to change the text size but not colors. See https://stackoverflow.com/a/29810469/1015595 for a more comprehensive solution. – Big McLargeHuge Dec 13 '18 at 17:44
3

I made a method like this that will change the default button colors and background

public static void setDefaultColorTextForDialogButton(AlertDialog alertDialog, Resources r) {
    Button b;

    b= alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE);
    if(b != null) {
        b.setTextColor(r.getColor(R.color.default_text));
        b.setBackgroundColor(Color.WHITE);
    }
    b = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
    if(b != null) {
        b.setTextColor(r.getColor(R.color.default_text));
        b.setBackgroundColor(Color.WHITE);
    }
    b = alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL);
    if(b != null) {
        b.setTextColor(r.getColor(R.color.default_text));
        b.setBackgroundColor(Color.WHITE);
    }
}

This could be useful for some people!!!

Gustavo Baiocchi Costa
  • 1,379
  • 3
  • 16
  • 34
  • 2
    In an OO language such as Java, this is really not the preferred way of doing things, especially considering maintainability. – class stacker Apr 01 '16 at 05:55
1

See all other anwsers below.

Incorrect:

I guess you have to implement your own Dialog class.

MrSnowflake
  • 4,724
  • 3
  • 29
  • 32
  • 4
    Hmm. That'd be annoying; changing Dialog buttons shouldn't be that an unusual of a thing to do, considering that it's easy to apply a button style to the 'normal' Activity buttons. – Steve Haley Mar 26 '10 at 15:11
  • In normal activities you define the layout of the content view with an xml. Dialog does this for you, so you don't have access to the layout xml. Implementing your own dialog (possibly with only a setContentView() call) allows you to use a different xml and thus different styles for buttons. – MrSnowflake Mar 29 '10 at 19:20
  • 1
    How would you subclass AlertDialog so that you could use AlertDialog.Builder with your subclass? – Matt Connolly Oct 05 '12 at 02:45
0

This allowed me to customize my AlertDialog buttons and layout.

private void openLookupTableEditDialog() {
    AlertDialog.Builder openLookupTableEditDialog = new AlertDialog.Builder(this);
    View v = this.getLayoutInflater().inflate(R.layout.lookup_table_edit_dialog, null);

    openLookupTableEditDialog.setView(v);
    final AlertDialog alert = openLookupTableEditDialog.create();
    openLookupTableEditDialog.setTitle("Lookup Table Edit");
    Button btnSpecies = v.findViewById(R.id.btnSpeciesLookupEdit);
    Button btnLocation = v.findViewById(R.id.btnLocationLookupEdit);
    Button btnSampler = v.findViewById(R.id.btnSamplerLookupEdit);
    Button btnExit = v.findViewById(R.id.btnCloseLookupTableEdit);
    alert.setView(v);
    alert.show();

    btnSpecies.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            editSpeciesTable();
            alert.dismiss();
        }
    });
    btnSampler.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            editSamplerTable();
            alert.dismiss();
        }
    });
    btnLocation.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            editLocationTable();
            alert.dismiss();
        }
    });
    btnExit.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
           alert.dismiss();
        }
    });
    ...
}
portsample
  • 1,986
  • 4
  • 19
  • 35
0

In kotlin it would be like this:

private fun showDialog() =
    AlertDialog.Builder(requireContext())
        .setTitle("Title")
        .setMessage("Message")
        .setPositiveButton("OK") { _, _ ->
            // Code
        }
        .setNegativeButton("Cancel") { _, _ -> 
            // Code
        }
        .show()
        .apply {
            getButton(DialogInterface.BUTTON_POSITIVE).apply{
                setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
            }
            getButton(DialogInterface.BUTTON_NEGATIVE).apply{
                setTextColor(ContextCompat.getColor(requireContext(), R.color.colorSecondary));
            }
        }
Alberto
  • 426
  • 6
  • 16