SeekBar setProgress trashes secondary progress

2019-06-24 08:31发布

问题:

I have primary and secondary progress growing in the same time, secondary progress grows faster than primary. Everytime I update the primary progress, the secondary one is lost (like it would be zero or less than primary). This produces a nasty flickering.

The only workaround I found is set the secondary to itself after setting the primary. I added "+1" as SeekBar has a check and doesn't redraw if I set the same value.

        mSeekBar.setProgress(newPosition);
        mSeekBar.setSecondaryProgress(mSeekBar.getSecondaryProgress()+1); // WTF?

The second line may fix it, but it looks ridiculous and I guess inefficient too.

This happens on Lollipop (5.0.1) - API 21. It used to work fine before - can't figure out why.

If I build for API level 19 it works fine. (video: API19Seekbar)

targetSdkVersion 19
compile 'com.android.support:appcompat-v7:19.+'

If I build for API level 21 it flickers. (video: API21SeekBar)

targetSdkVersion 21
compile 'com.android.support:appcompat-v7:21.+'

Full example Activity code follows:

int delay1 = 1000;
int delay2 = 200;
int primaryProgress;
int secondaryProgress;

boolean mStarted;
Button mButton;
SeekBar mSeekBar;
Handler h1 = new Handler();
Handler h2 = new Handler();

Runnable primary = new Runnable() {
    @Override
    public void run() {
        primaryProgress = (primaryProgress + 1) % 100;
        mSeekBar.setProgress(primaryProgress);
        h1.postDelayed(primary, delay1);
    }
};

Runnable secondary = new Runnable() {
    @Override
    public void run() {
        secondaryProgress = (secondaryProgress + 1) % 100;
        mSeekBar.setSecondaryProgress(secondaryProgress);
        h2.postDelayed(secondary, delay2);
    }
};


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mButton = (Button)findViewById(R.id.button);
    mButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            buttonClick();
        }
    });
    mSeekBar = (SeekBar)findViewById(R.id.seekBar);
}

private void buttonClick() {
    mStarted = !mStarted;
    if (mStarted) {
        mButton.setText("Started");
        h1.postDelayed(primary, delay1);
        h2.postDelayed(secondary, delay2);
    } else {
        mButton.setText("Stopped");
        h1.removeCallbacks(primary);
        h2.removeCallbacks(secondary);
    }

}

回答1:

This has been fixed for the next Android release, but the best workaround for now would probably be to copy scrubber_progress_horizontal_material.xml and its 9-patch PNGs from the SDK resources (<sdk-root>/platforms/android-21/data/res/...), then to rearrange the tags so that <layer-list> is at the root.

Here is what the revised XML should look like:

drawable-v21/scrubber_progress_horizontal_material_fixed.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <nine-patch
            android:src="@drawable/scrubber_track_mtrl_alpha"
            android:tint="?android:attr/colorControlNormal" />
    </item>
    <item android:id="@android:id/secondaryProgress">
        <scale android:scaleWidth="100%">
            <selector>
                <item android:state_enabled="false">
                    <color android:color="@android:color/transparent" />
                </item>
                <item>
                    <nine-patch
                        android:src="@drawable/scrubber_primary_mtrl_alpha"
                        android:tint="?android:attr/colorControlNormal" />
                </item>
            </selector>
        </scale>
    </item>
    <item android:id="@android:id/progress">
        <scale android:scaleWidth="100%">
            <selector>
                <item android:state_enabled="false">
                    <color android:color="@android:color/transparent" />
                </item>
                <item>
                    <nine-patch
                        android:src="@drawable/scrubber_primary_mtrl_alpha"
                        android:tint="?android:attr/colorControlActivated" />
                </item>
            </selector>
        </scale>
    </item>
</layer-list>

And then the appropriate changes in values-v21/styles.xml and/or your layout XML so that you use this background by default on API 21+. I can include examples for those if you need them.