热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要给大家介绍了关于Android自定义控件实现带文字提示的SeekBar的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

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);
  }
 });
 }
}

布局文件:

<&#63;xml version="1.0" encoding="utf-8"&#63;>


 

 

 

 

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

总结

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


推荐阅读
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • XNA 3.0 游戏编程:从 XML 文件加载数据
    本文介绍如何在 XNA 3.0 游戏项目中从 XML 文件加载数据。我们将探讨如何将 XML 数据序列化为二进制文件,并通过内容管道加载到游戏中。此外,还会涉及自定义类型读取器和写入器的实现。 ... [详细]
  • 从零开始构建完整手机站:Vue CLI 3 实战指南(第一部分)
    本系列教程将引导您使用 Vue CLI 3 构建一个功能齐全的移动应用。我们将深入探讨项目中涉及的每一个知识点,并确保这些内容与实际工作中的需求紧密结合。 ... [详细]
  • 本报告涵盖了个人博客账号和码云账号的注册过程,以及对网络工程专业学习的反思与展望。通过回顾初入大学时的专业选择,分析当前的专业知识和技能水平,并对未来的职业规划进行了详细讨论。 ... [详细]
  • 本文详细介绍了 Java 中 org.apache.xmlbeans.SchemaType 类的 getBaseEnumType() 方法,提供了多个代码示例,并解释了其在不同场景下的使用方法。 ... [详细]
  • 本文详细记录了在银河麒麟操作系统和龙芯架构上使用 Qt 5.15.2 进行项目打包时遇到的问题及解决方案,特别关注于 linuxdeployqt 工具的应用。 ... [详细]
  • VSCode与Gitee集成:项目提交的高效实践
    本文介绍如何利用VSCode内置的Git工具将项目提交到Gitee,简化Git命令的使用,提升代码管理效率。同时分享一些常见的踩坑经验和解决方案。 ... [详细]
  • 本文深入探讨了 com.example.android.sunshine.data.TestUtilities 中 validateThenCloseCursor() 方法的使用方法及其代码示例,旨在帮助开发者更好地理解和应用该方法。 ... [详细]
  • 本文详细介绍了如何在ECharts中使用线性渐变色,通过echarts.graphic.LinearGradient方法实现。文章不仅提供了完整的代码示例,还解释了各个参数的具体含义及其应用场景。 ... [详细]
author-avatar
lily0407520
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有