androidkotlinandroid-recyclerviewandroid-preferencespreferencescreen

Custom Preference Android Kotlin


I'd like to subclass Preference to create a custom preference item in Kotlin. I am unable to get the custom preference to inflate in the Preference screen. If I remove this custom preference from my preference screen, the rest of the preferences I have implemented (not shown here) are working fine. There are many similar seeming questions here, but none of the ones I've found directly deal with the issue of creating a Kotlin implementation of a custom preference.

Please help me with a working example that you've tested that shows three things:

  1. custom_preference.xml
  2. CustomPreference.kt
  3. preference_screen.xml (Parent Preference screen to display custom preference)

Here is my code: A custom xml preference item that displays a string (let's keep it simple for the example, although my preference will end up having significantly more functionality)

custom_preference.xml

<Preference 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@android:id/widget_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CustomPreference">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is a custom preference" />
</Preference>

A class that extends Preference and includes the proper constructors.

CustomPreference.kt

package com.example.myApp

import android.content.Context
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceViewHolder
import android.util.AttributeSet
import com.example.myApp.R
import com.example.myApp.R.layout.custom_preference

class CustomPreference (context: Context,
                            attrs: AttributeSet? = null,
                            defStyleAttr: Int = R.attr.preferenceStyle,
                            defStyleRes: Int = defStyleAttr)
    : Preference(context, attrs, defStyleAttr, defStyleRes) {
    override fun onBindViewHolder(holder: PreferenceViewHolder?) {
        super.onBindViewHolder(holder)
        layoutResource = custom_preference
    }
}

The custom preference declaration in the PreferenceScreen.

preference_screen.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <com.example.CustomPreference
        app:key="custom_preference_key"
        app:title="This is a custom preference" />
</android.support.v7.preference.PreferenceScreen>

Note: I manually renamed class names here for the example. Also, I must use the support libs instead of Androidx for this project.


Solution

  • Try to set your layout resource in initialization of class, instead of onBindViewHolder. Also you should create the layout using normal widget elements (not Preference).

    CustomPreference.kt

    import android.content.Context
    import android.support.v7.preference.Preference
    import android.support.v7.preference.PreferenceViewHolder
    import android.util.AttributeSet
    import kotlinx.android.synthetic.main.custom_preference_layout.view.*
    
    class CustomPreference @JvmOverloads constructor(
            context: Context,
            attrs: AttributeSet,
            defStyleAttr: Int = 0
    ) : Preference(context, attrs, defStyleAttr) {
    
        init {
            widgetLayoutResource = R.layout.custom_preference_layout
        }
    
        override fun onBindViewHolder(holder: PreferenceViewHolder) {
            super.onBindViewHolder(holder)
            with(holder.itemView) {
                // do the view initialization here...
    
                textView.text = "Another Text"
            }
        }
    
    } 
    

    res/layout/custom_preference_layout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="This is a custom preference" />
    
    </FrameLayout>
    

    preference_screen.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.preference.PreferenceScreen 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <com.example.CustomPreference
            app:key="custom_preference_key"
            app:title="This is a custom preference" />
    
    </android.support.v7.preference.PreferenceScreen>