Android - 带有文本的搜索栏
Android - Seekbar with text following
我想使用以下文本创建自定义 SeekBar,根据拇指的位置更新其位置(和进度)。
我需要实现什么:
假设:
- 最小值 = 1000
- 最大值 = 30000
案例一:
进度为 0%(或最小值 = 1000),文本应与拇指的左边框对齐。
案例二:
进度文本显示在 Thumb 可绘制对象上方并居中,因为它 space 可以自行绘制。
案例三:
进度为 100%(或最大值 = 3000),文本应与 Thumb 的右边框对齐。
我已经拥有什么:
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);
}
}
这适用于进度为 50% 的情况。一旦我向右或向左滑动,文本就会向错误的方向绘制一些偏移量。
我怎样才能做到这一点?
我最近实现了这样一个功能,这是我的代码:
首先定义一个 ThumbTextView,它位于 Seekbar
:
之上 TextView
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);
}
}
然后定义你自己的 ThumbTextSeekBar class 这是 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);
}
}
}
最后,按以下方式使用它:
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();
}
});
为了确保一切都已完成,这里是 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>
输出:
我想使用以下文本创建自定义 SeekBar,根据拇指的位置更新其位置(和进度)。
我需要实现什么:
假设:
- 最小值 = 1000
- 最大值 = 30000
案例一: 进度为 0%(或最小值 = 1000),文本应与拇指的左边框对齐。
案例二: 进度文本显示在 Thumb 可绘制对象上方并居中,因为它 space 可以自行绘制。
案例三: 进度为 100%(或最大值 = 3000),文本应与 Thumb 的右边框对齐。
我已经拥有什么:
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);
}
}
这适用于进度为 50% 的情况。一旦我向右或向左滑动,文本就会向错误的方向绘制一些偏移量。
我怎样才能做到这一点?
我最近实现了这样一个功能,这是我的代码:
首先定义一个 ThumbTextView,它位于 Seekbar
:
TextView
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);
}
}
然后定义你自己的 ThumbTextSeekBar class 这是 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);
}
}
}
最后,按以下方式使用它:
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();
}
});
为了确保一切都已完成,这里是 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>
输出: