Skip to content

Instantly share code, notes, and snippets.

@yqritc
Last active April 17, 2024 08:26
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save yqritc/7b5c786930dfb9b47e7a to your computer and use it in GitHub Desktop.
Save yqritc/7b5c786930dfb9b47e7a to your computer and use it in GitHub Desktop.
SeekBar showing its progress as text on thumb

TextThumbSeekBar

Define a custom class extending SeekBar to show the progress text on thumb of SeekBar.

public class TextThumbSeekBar extends SeekBar {

    private int mThumbSize;
    private TextPaint mTextPaint;

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

    public TextThumbSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.seekBarStyle);
    }

    public TextThumbSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        
        mThumbSize = getResources().getDimensionPixelSize(R.dimen.thumb_size);
        
        mTextPaint = new TextPaint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.thumb_text_size));
        mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }

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

        String progressText = String.valueOf(getProgress());
        Rect bounds = new Rect();
        mTextPaint.getTextBounds(progressText, 0, progressText.length(), bounds);

        int leftPadding = getPaddingLeft() - getThumbOffset();
        int rightPadding = getPaddingRight() - getThumbOffset();
        int width = getWidth() - leftPadding - rightPadding;
        float progressRatio = (float) getProgress() / getMax();
        float thumbOffset = mThumbSize * (.5f - progressRatio);
        float thumbX = progressRatio * width + leftPadding + thumbOffset;
        float thumbY = getHeight() / 2f + bounds.height() / 2f;
        canvas.drawText(progressText, thumbX, thumbY, mTextPaint);
    }
}

smaple.xml

Use TextThumbSeekBar in your layout xml as following.

<com.yqritc.sample.TextThumbSeekBar
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:max="10"
  android:progress="5"
  android:thumb="@drawable/shape_seek_bar_text_thumb"
  android:thumbOffset="4dp"/>

shape_seek_bar_text_thumb.xml

Define custom thumb drawable as you need like the following.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/primary_color"/>
    <size
        android:width="@dimen/thumb_size"
        android:height="@dimen/thumb_size"/>
</shape>

Note

If you use image drawable as the thumb instead of xml shape drawable, set the following attribute if the background of your drawable is transparent.

android:splitTrack="false"

DONE.

@hadifar
Copy link

hadifar commented Jul 8, 2016

Avoid object allocation in onDrawer/Layout warning...

@sench93
Copy link

sench93 commented Nov 28, 2016

how much is thumb size?

@UsmanGhauri
Copy link

i want to use rounded drawable for thumb and im not being able to do it even through xml property or from code setThumb .. any help? it shows transparent background with text on it.
screenshot_1

@Sonu999
Copy link

Sonu999 commented Jun 16, 2017

What should be thumb_size and thumb_text_size??

@kraunak
Copy link

kraunak commented Aug 10, 2018

how to set progress value as a float?

@mpierucci
Copy link

Why not use thum bounds to calculare x position to draw text?

@oxied
Copy link

oxied commented Sep 10, 2019

With 2 TextPaints:

package com.app.android.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatSeekBar;
import androidx.core.content.res.ResourcesCompat;

import com.app.android.R;

public class TextThumbSeekBar extends AppCompatSeekBar {

    private int mThumbSize;
    private TextPaint mTextPaintDollar;
    private TextPaint mTextPaintNumber;

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

    public TextThumbSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.seekBarStyle);
    }

    public TextThumbSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mThumbSize = getResources().getDimensionPixelSize(R.dimen._76udp);

        mTextPaintDollar = new TextPaint();
        mTextPaintDollar.setColor(Color.WHITE);
        mTextPaintDollar.setTextSize(getResources().getDimensionPixelSize(R.dimen._24usp));
        mTextPaintDollar.setTypeface(ResourcesCompat.getFont(context, R.font.proxima_nova_regular));
        mTextPaintDollar.setTextAlign(Paint.Align.CENTER);

        mTextPaintNumber = new TextPaint();
        mTextPaintNumber.setColor(Color.WHITE);
        mTextPaintNumber.setTextSize(getResources().getDimensionPixelSize(R.dimen._24usp));
        mTextPaintNumber.setTypeface(ResourcesCompat.getFont(context, R.font.proxima_nova_bold));
        mTextPaintNumber.setTextAlign(Paint.Align.CENTER);
    }

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

        String dollarText = "$";
        Rect boundsDollar = new Rect();
        mTextPaintDollar.getTextBounds(dollarText, 0, dollarText.length(), boundsDollar);

        String progressText = String.valueOf(getProgress());
        Rect boundsNumber = new Rect();
        mTextPaintNumber.getTextBounds(progressText, 0, progressText.length(), boundsNumber);

        int leftPadding = getPaddingLeft() - getThumbOffset();
        int rightPadding = getPaddingRight() - getThumbOffset();
        int width = getWidth() - leftPadding - rightPadding;
        float progressRatio = (float) getProgress() / getMax();
        float thumbOffset = mThumbSize * (.5f - progressRatio);
        float thumbX = progressRatio * width + leftPadding + thumbOffset;
        float thumbXDollar = thumbX - boundsNumber.width() / 2f;
        float thumbXNumber = thumbX + boundsDollar.width() / 2f;
        float thumbY = getHeight() / 2f + boundsNumber.height() / 2f;

        canvas.drawText(dollarText, thumbXDollar, thumbY, mTextPaintDollar);
        canvas.drawText(progressText, thumbXNumber, thumbY, mTextPaintNumber);
    }

}

The result is regular and bold font types (for $ and for 585):
Screenshot 2019-09-10 20 38 05

Also used: https://github.com/oxied/android-universal-dp-size

@senpl
Copy link

senpl commented Feb 14, 2020

Also to res\values U need to add dimens.xml if not present:
<resources> <dimen name="thumb_size">16dp</dimen> </resources>

@ZakAnun
Copy link

ZakAnun commented Apr 29, 2020

i want to use rounded drawable for thumb and im not being able to do it even through xml property or from code setThumb .. any help? it shows transparent background with text on it.
screenshot_1

Have you make it? I wonder how to draw background under text

@ZakAnun
Copy link

ZakAnun commented Apr 30, 2020

I made it with a TextView in onProgressChanged Callback, Haha.. Like this

audioSeekBarIndicate.text = string(R.string.audio_time_result).format(
                    ParseUtils.progressToMSS(progress),
                    ParseUtils.progressToMSS(seekBar.max))
val indicateWidth = audioSeekBarIndicate.width
val thumb = seekBar.thumb
val bounds = thumb.bounds
val seekBarWidth = seekBar.width
val indicatorX = thumb.intrinsicWidth / 2 + bounds.left + seekBar.x
audioSeekBarIndicate.x = when {
        indicatorX < seekBarWidth -> {
            indicatorX
        }
        indicatorX < 0 -> {
            0f
        }
        else -> {
            seekBarWidth.toFloat()
        }
}
if (audioSeekBarIndicate.x > 0 &&
        audioSeekBarIndicate.visibility == View.GONE) {
    audioSeekBarIndicate.visibility = View.VISIBLE
}

@paullok999
Copy link

paullok999 commented Apr 17, 2024

The text can't center in the thumb,unless calculate the offset by the intrinsic width of thumb,like this

val thumbOffset = thumb.intrinsicWidth * (.5f - progressRatio) //use width of thumb to calculate the offset

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment