Android自定义控件(一) 可滑动的进度条
前言
- 本篇文章记录通过自定义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.SlideView
为attrs.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)}}}
四、处理滑动事件
重写View
的OnTouchEvent
方法,需要判断手指按下的区域在外圆
的坐标值内,滑动的范围要限制在startX
和endX
之间
/*** 处理拖动事件*/@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
和文本获取到绘制区域的 top
和bottom
值如下图所示,这就造成为了绘制后不垂直的原因。因为文本绘制基线涉及需要大量篇幅去说明,这里就不详细解释。那要如何处理呢?其实很简单,让文本的基线
垂直于Y轴即可。
解决方法:
把绘制文本的top
和bottom
取中间值作为垂直于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自定义控件(一) 可滑动的进度条相关推荐
- android图标随着进度条动画,Android开发之ProgressBar字体随着进度条的加载而滚动...
在网上翻阅了很多关于ProgressBar滚动效果,但是始终没有找到适合项目中的这种效果,故自己写这篇文章,记录一下写作过程,给大家做一个参考.先看下最终效果效果图 我这里用的是LICEcap软件录制 ...
- android解压zip文件进度条,Android实现文件解压带进度条功能
解压的工具类 package com.example.videodemo.zip; public class ZipProgressUtil { /*** * 解压通用方法 * * @param zi ...
- Android自定义滑动进度条,Android自定义View实现圆形水波进度条
每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动,于是我开始了自定义View之路,虽然过程有坎坷,但是结果我还是挺满意的.我知道大牛还 ...
- Android自定义波浪加载圆形进度条——(自定义控件 一)
2019独角兽企业重金招聘Python工程师标准>>> 自定义控件-- 波浪形状圆形进度加载 时间管理的基础是精力管理,精力的高低.正负分影响到我们的效率 而时间是无法管理的,能够管 ...
- android 环形时间显示_Android圆形进度条颜色的设置
最近几天由于项目的需要研究了一下listView的滑动数据动态的更新显示,其中需要在数据加载过程有圆形进度条的显示,遇到的问题是进度条的颜色设置,在网上查了一些资料结合自己的所得分享在此. xml布局 ...
- android 图片处理过程中添加进度条,[Android] 随时拍图像处理部分总结及源码分......
? ? ? ? 随着寒假到来,终于有时间总结下Android这个随手拍的课程作业了,同时分享了我完成的这部分对图像处理的心得吧!你可以结合demo来阅读这篇文章,希望对大家有所帮助. ? ? ? ? ...
- android自定义渐变色圆环,Android实现渐变圆环、圆形进度条效果
最近做了一个功能,里面涉及到了渐变圆形的需求.就是一个颜色可以渐变的圆环,最后实现的效果如下图: 左图是带渐变效果,右图是不带渐变效果.原理还是绘图,Canvas可以绘制的对象有:弧线(arcs).填 ...
- android炫酷的自定义view,Android自定义View实现炫酷进度条
本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下 下面我们来实现如下效果: 第一步:创建attrs文件夹,自定义属性: 第二步:自定义View: /** * Cre ...
- android canvas_Android仿IOS11 控制中心进度条
这篇文章我们就用简单的自定义View实现类似iOS11的控制中心里边出现的很新颖的进度条. 先看一下目标效果图,以及最后的实现效果: 自定义属性分析 名字 释义 progressMax 最大值 pro ...
最新文章
- 2022-2028年中国超韧尼龙行业市场调查分析及未来前景分析报告
- java与mysql时间类型对应的问题
- java中怎么找类的路径_Java中获取类加载路径和项目根路径的5种方法
- android 启动器开发,Android启动器(Launcher)开发详解
- 学习指南_学习指南语文—成于素养,归于方法
- STM32学习:外部中断
- Aaron Swartz Rewriting Reddit中关于web.py的创建思路
- bui框架与php结合,bui框架前端自定义配色基础属性
- 计算机汇编语言编程教程,《汇编语言程序设计教程》.pdf
- 3DMAX场景渲染失败怎么办?
- MySql根据字段名查询重复记录并删除!只保留一条
- 程序员接单网站哪个好(盘点十大程序员接私活平台)
- 卧龙图说酒:喝酒的人生,是有意义的人生!
- 猴子定律--如何跳出你深陷的棋局看趋势
- 【Linux】如何查找命令及历史记录history
- uni-app分享小程序页面给微信好友;小程序分享无效原因;小程序分享失败原因;
- java基础系列(四)UTF-8和GBK编码的区别
- Redis之懒惰删除
- 2021年三门中学高考成绩查询,2021长沙市地区高考成绩排名查询,长沙市高考各高中成绩喜报榜单...
- MID:为3G而生,因3G引爆流行