Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListPreference's summary text is not updated automatically whenever there is change in selection

Tags:

android

Although it is being documented, by using "%s", I can display selected value as summary, it doesn't work as expected.

http://developer.android.com/reference/android/preference/ListPreference.html#getSummary%28%29

I am having the following preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/preference_xxx_category">
        <CheckBoxPreference
            android:title="@string/preference_xxx_mode_title"
            android:defaultValue="false"
            android:summary="@string/preference_xxx_mode_summary"
            android:key="xxxModePreference" />
    </PreferenceCategory>

    <PreferenceCategory
        android:title="@string/preference_yyy_category">
        <ListPreference
           android:title="@string/preference_yyy_mode_title"
           android:defaultValue="0"
           android:entries="@array/yyy"
           android:entryValues="@array/yyy_values"
           android:summary="%s"
           android:key="yyyModePreference" />         
    </PreferenceCategory>

</PreferenceScreen>

and here is my arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="yyy">
        <item>Display value 0</item>
        <item>Display value 1</item>
    </string-array>

    <string-array name="yyy_values">
        <item>0</item>
        <item>1</item>
    </string-array>    
</resources>

What I expecting by having the following Java code to build a complete preference activity, with a check box and a list preference, the summary text of list preference can be updated automatically, whenever I perform selection. The summary text will toggle in between Display value 0 and Display value 1.

public class Preferences extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}

However, I realize the summary text for list preference will not update automatically. Only when I perform tick/ untick on check box, only the whole activity will be invalidate and summary text for list preference will ONLY be updated.

Hence, I have to revise my code as follow, which looks more cumbersome.

public class Preferences extends PreferenceActivity implements OnSharedPreferenceChangeListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onResume() {
        super.onResume();

        // Cumbersome way to make sure list preference's summary text is being updated.
        final String key = "yyyModePreference";
        ListPreference listPreference = (ListPreference)findPreference(key);
        final String value = PreferenceManager.getDefaultSharedPreferences(this).getString(key, key);
        final int index = listPreference.findIndexOfValue(value);
        if (index >= 0) {
            final String summary = (String)listPreference.getEntries()[index];         
            listPreference.setSummary(summary);
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences options, String key) {
        // Cumbersome way to make sure list preference's summary text is being updated.
        if (key.equals("yyyModePreference")) {
            ListPreference listPreference = (ListPreference)findPreference(key);
            final String value = options.getString(key, key);
            final int index = listPreference.findIndexOfValue(value);            
            if (index >= 0) {
                final String summary = (String)listPreference.getEntries()[index];         
                listPreference.setSummary(summary);
            }
        }
    }
}

Is this a correct way? Is there any simplified way, to ensure ListPreference's summary text is always updated automatically whenever there is change in selection.

like image 439
Cheok Yan Cheng Avatar asked Apr 12 '12 08:04

Cheok Yan Cheng


3 Answers

This is a bug that existed on platforms prior to KITKAT (i.e. android-4.4_r0.7), and it is fixed by commit 94c02a1

The solution from @ilomambo works, but may not be the best one. I read through the source of ListPreference and came up with this solution:

File: ListPreferenceCompat.java

package com.example.yourapplication;

import android.content.Context;
import android.os.Build;
import android.preference.ListPreference;
import android.text.TextUtils;
import android.util.AttributeSet;

public class ListPreferenceCompat extends ListPreference {

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ListPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ListPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

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

    // NOTE:
    // The framework forgot to call notifyChanged() in setValue() on previous versions of android.
    // This bug has been fixed in android-4.4_r0.7.
    // Commit: platform/frameworks/base/+/94c02a1a1a6d7e6900e5a459e9cc699b9510e5a2
    // Time: Tue Jul 23 14:43:37 2013 -0700
    //
    // However on previous versions, we have to work around it by ourselves.
    @Override
    public void setValue(String value) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            super.setValue(value);
        } else {
            String oldValue = getValue();
            super.setValue(value);
            if (!TextUtils.equals(value, oldValue)) {
                notifyChanged();
            }
        }
    }
}

When you need to use ListPreference in xml, simply replace the tag to com.example.yourapplication.ListPreferenceCompat.

This works neatly on my Samsung S4 device running Android 4.3, and inside one of my production app.

like image 188
Hai Zhang Avatar answered Nov 16 '22 02:11

Hai Zhang


I guess its some kind of a weird bug, because I've tested it on emulator running Android 2.3.3 and it didn't work, but when i tested it on 4.0.3 it worked. What i did was android:summary="%s" in xml for ListPreference and that's all.

like image 25
waqaslam Avatar answered Nov 16 '22 01:11

waqaslam


IMHO it is a bug. The onClickListener() defined in ListPreference.java#builder.setSingleChoiceItems() does not call notifyChanged()

I happen to be coding my own ListPreference based on that code, and ran across the same problem as the OP, I added notifyChanged():

            ...
            dialog.dismiss();
            notifyChanged(); // <<<<<<<<<<
        }
});

and now the preference summary is updated as soon as I choose it. It is not dependent on the Android version, ListPreference has been coded like this since ever.

like image 45
ilomambo Avatar answered Nov 16 '22 01:11

ilomambo