前言

  • 本篇文章记录通过自定义View实现Android下可滑动的进度条
  • 学习巩固自定义View知识

说明

1、实现效果

文中实现的效果都是未加抗锯齿

2、View绘制解析

上图自定义View中有文本(大小)背景条(灰色)进度条(绿色)滑动区域(白色内圆)外框圆(绿色)、进度文字等元素,分析清晰元素的属性,代码更容易的去实现。

  • 背景条属性: 起点坐标(startX)、长度(backgroundTotalLen)、颜色(backgroundColor)、线宽(backgroundStrokeW)

  • 进度条属性: 起点坐标(startX)、进度(progress)为0-100、颜色
    (progressColor)、线宽(progressStrokeW)

  • 滑块内圆: 半径(handleRadius)

  • 外框圆: 这里设置半径比内圆大2f ,颜色和进度条颜色一致(progressColor)

  • 文本(进度值): 是否显示(showProgressText)

注意:

1、进度条的进度对应的坐标和滑动区域(内圆的坐标中心)、外框圆(坐标中心)一致

2、外框圆的颜色和进度条的颜色一致

3、绘制进度值文本如何与背景条与进度条保持垂直居中?

实现

上面分析了自定义View元素的属性值,下面代码进行实现

一、声明属性文件,在values下新建 attrs.xml文件

新建标签为 declare-styleable类型的xml,名字SlideView可自定义,一般和自定义View名保持一致,声明标签和名和对应的类型。

<resources><declare-styleable name="SlideView"><!--进度背景颜色--><attr name="backgroundColor" format="color"/><!--背景的线宽--><attr name="backgroundStrokeW" format="float"/><!--背景总长度--><attr name="backgroundTotalLen" format="integer"/><!--起点x坐标--><attr name="startX" format="integer"/><!--进度--><attr name="progress" format="integer"/><!--进度颜色--><attr name="progressColor" format="color"/><!--进度线宽--><attr name="progressStrokeW" format="float"/><!--手柄圆半径--><attr name="handleRadius" format="float"/><!--是否显示文字进度--><attr name="showProgressText" format="boolean"/></declare-styleable></resources>

二、获取xml文件上的属性值,并给画笔设置属性

attributeSet为Activity布局文件中声明的属性,R.styleable.SlideViewattrs.xml中声明的属性

 /*** 进度条监听,回调到外面*/private lateinit var listener:(progress:Int) -> Unitfun onProgressChange(l:(progress:Int) ->Unit){this.listener = l}/*** 背景条*/private val backgroundPaint = Paint().apply {style = Paint.Style.FILLstrokeCap = Paint.Cap.ROUND}/*** 进度条画笔*/private val progressPaint = Paint().apply {style = Paint.Style.FILLstrokeCap = Paint.Cap.ROUND}/*** 内实心圆画笔*/private val innerCirclePaint = Paint().apply {style = Paint.Style.FILLstrokeWidth = 10fcolor = Color.WHITE}/*** 外圆画笔*/private val outerCirclePaint = Paint().apply {style = Paint.Style.STROKEstrokeWidth = 2f}/*** 文字画笔*/private val textPaint = Paint().apply {style = Paint.Style.FILLtextSize = 40fcolor = Color.BLACK}init{val ta = context.obtainStyledAttributes(attributeSet,`R.styleable.SlideView`)//获取背景条颜色backgroundColor = ta.getColor(R.styleable.SlideView_backgroundColor,context.getColor(R.color.colorEC))//获取背景条线宽backgroundStrokeW = ta.getFloat(R.styleable.SlideView_backgroundStrokeW,18f)//获取背景条总长totalLen = ta.getInt(R.styleable.SlideView_backgroundTotalLen,100)//获取进度条颜色progressColor = ta.getColor(R.styleable.SlideView_progressColor,context.getColor(R.color.colorGrassGreen))//获取进度条线宽progressStrokeW = ta.getFloat(R.styleable.SlideView_progressStrokeW,18f)//获取进度条的进度progress = ta.getInt(R.styleable.SlideView_progress,0)//获取内圆半径radius = ta.getFloat(R.styleable.SlideView_handleRadius,30.toFloat())//获取绘制起点startX = DeviceUtils.dp2px(context,ta.getInt(R.styleable.SlideView_startX,0).toFloat() + DeviceUtils.px2dp(context,radius) + 2f)//是否显示进度值showProgressText = ta.getBoolean(R.styleable.SlideView_showProgressText,true)ta.recycle()}

三、绘制元素

重写onDraw方法,绘制背景条进度条滑动区域(内圆)外圆

/*** 绘制元素*/override fun onDraw(canvas: Canvas) {super.onDraw(canvas)canvas.apply {//绘制背景条drawLine(startX.toFloat(),endY ,endX.toFloat(),endY,backgroundPaint)//绘制进度条drawLine(startX.toFloat(),endY, progressValue,endY,progressPaint)//绘制内圆drawCircle(progressValue,endY, radius,innerCirclePaint)//绘制外圆drawCircle(progressValue,endY ,radius + 2f,outerCirclePaint)//是否绘制进度值if(showProgressText){drawText("${(((progressValue - startX) / totalLen) * 100).toInt()} %", endX + 40f ,radius + 2f - baseLine,textPaint)}}}

四、处理滑动事件

重写ViewOnTouchEvent方法,需要判断手指按下的区域在外圆的坐标值内,滑动的范围要限制在startXendX之间

/*** 处理拖动事件*/@SuppressLint("ClickableViewAccessibility")override fun onTouchEvent(event: MotionEvent): Boolean {var cx = event.xval cy = event.ywhen(event.action){MotionEvent.ACTION_DOWN ->{//判断手指按下区域是否在句柄圆上, 左右和上下有效触摸区域扩大各40fisOnTouch = (cx > progressValue - radius - 20f  && cx < progressValue + radius + 20f  && cy > -20f && cy < 2 * radius + 20f)}MotionEvent.ACTION_MOVE ->{if (isOnTouch){//限制最小值为起点startXif(cx < startX){ cx = startX.toFloat() }//限制最大值为终点endXelse if(cx > endX) { cx = endX.toFloat() }progressValue = cx//重新绘制invalidate()//将进度回调出去listener.invoke(((progressValue / (endX  - startX)) * 100).toInt())}}MotionEvent.ACTION_UP ->{isOnTouch = false}}return true}

五、问题点的处理

绘制进度文字时遇到一个问题,就是文字和背景条和进度条无法居中对齐。

if(showProgressText){drawText("${(((progressValue - startX) / totalLen) * 100).toInt()} %", endX + 40f ,radius + 2f,textPaint)}

进度值的X坐标在背景条后面,距离为40F,Y坐标和内圆半径raduis + 外圆半径2F,按理说应该是和滑动区域是垂直居中的。然后显示起来并没有居中,猜想文本在坐标系中的绘制较其有特殊。


那我们看下文本是怎么绘制在坐标系中的? 在Android中,提供了方法getTextBounds查看文本的绘制区域。

/*** 文字画笔*/private val textPaint = Paint().apply {style = Paint.Style.FILLtextSize = 40fcolor = Color.BLACK}rect = Rect()textPaint.getTextBounds("100%",0,"100%".length,rect)Log.d("AAAAAA","left = $left , top = ${rect?.top!!} , right = $right , bottom = ${rect?.bottom}")输出:left = 0 , top = -29 , right = 0 , bottom = 2

通过打印出来的值,可以发现文本基线并不是垂直于坐标轴Y轴的,当前Paint和文本获取到绘制区域的 topbottom值如下图所示,这就造成为了绘制后不垂直的原因。因为文本绘制基线涉及需要大量篇幅去说明,这里就不详细解释。那要如何处理呢?其实很简单,让文本的基线
垂直于Y轴即可。

解决方法:

把绘制文本的topbottom取中间值作为垂直于Y轴的基线带入计算即可

//文字绘制的基线va baseLine = rect?.let {(rect?.top!! + rect?.bottom!!)/ 2}!!Log.d("AAAAAA","baseLine = $baseLine")输出: baseLine = 13if(showProgressText){drawText("${(((progressValue - startX) / totalLen) * 100).toInt()} %", endX + 40f ,radius + 2f  - baseLine ,textPaint)}

六、布局文件中使用

 Xml中:<com.xn.customview.widget.SlideViewandroid:id="@+id/svAlpha"android:layout_width="@dimen/px_906"android:layout_height="@dimen/px_72"android:layout_gravity="center_vertical"android:layout_marginStart="10dp"app:backgroundColor="@color/colorEC"app:backgroundStrokeW="18"app:backgroundTotalLen="500"app:handleRadius="30"app:progress="50"app:progressColor="@color/colorGrassGreen"app:progressStrokeW="18"app:showProgressText="true"app:startX="0" />
Activity中://获取进度回调mBinding.svAlpha.onProgressChange {Log.d("AAAAAA","svAlpha progress = $it")}mBinding.svSize.onProgressChange {Log.d("AAAAAA","svSize progress = $it")}

结尾

文章中对文字位置处理不够完善,正确的处理方式请查看文章Android自定义控件(六) Andriod仿iOS控件Switch开关

Android自定义控件(一) 可滑动的进度条相关推荐

  1. android图标随着进度条动画,Android开发之ProgressBar字体随着进度条的加载而滚动...

    在网上翻阅了很多关于ProgressBar滚动效果,但是始终没有找到适合项目中的这种效果,故自己写这篇文章,记录一下写作过程,给大家做一个参考.先看下最终效果效果图 我这里用的是LICEcap软件录制 ...

  2. android解压zip文件进度条,Android实现文件解压带进度条功能

    解压的工具类 package com.example.videodemo.zip; public class ZipProgressUtil { /*** * 解压通用方法 * * @param zi ...

  3. Android自定义滑动进度条,Android自定义View实现圆形水波进度条

    每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动,于是我开始了自定义View之路,虽然过程有坎坷,但是结果我还是挺满意的.我知道大牛还 ...

  4. Android自定义波浪加载圆形进度条——(自定义控件 一)

    2019独角兽企业重金招聘Python工程师标准>>> 自定义控件-- 波浪形状圆形进度加载 时间管理的基础是精力管理,精力的高低.正负分影响到我们的效率 而时间是无法管理的,能够管 ...

  5. android 环形时间显示_Android圆形进度条颜色的设置

    最近几天由于项目的需要研究了一下listView的滑动数据动态的更新显示,其中需要在数据加载过程有圆形进度条的显示,遇到的问题是进度条的颜色设置,在网上查了一些资料结合自己的所得分享在此. xml布局 ...

  6. android 图片处理过程中添加进度条,[Android] 随时拍图像处理部分总结及源码分......

    ? ? ? ? 随着寒假到来,终于有时间总结下Android这个随手拍的课程作业了,同时分享了我完成的这部分对图像处理的心得吧!你可以结合demo来阅读这篇文章,希望对大家有所帮助. ? ? ? ? ...

  7. android自定义渐变色圆环,Android实现渐变圆环、圆形进度条效果

    最近做了一个功能,里面涉及到了渐变圆形的需求.就是一个颜色可以渐变的圆环,最后实现的效果如下图: 左图是带渐变效果,右图是不带渐变效果.原理还是绘图,Canvas可以绘制的对象有:弧线(arcs).填 ...

  8. android炫酷的自定义view,Android自定义View实现炫酷进度条

    本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下 下面我们来实现如下效果: 第一步:创建attrs文件夹,自定义属性: 第二步:自定义View: /** * Cre ...

  9. android canvas_Android仿IOS11 控制中心进度条

    这篇文章我们就用简单的自定义View实现类似iOS11的控制中心里边出现的很新颖的进度条. 先看一下目标效果图,以及最后的实现效果: 自定义属性分析 名字 释义 progressMax 最大值 pro ...

最新文章

  1. 2022-2028年中国超韧尼龙行业市场调查分析及未来前景分析报告
  2. java与mysql时间类型对应的问题
  3. java中怎么找类的路径_Java中获取类加载路径和项目根路径的5种方法
  4. android 启动器开发,Android启动器(Launcher)开发详解
  5. 学习指南_学习指南语文—成于素养,归于方法
  6. STM32学习:外部中断
  7. Aaron Swartz Rewriting Reddit中关于web.py的创建思路
  8. bui框架与php结合,bui框架前端自定义配色基础属性
  9. 计算机汇编语言编程教程,《汇编语言程序设计教程》.pdf
  10. 3DMAX场景渲染失败怎么办?
  11. MySql根据字段名查询重复记录并删除!只保留一条
  12. 程序员接单网站哪个好(盘点十大程序员接私活平台)
  13. 卧龙图说酒:喝酒的人生,是有意义的人生!
  14. 猴子定律--如何跳出你深陷的棋局看趋势
  15. 【Linux】如何查找命令及历史记录history
  16. uni-app分享小程序页面给微信好友;小程序分享无效原因;小程序分享失败原因;
  17. java基础系列(四)UTF-8和GBK编码的区别
  18. Redis之懒惰删除
  19. 2021年三门中学高考成绩查询,2021长沙市地区高考成绩排名查询,长沙市高考各高中成绩喜报榜单...
  20. MID:为3G而生,因3G引爆流行

热门文章

  1. Postman是什么 怎么用
  2. Linux SPI驱动框架(2)——控制器驱动层
  3. strtok函数源码
  4. C51模拟PS2键盘(二)
  5. 计算机基础知识(基础入门小白专属)十
  6. js jquery 计算两个时间差
  7. JS两个日期之间计算时间差(返回:天数,小时,分钟,秒数)
  8. Java基础——Java多继承的三种实现方式
  9. 多元函数的泰勒展开(Taylor series expansion)
  10. java大作业设计_Java程序设计_大作业.doc