Custom EditTextPreference updating incorrect summary

23 Views Asked by At

I'm trying to implement a custom Preference MyEditTextPreference whose summary is an EditText, as opposed to the default TextView (see preference.xml). This lets me edit the summary directly by tapping on it.

My problem is: in my use-case, I have 2 (or more) MyEditTextPreference with different summary texts and a SwitchPreference which toggles the preferences' visibility on / off.

When I toggle their visibility multiple times, at some point one of the MyEditTextPreference will have the summary of the other one.

example:
state: "summary 111" | "summary 222"
toggle visibility on & off
state: "summary 111" | "summary 222"
toggle visibility on & off
...
toggle visibility on & off
state: "summary 111" | "summary 111"

Can someone please tell me what is it that I am doing wrong, or not understanding here? all I know is under the hood the list of Preferences are stored in a RecyclerView, but I'm not sure what to do with this information or where to look deeper.

MyEditTextPreference.kt

package com.example.settingspreferences.widget

import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.example.settingspreferences.R

class MyEditTextPreference : Preference {

    private var TAG = "ABC MSSEditTextPreference"
    private var editText: EditText? = null

    private var customSummary: CharSequence? = null
    private var textWatcher: TextWatcher? = null
    private var cursorPosition: Int = 0


    constructor(context: Context) : super(context) {
        layoutResource = R.layout.my_edittext_preference
        TAG += "[$key]"
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        layoutResource = R.layout.my_edittext_preference
        TAG += "[$key]"
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        layoutResource = R.layout.my_edittext_preference
        TAG += "[$key]"
    }

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
        layoutResource = R.layout.my_edittext_preference
        TAG += "[$key]"
    }

    override fun onBindViewHolder(holder: PreferenceViewHolder) {
        super.onBindViewHolder(holder)

        editText = holder.findViewById(android.R.id.summary) as EditText
        editText!!.visibility = View.VISIBLE
        editText!!.imeOptions = EditorInfo.IME_ACTION_DONE

        Log.d(TAG, "onBindViewHolder: editText.text = " + editText!!.text.toString())

        if (customSummary != null) {
            editText!!.setText(customSummary)
        } else {
            editText!!.setText("")
        }

        if (textWatcher != null) {
            editText!!.removeTextChangedListener(textWatcher)
        }

        textWatcher = object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                // Save the cursor position before text changed
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // Update the custom summary when the text is changed
                customSummary = s
            }

            override fun afterTextChanged(s: Editable?) {
                // No action needed
            }
        }

        editText!!.addTextChangedListener(textWatcher)
    }

    override fun getSummary(): CharSequence? {
        Log.d(TAG, "getSummary: super.getSummary()=${super.getSummary()}, customSummary=${customSummary}")
        return customSummary ?: super.getSummary()
    }

    override fun setSummary(summary: CharSequence?) {
        Log.d(TAG, "setSummary: customSummary=$customSummary, summary=$summary")
        customSummary = summary
        super.setSummary(summary)
        if (editText != null) {
            editText!!.setText(customSummary)
        }
    }
}

my_edittext_preference.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:baselineAligned="false"
    android:clipToPadding="true"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:orientation="vertical"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@android:id/icon"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_gravity="center"
            tools:background="@color/black"
            tools:src="@drawable/ic_accessibility_animation" />

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="30dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:layout_marginBottom="6dp"
            android:layout_weight="1">

            <TextView
                android:id="@android:id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="6dp"
                android:layout_marginBottom="6dp"
                android:ellipsize="marquee"
                android:fadingEdge="horizontal"
                android:hyphenationFrequency="normal"
                android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
                android:singleLine="true"
                android:textAppearance="?android:attr/textAppearanceListItem"
                tools:text="Title" />

            <EditText
                android:id="@android:id/summary"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@android:id/title"
                android:layout_alignStart="@android:id/title"
                android:background="@null"
                android:ellipsize="end"
                android:hyphenationFrequency="normal"
                android:inputType="textNoSuggestions"
                android:lines="1"
                android:maxLines="1"
                android:minWidth="10dp"
                android:paddingStart="2dp"
                android:paddingTop="6dp"
                android:paddingEnd="2dp"
                android:paddingBottom="6dp"
                android:singleLine="true"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textColor="?android:attr/textColorSecondary"
                tools:text="summary" />

        </RelativeLayout>

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginStart="54dp"
        android:background="#404040" />


</LinearLayout>

MainActivity.kt (which launches SettingsFragment, which launches CustomSettingsFragment)

package com.example.settingspreferences

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState == null) {
            supportFragmentManager
                .beginTransaction()
                .replace(R.id.settings_container, SettingsFragment())
                .commit()
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/settings_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />

</androidx.constraintlayout.widget.ConstraintLayout>

SettingsFragment.kt

package com.example.settingspreferences

import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat

class SettingsFragment : PreferenceFragmentCompat() {
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.root_preferences, rootKey)
    }
}

R.xml.root_preferences

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

    <PreferenceCategory app:title="Custom Preferences">

        <Preference
            android:icon="@drawable/ic_ampos_settings_color_correction"
            android:key="high_contrast_mode"
            android:title="See custom preferences"
            android:fragment="com.example.settingspreferences.CustomSettingsFragment" />

    </PreferenceCategory>

</PreferenceScreen>

CustomSettingsFragment.kt

package com.example.settingspreferences

import android.os.Bundle
import android.text.InputType
import android.util.Log
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import com.example.settingspreferences.widget.MyEditTextPreference

class CustomSettingsFragment : PreferenceFragmentCompat() {

    private val TAG = "ABC CustomSettingsFragment"

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.custom_preferences, rootKey)

        val switchPreference = findPreference<SwitchPreference>("switch_make_visible")
        val et1: MyEditTextPreference? = findPreference("cet1")
        val et2: MyEditTextPreference? = findPreference("cet2")

        et1!!.setSummary("Summary 1")

        et2!!.setSummary("000000")

        switchPreference!!.onPreferenceChangeListener =
            Preference.OnPreferenceChangeListener { preference, newValue ->
                val newVal = newValue as Boolean
                et1.isVisible = !newVal
                et2.isVisible = !newVal
                true
            }
    }
}
0

There are 0 best solutions below