I want to create a custom SeekBar with following text that updates its position (and progress) according to where the thumb is positioned.
What do I need to achieve:
Assumptions:
Case 1: The progress is on 0% (or min value = 1000) and the text should align to the left border of thumb.
Case 2: The progress text is displayed above Thumb drawable and centered since it has space to draw itself.
Case 3: The progress is on 100% (or max value = 3000) and the text should align to the right border of Thumb.
What do i already have:
public class CustomSeekBar extends SeekBar {
private Paint paint;
private int[] coordCenterTemp = new int[]{0, 0};
// .....
private void init() {
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setTextSize(sp2px(20));
paint.setTextAlign(Paint.Align.CENTER);
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
String str = String.valueOf(getProgress());
int thumb_x = (int) (( (double)this.getProgress()/this.getMax() ) * (double)(this.getWidth()));
int thumb_y = 30;
int textMeasure = (int) paint.measureText(str, 0, str.length());
if ((textMeasure / 2) + thumb_x <= getRight()) {
coordCenterTemp[0] = thumb_x;
coordCenterTemp[1] = thumb_y;
}
canvas.drawText(str, coordCenterTemp[0], coordCenterTemp[1], paint);
}
}
This is working for when the progress is 50%. As soon as I slide to right or left, the Text is painted with some offset to wrong direction.
How can I accomplish this?
I implemented such a feature recently and here is my code:
First define a ThumbTextView which is TextView
above your Seekbar
:
public class ThumbTextView extends TextView {
private LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
private int width = 0;
public ThumbTextView(Context context) {
super(context);
}
public ThumbTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void attachToSeekBar(SeekBar seekBar) {
String content = getText().toString();
if (TextUtils.isEmpty(content) || seekBar == null)
return;
float contentWidth = this.getPaint().measureText(content);
int realWidth = width - seekBar.getPaddingLeft() - seekBar.getPaddingRight();
int maxLimit = (int) (width - contentWidth - seekBar.getPaddingRight());
int minLimit = seekBar.getPaddingLeft();
float percent = (float) (1.0 * seekBar.getProgress() / seekBar.getMax());
int left = minLimit + (int) (realWidth * percent - contentWidth / 2.0);
left = left <= minLimit ? minLimit : left >= maxLimit ? maxLimit : left;
lp.setMargins(left, 0, 0, 0);
setLayoutParams(lp);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (width == 0)
width = MeasureSpec.getSize(widthMeasureSpec);
}
}
Then define your own ThumbTextSeekBar class which is a wrapper around Seekbar
& ThumbSeekbar
:
public class ThumbTextSeekBar extends LinearLayout {
public ThumbTextView tvThumb;
public SeekBar seekBar;
private SeekBar.OnSeekBarChangeListener onSeekBarChangeListener;
public ThumbTextSeekBar(Context context) {
super(context);
init();
}
public ThumbTextSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_thumb_text_seekbar, this);
setOrientation(LinearLayout.VERTICAL);
tvThumb = (ThumbTextView) findViewById(R.id.tvThumb);
seekBar = (SeekBar) findViewById(R.id.sbProgress);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (onSeekBarChangeListener != null)
onSeekBarChangeListener.onStopTrackingTouch(seekBar);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (onSeekBarChangeListener != null)
onSeekBarChangeListener.onStartTrackingTouch(seekBar);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (onSeekBarChangeListener != null)
onSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
tvThumb.attachToSeekBar(seekBar);
}
});
}
public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener l) {
this.onSeekBarChangeListener = l;
}
public void setThumbText(String text) {
tvThumb.setText(text);
}
public void setProgress(int progress) {
if (progress == seekBar.getProgress() && progress == 0) {
seekBar.setProgress(1);
seekBar.setProgress(0);
} else {
seekBar.setProgress(progress);
}
}
}
Finally, utilize it in following way:
thumbTextSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
thumbTextSeekbar.setThumbText(seekBar.getProgress()));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
thumbTextSeekbar.showTxt();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
thumbTextSeekbar.hideTxt();
}
});
Just to be sure everything is complete, here is R.layout.view_thumb_text_seekbar
:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<xxx.ui.widget.ThumbTextView
android:id="@+id/tvThumb"
style="@style/Textview.White.MediumSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/round_rect_black_trans"
android:padding="4dp"
android:visibility="gone" />
<SeekBar
android:id="@+id/sbProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-16dp"
android:layout_marginRight="-16dp"
android:layout_marginTop="5dp"
android:minHeight="20dp" />
</merge>
The output:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With