Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom SwitchPreference in Android

How to set a custom style or other background selector drawable for the SwitchPreference widget in Android?

(Note: not the regular Switch widget, I mean the standart SwitchPreference widget that used in PreferenceActivity / PreferenceFragment)

like image 915
micnoy Avatar asked Jan 20 '14 13:01

micnoy


3 Answers

You have to create a custom layout for the switch itself and you can apply it dynamically like.

preference.setWidgetLayoutResource(R.layout.custom_switch);

But I'll go into details and show you exactly how to achieve this.

So, you define your preference in an xml file like preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory android:title="YOUR_CATEGORY_TITLE" >
        <SwitchPreference
            android:key="SWITCH"
            android:title="YOUR_TITLE_FOR_SWITCH" />
    </PreferenceCategory>
</PreferenceScreen>

Then read it in the onCreate() method inside your PreferenceActivty class:

    SwitchPreference pref = (SwitchPreference) findPreference(getString(R.string.SWITCH));
    //pref.setChecked(true); // You can check it already if needed to true or false or a value you have stored persistently
    pref.setWidgetLayoutResource(R.layout.custom_switch); // THIS IS THE KEY OF ALL THIS. HERE YOU SET A CUSTOM LAYOUT FOR THE WIDGET
    pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {

        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            // Here you can enable/disable whatever you need to
            return true;
        }
    });

The custom_switch layout looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_switch_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:textColor="@android:color/white"
    android:textIsSelectable="false"
    android:textSize="18sp"
    android:textStyle="bold"
    android:track="@drawable/switch_track" 
    android:thumb="@drawable/switch_thumb"/>

And for the switch you'll have 2 selectors for the track and thumb properties. The drawables for these selectors can be generated with the Android Holo Color Generator, which was suggested by tasomaniac. In this case, all you have to do, is to copy the content of the generated drawable folders(only for the drawable-hdpi, drawable-mdpi, drawable-xhdpi, drawable-xxhdpi). But you can create custom views for each state you need.

Here is how these selectors will look like: switch_track:

<?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/switch_bg_focused" android:state_focused="true"/>
    <item android:drawable="@drawable/switch_bg"/>

</selector>

switch_thumb:

<?xml version="1.0" encoding="utf-8"?>
     <selector xmlns:android="http://schemas.android.com/apk/res/android">

     <item android:drawable="@drawable/switch_thumb_disabled" android:state_enabled="false"/>
     <item android:drawable="@drawable/switch_thumb_pressed" android:state_pressed="true"/>
     <item android:drawable="@drawable/switch_thumb_activated" android:state_checked="true"/>
     <item android:drawable="@drawable/switch_thumb"/>

</selector>

And that's pretty much it. This solution helped me out. If I omitted something, please let me know and I'll correct the issues.

like image 80
Mike Avatar answered Nov 05 '22 00:11

Mike


You can use the below website to generate style for your Switch. http://android-holo-colors.com/

And then you can use following libraries to custom implementation of the regular Switch. These libraries also include SwitchPreference alternative.

https://github.com/BoD/android-switch-backport

https://github.com/ankri/SwitchCompatLibrary

like image 45
tasomaniac Avatar answered Nov 04 '22 23:11

tasomaniac


One way of doing this is to subclass the SwitchPreference and override the onBindView method. In doing so, you'll want to still call super.onBindView(view) in that method, but then find the Switch in the child views and style it as appropriate:

package com.example;

import android.annotation.SuppressLint;
import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;

import com.example.R;


public class CustomSwitchPreference extends SwitchPreference {

    @SuppressLint("NewApi")
    public CustomSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public CustomSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomSwitchPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomSwitchPreference(Context context) {
        super(context);
    }

    @Override
    protected void onBindView(View view) {

        super.onBindView(view);
        Switch theSwitch = findSwitchInChildviews((ViewGroup) view);
        if (theSwitch!=null) {
            //do styling here
            theSwitch.setThumbResource(R.drawable.new_thumb_resource);
        }

    }

    private Switch findSwitchInChildviews(ViewGroup view) {
        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;
    }
}
like image 2
Chris Avatar answered Nov 05 '22 00:11

Chris