Magnus
Magnus

Reputation: 18738

Android: SeekBar onProgressChanged-event doesn't get fired when setting progress programmatically

My onProgressChanged()-event doesn't get fired when I set the progress of a SeekBar programmatically, but it does get fired perfectly fine when I physically move the SeekBar slider.

I'd expect the event to fire when using setProgress() - the Android Developer Reference even states that:

public abstract void onProgressChanged (SeekBar seekBar, int progress, boolean fromUser)

Notification that the progress level has changed. Clients can use the fromUser parameter to distinguish user-initiated changes from those that occurred programmatically.

Some code snippets from my project:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_activity);               

    final SeekBar mySeekBar = ((SeekBar) findViewById(R.id.mySeekBar));
    
    mySeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
        @Override
        public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
            // Do some stuff
        }
    }
}


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

    final SeekBar mySeekBar = ((SeekBar) findViewById(R.id.mySeekBar));
    mySeekBar.setProgress(someValue); // This SHOULD trigger onProgressChanged(), but it doesn't...
}

Upvotes: 8

Views: 16065

Answers (10)

TomV
TomV

Reputation: 1232

You need to ensure the value has definitely changed, otherwise it won't be picked up. Put in 2 lines. The 1st not wanted. The 2nd desired.

seekbar.setProgress(7); // Necessary to force the change to desired
seekbar.setProgress(50); // Desired progress

Upvotes: 1

zevero
zevero

Reputation: 2402

My listener didn't fire because I was calling setProgress() BEFORE setting the listener

Upvotes: 3

hushed_voice
hushed_voice

Reputation: 3608

The accepted answer didn't work for me because it stopped getting fired when the progress is set to 1 programmatically (which is the same bug).

What I did instead was to invoke the onProgressChanged() myself. I the seek bar is a member variable.

void setProgressValue(int progress){
    if(progress == 0){ //the default progress is 0. if i set it to 0 again the call back wont be invoked
        mSeekBar.setProgress(progress);
        onProgressChanged(progress,mSeekBar,false);
    }
}

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    //things to be done when the call back is received
}

Posting in the hope of helping someone later.

Upvotes: 0

Lukas
Lukas

Reputation: 1215

one possible workaround is to create own widget extending Seekbar and implementing two methods for your needs:

    private OnSeekBarChangeListener onSeekBarChangeListener;

    @Override
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
        super.setOnSeekBarChangeListener(l);
        onSeekBarChangeListener = l;
    }

    @Override
    public void setProgress(int progress) {
        super.setProgress(progress);
        if (onSeekBarChangeListener != null) {
            onSeekBarChangeListener.onProgressChanged(this, progress, false);
        }
    }

Upvotes: 0

Snehal
Snehal

Reputation: 553

Set progress=10 in xml file as shown below:

<SeekBar android:id="@+id/seekbar" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content"  
  android:progress="10"
  android:max="100" />

Then in main file write below code:

new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            seekbar.setProgress(50)
                        }
                    }, 100);

Give delay less so that it doesn't affect the UI.

Upvotes: 1

jpihl
jpihl

Reputation: 8071

SeekBar.OnSeekBarChangeListener.onProgressChanged is only called when the progress is actually changed, not necessarily when setProgress is called - this only happens if the new progress is different from the last.

Looking at the source of SeekBar, which extends AbsSeekBar which in turn extends ProgressBar, we can see that a call to setProgress() doesn't fire the onProgressChanged() event unless the new progress is different from the previous one as seen in the source below.

/* 
    Snippet from ProgressBar.java 
*/

public void setProgress(int progress, boolean animate) {
    setProgressInternal(progress, false, animate);
}


synchronized boolean setProgressInternal(int progress, boolean fromUser, boolean animate) {
    if (mIndeterminate) {
        // Not applicable.
        return false;
    }

    progress = MathUtils.constrain(progress, mMin, mMax);
    if (progress == mProgress) {
        // No change from current.
        return false;
    }

    mProgress = progress;
    refreshProgress(R.id.progress, mProgress, fromUser, animate);
    return true;
}

Upvotes: 1

Hemanth Kumar
Hemanth Kumar

Reputation: 237

simply call

 mySeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
    @Override
    public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
       int i=    arg0.getProgress();
     //youre seekbar.setProgress(i);
    }
}

Upvotes: -1

Nicholas Miller
Nicholas Miller

Reputation: 4390

I can confirm with certainty that setProgress works on Android version 6.0.1.

Another StackOverflow answer said it was a bug. If this was true, it is definitely no longer the case.

This code works on my test setup:

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

    SeekBar seekBar = (SeekBar)findViewById(R.id.seek_bar);
    seekBar.setProgress(seekBar.getProgress());
}

As expected, the SeekBar's OnSeekBarChangeListener is getting called.

For troubleshooting, make sure that you set the listener properly using setOnSeekBarChangeListener() and make sure you implement the interface functions. Other SO answers seem to indicate that you should only call setProgress() from the UI thread. This answer shows how that can be done.

Upvotes: -1

Aaron Bar
Aaron Bar

Reputation: 620

+1 for Romuald Brunet's answer:

Here was my "hack" to fix it:

        <SeekBar android:id="@+id/seekbar" 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content"  
            android:progress="1"
            android:max="200" />

Note the progress="1", set the default progress in the layout to 1, then in my code when I actually default it to 0, a change in the progress occurs and the onProgressChanged() event fires.

Upvotes: 6

Romuald Brunet
Romuald Brunet

Reputation: 5831

Stumbled across the same problem just now.

In my case, the onProgressChanged did not get fired simply because the value did not actually change. I was setting the same value as the current one (0 :)

(and I don't see anything wrong with your code)

Upvotes: 9

Related Questions