8
votes

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...
}
10

10 Answers

8
votes

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)

6
votes

+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.

3
votes

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

1
votes

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;
}
1
votes

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.

1
votes

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
0
votes

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);
        }
    }
0
votes

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.

-1
votes

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.

-1
votes

simply call

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