Changing Android SwitchCompat colors programmatically in Java (at Runtime)

In my Daily Wallpaper app, recently ported to Android, I wanted to use the Palette API to customize the colors of the UI depending on the day’s image. Instead of hard-coding one particular set of colors for my theme, which have the potential to conflict with whatever photo we receive from Bing that day, we can get the dominant and accent colors from the bitmap and integrate them into elements such as progress spinners, buttons and the Switch.

Changing the entire color scheme of an android app during runtime, however, is neither simple nor elegant. Generally the primary and accent colors are predefined in an XML file and those values cannot be changed programmatically. Instead, we must basically override the color settings of each UI component individually. To make matters worse, we won’t know what colors to assign until we’ve loaded the day’s image.

Therefore, we assign the visibility of all UI components to be invisible upon launching the app. We don’t want to see the UI load in one color and then change abruptly as the bitmap is loaded from the URL. The one exception to this is the Progress spinner, which should remain visible to indicate that the image is loading. That one element will have to be displayed in whatever default color we chose, but will be changed once we load the image.

The other thing we need to do is assign a listener to the ImageView when a bitmap is set. Then we can promptly evaluate the color scheme of the bitmap using the Palette API, then assign all the colors to the elements and, finally, set all UI components to be visible. In the onCreate() of MainActivity.java:

imageView = findViewById(R.id.imageView);
imageView.setOnImageChangedListener(bm -> {
            Palette.from(bm).generate(palette -> {
                int button_color = palette.getMutedColor(R.attr.colorAccent);
                int toolbar_color = palette.getDominantColor(R.attr.colorPrimary);
                int text_color = palette.getDominantSwatch().getBodyTextColor();
    });
});

In my case, I will grab these three values and use them for all the different elements. Here we set the Floating Action Buttons background color and the toolbar background and title text:

fab_1.setBackgroundTintList(ColorStateList.valueOf(button_color));
                fab_2.setBackgroundTintList(ColorStateList.valueOf(button_color));                
              toolbar.setBackgroundColor(toolbar_color);
                toolbar.setTitleTextColor(text_color);

Lastly we need to update the colors of our SwitchCompat. This is a little more tricky because there are several components within the SwitchCompat element that must be set separately. There is the “thumb” the “track,” but each of these also has two states – checked and unchecked – which can each be assigned their own color. However, there is no documentation or API methods that I could find for assigning each of these particular values. Here’s how we do it:

if (mSwitchCompat != null) {
                    ColorStateList thumbColors = new ColorStateList(new int[][] {
                            new int [] {android.R.attr.state_checked},
                            new int[] {}
                    }, new int[] {
                            palette.getVibrantColor(R.attr.colorAccent),
                            Color.WHITE
                    } );
                    mSwitchCompat.setTrackTintList(ColorStateList.valueOf(Color.GRAY));
                    mSwitchCompat.setThumbTintList(thumbColors);
                }

The code above sets the color of the thumb in both states (checked/unchecked) but uses only one color for the track in both states. This is for simplicity and, really, we only care about the color of the thumb, as long as the track behind it is a neutral (gray) color. If you wanted to take more control of the track colors, you can simply mirror the structure of the thumbColors array above and create another ColorStateList for setTrackTintList().

The beauty of this code is that you have full control to customize the SwitchCompat at runtime, so that we can customize it to match with our daily wallpaper!

Leave a comment

Your email address will not be published. Required fields are marked *