当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义控件实现带文字提示的SeekBar

Android自定义控件实现带文字提示的SeekBar

2019年07月30日  | 移动技术网移动技术  | 我要评论

1.写在前面

seekbar控件在开发中还是比较常见的,比如音视频进度、音量调节等,但是原生控件有时还不能满足我们的需求,今天就来学习一下如何自定义seekbar控件,本文主要实现了一个带文字指示器效果的seekbar控件

看下最终效果:

indicatorseekbar

2.实现

indicatorseekbar

public class indicatorseekbar extends appcompatseekbar {

 // 画笔
 private paint mpaint;
 // 进度文字位置信息
 private rect mprogresstextrect = new rect();
 // 滑块按钮宽度
 private int mthumbwidth = dp2px(50);
 // 进度指示器宽度
 private int mindicatorwidth = dp2px(50);
 // 进度监听
 private onindicatorseekbarchangelistener mindicatorseekbarchangelistener;

 public indicatorseekbar(context context) {
 this(context, null);
 }

 public indicatorseekbar(context context, attributeset attrs) {
 this(context, attrs, r.attr.seekbarstyle);
 }

 public indicatorseekbar(context context, attributeset attrs, int defstyleattr) {
 super(context, attrs, defstyleattr);
 init();
 }

 private void init() {
 mpaint = new textpaint();
 mpaint.setantialias(true);
 mpaint.setcolor(color.parsecolor("#00574b"));
 mpaint.settextsize(sp2px(16));

 // 如果不设置padding,当滑动到最左边或最右边时,滑块会显示不全
 setpadding(mthumbwidth / 2, 0, mthumbwidth / 2, 0);

 // 设置滑动监听
 this.setonseekbarchangelistener(new onseekbarchangelistener() {
 @override
 public void onprogresschanged(seekbar seekbar, int progress, boolean fromuser) {
 // no op
 }

 @override
 public void onstarttrackingtouch(seekbar seekbar) {
 if (mindicatorseekbarchangelistener != null) {
  mindicatorseekbarchangelistener.onstarttrackingtouch(seekbar);
 }
 }

 @override
 public void onstoptrackingtouch(seekbar seekbar) {
 if (mindicatorseekbarchangelistener != null) {
  mindicatorseekbarchangelistener.onstoptrackingtouch(seekbar);
 }
 }
 });
 }

 @override
 protected synchronized void ondraw(canvas canvas) {
 super.ondraw(canvas);
 string progresstext = getprogress() + "%";
 mpaint.gettextbounds(progresstext, 0, progresstext.length(), mprogresstextrect);

 // 进度百分比
 float progressratio = (float) getprogress() / getmax();
 // thumb偏移量
 float thumboffset = (mthumbwidth - mprogresstextrect.width()) / 2 - mthumbwidth * progressratio;
 float thumbx = getwidth() * progressratio + thumboffset;
 float thumby = getheight() / 2f + mprogresstextrect.height() / 2f;
 canvas.drawtext(progresstext, thumbx, thumby, mpaint);

 if (mindicatorseekbarchangelistener != null) {
 float indicatoroffset = getwidth() * progressratio - (mindicatorwidth - mthumbwidth) / 2 - mthumbwidth * progressratio;
 mindicatorseekbarchangelistener.onprogresschanged(this, getprogress(), indicatoroffset);
 }
 }

 /**
 * 设置进度监听
 *
 * @param listener onindicatorseekbarchangelistener
 */
 public void setonseekbarchangelistener(onindicatorseekbarchangelistener listener) {
 this.mindicatorseekbarchangelistener = listener;
 }

 /**
 * 进度监听
 */
 public interface onindicatorseekbarchangelistener {
 /**
 * 进度监听回调
 *
 * @param seekbar seekbar
 * @param progress 进度
 * @param indicatoroffset 指示器偏移量
 */
 public void onprogresschanged(seekbar seekbar, int progress, float indicatoroffset);

 /**
 * 开始拖动
 *
 * @param seekbar seekbar
 */
 public void onstarttrackingtouch(seekbar seekbar);

 /**
 * 停止拖动
 *
 * @param seekbar seekbar
 */
 public void onstoptrackingtouch(seekbar seekbar);
 }

 /**
 * dp转px
 *
 * @param dp dp值
 * @return px值
 */
 public int dp2px(float dp) {
 return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dp,
 getresources().getdisplaymetrics());
 }

 /**
 * sp转px
 *
 * @param sp sp值
 * @return px值
 */
 private int sp2px(float sp) {
 return (int) typedvalue.applydimension(typedvalue.complex_unit_sp, sp,
 getresources().getdisplaymetrics());
 }
}

重点看下ondraw方法:

@override
protected synchronized void ondraw(canvas canvas) {
 super.ondraw(canvas);
 string progresstext = getprogress() + "%";
 mpaint.gettextbounds(progresstext, 0, progresstext.length(), mprogresstextrect);

 // 进度百分比
 float progressratio = (float) getprogress() / getmax();
 // thumb偏移量
 float thumboffset = (mthumbwidth - mprogresstextrect.width()) / 2 - mthumbwidth * progressratio;
 float thumbx = getwidth() * progressratio + thumboffset;
 float thumby = getheight() / 2f + mprogresstextrect.height() / 2f;
 canvas.drawtext(progresstext, thumbx, thumby, mpaint);

 if (mindicatorseekbarchangelistener != null) {
 float indicatoroffset = getwidth() * progressratio - (mindicatorwidth - mthumbwidth) / 2 - mthumbwidth * progressratio;
 mindicatorseekbarchangelistener.onprogresschanged(this, getprogress(), indicatoroffset);
 }
}

再看一遍效果图:

indicatorseekbar

可以看到,进度百分比文字是跟着进度变化在平移的,所以x轴坐标根据进度动态计算就可以了【总宽度 * 进度百分比】(getwidth() * progressratio),文字需要居中显示,所以需要向右平移【(滑块宽度 - 文字宽度)/ 2】( (mthumbwidth - mprogresstextrect.width()) / 2)。

为了避免滑块滑动到终点时布局被隐藏,需要为seekbar设置左右padding,距离分别为滑块宽度的一半,,所以【控件总长度 = 控件实际长度 + 滑块宽度】,向右平移的过程中就要动态减去滑块宽度【滑块宽度 * 进度百分比】(mthumbwidth * progressratio),到这里文字的x轴坐标就计算完成了。

文字在平移的过程中始终是垂直居中的,所以y轴坐标可以这样计算【控件高度 / 2 + 文字高度 / 2】(getheight() / 2f + mprogresstextrect.height() / 2f),注意drawtext方法默认是从左下角开始绘制文字的,如果对绘制文字还不太了解,可以看下这篇文章《android 图解canvas drawtext文字居中的那些事

指示器跟随滑块移动

在indicatorseekbar中,向外提供了一个setonseekbarchangelistener方法用来回调seekbar的状态,其中onprogresschanged方法中的indicatoroffset参数就是指示器控件的x坐标,计算方式与上文中进度百分比文字的计算方式一致:

// 【总宽度 * 进度百分比 -(指示器宽度 - 滑块宽度)/ 2 - 滑块宽度 * 进度百分比】
float indicatoroffset = getwidth() * progressratio - (mindicatorwidth - mthumbwidth) / 2 - mthumbwidth * progressratio;
mindicatorseekbarchangelistener.onprogresschanged(this, getprogress(), indicatoroffset);

看下如何使用:

public class mainactivity extends appcompatactivity {

 private textview tvindicator;
 private indicatorseekbar indicatorseekbar;
 private paint mpaint = new paint();

 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
 tvindicator = findviewbyid(r.id.tv_indicator);
 indicatorseekbar = findviewbyid(r.id.indicator_seek_bar);

 initdata();
 }

 private void initdata() {
 final linearlayout.layoutparams params = (linearlayout.layoutparams) tvindicator.getlayoutparams();
 indicatorseekbar.setonseekbarchangelistener(new indicatorseekbar.onindicatorseekbarchangelistener() {
  @override
  public void onprogresschanged(seekbar seekbar, int progress, float indicatoroffset) {
  string indicatortext = progress + "%";
  tvindicator.settext(indicatortext);
  params.leftmargin = (int) indicatoroffset;
  tvindicator.setlayoutparams(params);
  }

  @override
  public void onstarttrackingtouch(seekbar seekbar) {
  tvindicator.setvisibility(view.visible);
  }

  @override
  public void onstoptrackingtouch(seekbar seekbar) {
  tvindicator.setvisibility(view.invisible);
  }
 });
 }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <linearlayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_centerinparent="true"
 android:layout_marginstart="20dp"
 android:layout_marginend="20dp"
 android:orientation="vertical">

 <textview
  android:id="@+id/tv_indicator"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@drawable/bg_indicator"
  android:gravity="center"
  android:textcolor="#ffffff"
  android:textsize="16sp"
  android:visibility="invisible" />

 <com.yl.indicatorseekbar.indicatorseekbar
  android:id="@+id/indicator_seek_bar"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_margintop="5dp"
  android:background="@null"
  android:max="100"
  android:maxheight="2dp"
  android:minheight="2dp"
  android:progress="50"
  android:progressdrawable="@drawable/seekbar_progress_drawable"
  android:thumb="@drawable/seekbar_thumb" />

 </linearlayout>

</relativelayout>

3.写在最后

代码已上传至github,欢迎star、fork!

github地址:https://github.com/alidili/demos/tree/master/indicatorseekbardemo

本文demo的apk下载地址:

https://github.com/alidili/demos/raw/master/indicatorseekbardemo/indicatorseekbardemo.apk

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网