Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why seekbar tick marker shown in front of thumb?

My seekBar style is android Widget.AppCompat.SeekBar.Discrete. I have my own tickMarker but as you can see it shown in front of thumb marker, but I don want to see ticks behind of thumb.

what I want: enter image description here

and what I have :

enter image description here

my XML:

 <android.support.v7.widget.AppCompatSeekBar
    style="@style/seekbarStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:max="4"
    android:padding="4dp"
    android:progress="0"/>

my style:

<style name="seekbarStyle" parent="Widget.AppCompat.SeekBar.Discrete">
    <item name="tickMark">@drawable/seekbar_tickmark</item>
    <item name="android:thumb">@drawable/circle</item>
</style>
like image 319
FarshidABZ Avatar asked Jan 05 '17 16:01

FarshidABZ


People also ask

What do the tick mark and double arrow icons mean?

This feature is responsible for the Tick mark (green box) and double arrow (blue box) overlay icons. A check in a green box means the file’s backup is current, while a chevron (») character in a blue box indicates a file that’s been changed since its last backup. Files excluded from the backup set display a slash in a gray box.

What is Seekbar in Android?

SeekBar Tutorial With Example In Android Studio In Android, SeekBar is an extension of ProgressBar that adds a draggable thumb, a user can touch the thumb and drag left or right to set the value for current progress.

What is the maximum value for a Seekbar?

By default, a SeekBar takes maximum value of 100. Below we set 150 maximum value for a Seek bar. 3. progress: progress is an attribute of SeekBar used to define the default progress value, between 0 and max.


2 Answers

It seems the problem is the AppCompatSeekBar widget calls super, which draws the thumb, and then draws the ticks over it.

Here is a Kotlin class that fixes the issue by redrawing the thumb over the canvas (which, at this point has the thumb and then the ticks drawn over it:

class SeekBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = android.R.attr.seekBarStyle
) : AppCompatSeekBar(context, attrs, defStyleAttr) {
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawThumb(canvas)
    }

    private fun drawThumb(canvas: Canvas) {
        if (thumb != null) {
            val saveCount = canvas.save()
            canvas.translate((paddingLeft - thumbOffset).toFloat(), paddingTop.toFloat())
            thumb.draw(canvas)
            canvas.restoreToCount(saveCount)
        }
    }
}

Note: this solution might leave some artifacts because the thumb is drawn twice. A workaround might be to define a secondary thumb and use that while hiding the original thumb.

like image 83
Eran Boudjnah Avatar answered Sep 23 '22 22:09

Eran Boudjnah


This is a bug of AppCompatSeekBar. I resolved this problem with a custom Class that extends AppCompatSeekBar:

public class CustomSeekBar extends AppCompatSeekBar {

    private Drawable mTickMark;

    public CustomSeekBar(Context context) {
        this(context, null);
    }

    public CustomSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, android.support.v7.appcompat.R.attr.seekBarStyle);
    }

    public CustomSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        applyAttributes(attrs, defStyleAttr);
    }

    private void applyAttributes(AttributeSet rawAttrs, int defStyleAttr)
    {
        TypedArray attrs = getContext().obtainStyledAttributes(rawAttrs, R.styleable.CustomSeekBar, defStyleAttr, 0);
        try {
            mTickMark = attrs.getDrawable(R.styleable.CustomSeekBar_tickMarkFixed);
        } finally {
            attrs.recycle();
        }
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawTickMarks(canvas);
    }

    @Override
    public int getThumbOffset() {
        return super.getThumbOffset();
    }

    void drawTickMarks(Canvas canvas) {
        if (mTickMark != null) {
            final int count = getMax();
            if (count > 1) {
                final int w = mTickMark.getIntrinsicWidth();
                final int h = mTickMark.getIntrinsicHeight();
                final int halfThumbW = getThumb().getIntrinsicWidth() / 2;
                final int halfW = w >= 0 ? w / 2 : 1;
                final int halfH = h >= 0 ? h / 2 : 1;
                mTickMark.setBounds(-halfW, -halfH, halfW, halfH);
                final float spacing = (getWidth() - getPaddingLeft() - getPaddingRight() + getThumbOffset() * 2 - halfThumbW * 2) / (float) count;
                final int saveCount = canvas.save();
                canvas.translate(getPaddingLeft() - getThumbOffset() + halfThumbW, getHeight() / 2);
                for (int i = 0; i <= count; i++) {
                    if(i!=getProgress())
                        mTickMark.draw(canvas);
                    canvas.translate(spacing, 0);
                }
                canvas.restoreToCount(saveCount);
            }
        }
    }
}

with the attrs.xml:

<resources>
    <declare-styleable name="CustomSeekBar">
        <attr name="tickMarkFixed" format="reference"/>
    </declare-styleable>
</resources>

and in layout you must use tickMarkFixed instead of tickMark.

like image 29
andrea689 Avatar answered Sep 20 '22 22:09

andrea689