androidandroid-layoutandroid-widgetandroid-preferencesswitchpreference

Android - How to dynamically change the thumb/track color of a SwitchPreference's switch


So far, I can only change the summary/title text color of a preference with setSpan() on the text, but I couldn't find anything that can change the Switch's color in a SwitchPreference, I considered several plans including set a custom layout, or set a custom adapter for the preference, but I don't have a clear idea of how to achieve them yet, so, your help will be greatly appreciated.

PS: I considered simply set a custom layout, but that won't work in my case because I need to change the color of a SwitchPreference when the app is running, and users can set a custom color for the Switches, so themes also won't work in this case.


Solution

  • OK, I found the solution by myself:

    Create a custom SwitchPreference class, then apply that to all the SwitchPreference widgets that you got, the class looks like this:

        class ColorSwitchPreference extends SwitchPreference {
        Switch aSwitch;
        SharedPreferences sharedPreferences;
    
        public ColorSwitchPreference(Context context){
            super(context);
        }
    
        public ColorSwitchPreference(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public ColorSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public ColorSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
    
        @Override
        protected void onBindView(View view) {
            super.onBindView(view);
            aSwitch = findSwitchInChildViews((ViewGroup) view);
            if (aSwitch!=null) {
                //do change color here
                changeColor(aSwitch.isChecked(),aSwitch.isEnabled());
                aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        changeColor(isChecked, aSwitch.isEnabled());
                    }
                });
            }
        }
    
        private void changeColor(boolean checked, boolean enabled){
            try {
                sharedPreferences = getContext().getSharedPreferences("settings_data",MODE_PRIVATE);
                //apply the colors here
                int thumbCheckedColor = sharedPreferences.getInt("theme_color_key",Color.parseColor("#3F51B5"));
                int thumbUncheckedColor = Color.parseColor("#ECECEC");
                int trackCheckedColor = sharedPreferences.getInt("theme_color_key",Color.parseColor("#3F51B5"));
                int trackUncheckedColor = Color.parseColor("#B9B9B9");
                if(enabled){
                    aSwitch.getThumbDrawable().setColorFilter(checked ? thumbCheckedColor : thumbUncheckedColor, PorterDuff.Mode.MULTIPLY);
                    aSwitch.getTrackDrawable().setColorFilter(checked ? trackCheckedColor : trackUncheckedColor, PorterDuff.Mode.MULTIPLY);
                }else {
                    aSwitch.getThumbDrawable().setColorFilter(Color.parseColor("#B9B9B9"), PorterDuff.Mode.MULTIPLY);
                    aSwitch.getTrackDrawable().setColorFilter(Color.parseColor("#E9E9E9"), PorterDuff.Mode.MULTIPLY);
                }
            }catch (NullPointerException e){
                e.printStackTrace();
            }
        }
    
        private Switch findSwitchInChildViews(ViewGroup view) {// find the Switch widget in the SwitchPreference
            for (int i=0;i<view.getChildCount();i++) {
                View thisChildview = view.getChildAt(i);
                if (thisChildview instanceof Switch) {
                    return (Switch)thisChildview;
                }
                else if (thisChildview instanceof  ViewGroup) {
                    Switch theSwitch = findSwitchInChildViews((ViewGroup) thisChildview);
                    if (theSwitch!=null) return theSwitch;
                }
            }
            return null;
        }
    }
    

    Basically, you use findSwitchInChildViews() to get the Switch widget in the SwitchPreference, and then change the color from there.

    That's it! it's actually a pretty straightforward approach, but I just didn't think about it before, hope this post can help someone in the future to avoid my struggle.

    (PS: I got the code for finding Switch from here, changing Switch color from here, thanks!)