Custom inline SeekBarPreference - how to set SeekBar progress on the 1st run?
Asked Answered
D

4

6

I have prepared a simple test project for my question at GitHub.

In my project there is a custom inline SeekBarPreference, which mostly works fine (its summary is updated when seekbar is being dragged and it saves integer value):

app screenshot

However there is a problem:

On the very 1st run of the app (you might need to uninstall my app when you try see the error again) the progress of the SeekBar is not set (but the summaries are set):

app screenshot

My question is: how to fix this issue in my code?

I have tried adding mSeekBar.setProgress(mProgress) in different spots of SeekBarPreference.java, but just can't find the correct place for that code.

Below are excerpts from my source code (in case Stackoverflow ever outlives GitHub) -

MainActivity.java:

PreferenceManager.setDefaultValues(this, R.xml.preferences, false);

getFragmentManager().beginTransaction()
    .addToBackStack(null)
    .replace(R.id.root, new PrefFragment(), "prefs")
    .commit();

PrefFragment.java:

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

    SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
    // set the summaries from saved values
    onSharedPreferenceChanged(prefs, SEEK_1);
    onSharedPreferenceChanged(prefs, SEEK_2);
    prefs.registerOnSharedPreferenceChangeListener(this);
}

@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {

    if (SEEK_1.equals(key)) {
        int i1 = prefs.getInt(key, DEFAULT_1);
        mSeek1.setSummary("$ " + i1);
    } else if (SEEK_2.equals(key)) {
        int i2 = prefs.getInt(key, DEFAULT_2);
        mSeek2.setSummary("$ " + i2);
    }       
}  

SeekBarPreference.java (the complete source code):

public class SeekBarPreference extends Preference implements OnSeekBarChangeListener {

    private SeekBar mSeekBar; // FIXME how to set its progress?
    private int mProgress;

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

    @Override
    protected View onCreateView(ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.preference_seekbar, parent, false);
        // can also be done in onBindView, does not work either
        mSeekBar = (SeekBar) view.findViewById(R.id.seekbar);
        mSeekBar.setProgress(mProgress);
        mSeekBar.setOnSeekBarChangeListener(this);
        return view;
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (!fromUser)
            return;

        setValue(progress);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        // not used
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        // not used
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        setValue(restoreValue ? getPersistedInt(mProgress) : (Integer) defaultValue);
    }

    public void setValue(int value) {
        if (shouldPersist()) {
            persistInt(value);
        }

        if (value != mProgress) {
            mProgress = value;
            notifyChanged();
        }
    }
}

UPDATE:

What I have unsuccessfully tried sofar -

1) Adding the following lines to the onCreateView() method:

    int progress = getPersistedInt(mProgress); // shows 0 in debugger
    mSeekBar.setProgress(progress);

2) Adding the following lines to the setValue() method:

    if (value != mProgress) {
        mProgress = value;
        mSeekBar.setProgress(mProgress);
        notifyChanged();
    }

Alas this does not work, the progress of the mSeekBar stays at 0.

Also, there is a SeekBarPreference by Google - but I don't understand how it works there (or if it works at all).

Devlin answered 11/5, 2015 at 14:29 Comment(9)
In onResume I don't see anywhere that you're setting the progress bar's progress when it's value hasn't been changed.Flannelette
Yes, you're setting the summary with mSeek1.setSummary("$ " + i1); but you never set the progress of the bar itself in this code block.Flannelette
You are wrong: I do it with onSharedPreferenceChanged(prefs, SEEK_1) in PrefFragment.javaDevlin
Please explain how you're setting it in onSharedPreferenceChanged(prefs, SEEK_1), what I see happening is: ensuring your string keys match, then getting the int value using the appropriate key, then setting the summary for the seek bar using the int value returned or the default value. So unless setting the summary also sets the current progress of the progress bar, I'm not seeing it being set in that code block.Flannelette
Yes, you are correct: I set the summaries there. I am not setting any specific values for any of the preferences: not for TextEditPreference, not for CheckBoxPreference, not for my custom SeekBarPreference. And still for the first 2 kinds of preferences everything works. And for my custom preference it almost works (the summary is ok). So the code of the SeekBarPreference.java should be fixed (and not of the PrefFragment.java).Devlin
Let us continue this discussion in chat.Flannelette
Sorry, I can not chat.Devlin
No problem, I will continue to comment here. So to clarify, in SeekBarPreference you're saying that you're not getting a value (and thus getting the default value for an int) when you call getPersistedInt from onCreateView but you are getting a value when you call getPersistedInt from onSetInitialValue, is that correct?Flannelette
This other post might help youFlannelette
N
4

You need to override onGetDefaultValue in SeekBarPreference.

Try this:

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
    return a.getInt(index, 0);
}
Noellanoelle answered 13/5, 2015 at 20:52 Comment(2)
This works, thanks. But I wonder, what is happening (at which point of Preference-lifecycle is onGetDefaultValue called?) and have always wondered about the TypedArray a argument in that method - what does it hold? Does it hold the XML-attributes of the Preference?Devlin
Yes it contains the set of XML attributes. You can find more information about TypedArray hereNoellanoelle
P
0

When you instantiate

mSeek1 = (SeekBarPreference) findPreference(SEEK_1);

private int mProgress; is 0

So probably it will help if you add a setter in the SeekBarPreference for mProgress and set its value:

mSeek1 = (SeekBarPreference) findPreference(SEEK_1);
mSeek1.setMProgress(valueIhopeIsKnownAtThisMonent);
Phile answered 13/5, 2015 at 15:12 Comment(1)
Yes I could do that workaround in my preference fragment. But actually I am looking for a way to do that in SeekBarPreference.java itself.Devlin
S
0

SOLUTION: It's a Bug in ProgressBar!

finally... I think I found the solution...

this does not work as one would expect:

seekBar.setMax(50);
seekBar.setProgress(20);
seekBar.setMax(20);
seekBar.setProgress(20);

The setProgress(...) seems to not trigger the update on the drawable if the same value is passed again. But it's not triggered during the setMax, too. So the update is missing. Seems like a Bug in the android ProgressBar! This took me about 8 hours now.. lol :D

To solve this, I'm just doing a bar.setProgress(0) before each update... this is only a workaround, but it works for me as expected:

seekBar.setMax(50);
seekBar.setProgress(20);
seekBar.setProgress(0); // <--
seekBar.setMax(20);
seekBar.setProgress(20);
Slighting answered 13/5, 2015 at 15:16 Comment(0)
F
0

Add this code before you call mSeekBar.setProgress(mProgress); :

String preferenceSummary = this.getSummary().toString();
preferenceSummary = preferenceSummary.replace("$ ", "");
mProgress = Integer.valueOf(preferenceSummary);
Flannelette answered 13/5, 2015 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.