Search code examples
androidxmlkotlinandroid-preferencescheckboxpreference

Unsucceedable cast when declaring CheckBoxPreference


After declaring a CheckboxPreference in my activity for my app's settings, a warning appears for as in the line 'val mCheckBoxPreference = findPreference("preference_a") as CheckBoxPreference'. What should be done so that the cast does succeed?

This cast can never succeed

app_preferences.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <CheckBoxPreference
        android:key="preference_a"
        android:defaultValue="false"
        android:title="Preference A"/>

</PreferenceScreen>

fragment class

import android.content.Context
import android.os.Bundle
import android.preference.CheckBoxPreference
import android.preference.Preference
import android.support.v7.preference.PreferenceFragmentCompat
import android.util.Log

class MySettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener {
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        addPreferencesFromResource(R.xml.app_preferences)

        val mCheckBoxPreference = findPreference("preference_a") as CheckBoxPreference
        mCheckBoxPreference.onPreferenceChangeListener = this
    }

    // declaring PreferenceXchangeListener
    private var mPreferenceXchangeListener: PreferenceXchangeListener? = null

    // declaring PreferenceXchangeListener in order to communicate with Activities
    interface PreferenceXchangeListener {
        fun onXchange(value:Boolean)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        // on attach - assign parent Activity as PreferenceXchangeListener
        try
        {
            mPreferenceXchangeListener = context as MySettingsFragment.PreferenceXchangeListener
        }
        catch (e:ClassCastException) {
            Log.e(TAG, "onAttach::::: PreferenceXchangeListener must be set in parent Activity")
        }
    }

    override fun onPreferenceChange(preference: Preference, newValue:Any):Boolean {
        val preferenceKey = preference.key

        if (preferenceKey == "preference_a")
        {
            (preference as CheckBoxPreference).isChecked = newValue as Boolean
            // executing parent Activity's callback with the new value
            mPreferenceXchangeListener!!.onXchange(newValue)
            return true
        }
        return false
    }

    companion object {
        private val TAG = MySettingsFragment::class.java.simpleName
    }
}

activity class

class MySettingsActivity : AppCompatActivity(), MySettingsFragment.PreferenceXchangeListener {
    private var mCurrentValue: Boolean? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        val mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        mCurrentValue = mSharedPreferences.getBoolean("preference_a", false)
        if (mCurrentValue as Boolean)
        {
            setTheme(R.style.MyDarkAppCompatTheme)
        }
        else
        {
            setTheme(R.style.MyLightAppCompatTheme)
        }
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_mysettings)

        val settingsFragment = MySettingsFragment()
        supportFragmentManager
                .beginTransaction()
                .replace(R.id.settings_container, settingsFragment)
                .commit()

        val myActionBar = actionBar

        if (myActionBar != null)
        {
            myActionBar.setTitle(R.string.settings)                
            myActionBar.setBackgroundDrawable(ColorDrawable(Color.BLACK))
        }
    }

    override fun onXchange(value:Boolean) {
        if (value !== mCurrentValue)
        {
            mCurrentValue = value
            recreate()
        }
    }


    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> {
                val intent = parentActivityIntent
                intent?.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
                onBackPressed()
                return true
            }

            else ->
                return super.onOptionsItemSelected(item)
        }
    }

    companion object {
        private val TAG = MySettingsActivity::class.java.simpleName
    }
}

Solution

  • TL;DR You have to change the import to android.support.v7.preference.CheckBoxPreference.

    There are two versions of CheckBoxPreference:

    • android.preference.CheckBoxPreference, which was added in API level 1 and has android.preference.Preference as ancestor class
    • android.support.v7.preference.CheckBoxPreference which belongs to the support library and has android.support.v7.preference.Preference as ancestor class

    Your Fragment extends from PreferenceFragmentCompat, so findPreference() will return a android.support.v7.preference.Preference. Since you can't cast support Preference classes to their non support equivalents, Android Studio is showing the error message "This cast can never succeed" when you attempt to cast to the non support CheckBoxPreference.