自定义view-滑动进度条
Posted da_caoyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义view-滑动进度条相关的知识,希望对你有一定的参考价值。
好久没有写文章啦。记录一下此时的心情,哈哈。先上效果图:
布局
样式:
<declare-styleable name="SlidingScaleBarView">
<!--刻度文字的大小-->
<attr name="scaleTextSize" format="dimension" />
<!--刻度文字的颜色-->
<attr name="scaleTextColor" format="color" />
<!--最大值-->
<attr name="maxScaleValue" format="integer" />
<!--step-->
<attr name="step" format="integer" />
<!--精度,默认小数点0位-->
<attr name="precision" format="integer" />
<!--单位-->
<attr name="unit" format="string" />
<!--改变的步幅大小 默认1,如果 precision 精度不等于0,changeStep始终等于1 -->
<attr name="changeStep" format="integer" />
</declare-styleable>
布局文件:
<com.ypk.tablayout.view.fram.SlidingScaleBarView
android:id="@+id/scaleBarView5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
app:changeStep="100"
app:maxScaleValue="40"
app:precision="0"
app:scaleTextColor="@color/green"
app:scaleTextSize="12sp"
app:step="10"
app:unit="" />
自定义view完整代码 SlidingScaleBarView:
package com.ypk.tablayout.view.fram
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.ConvertUtils
import com.ypk.tablayout.R
/**
* Created by ypk on 2022/7/4
* 滑动刻度条
*
* 点击任意位置,就可以滑动
*/
class SlidingScaleBarView : View
private var viewWidth = 0f
private var viewHeight = 0f
private lateinit var bgPaint: Paint
private lateinit var bgSlidePaint: Paint
private lateinit var linePaint: Paint
private lateinit var scaleTextPaint: Paint
private lateinit var auxiliaryLinePaint: Paint
lateinit var bgRect: RectF
//背景进度条的高度
private var bgRectHeight = 0f
//背景矩形圆角大小
private var bgRoundedCorners = ConvertUtils.dp2px(10f).toFloat()
private var bgRectMarginHorizontal = 0f
//改变的步幅大小
var changeStep = 1
//最大刻度值
var maxScaleValue = 100;
var step = 10
//精度
var precision = 0
//分的份数
var number = 0
//分割后,每个间隔的宽度
var spaceWidth = 0f
//刻度线的高度
private var lineHeight = ConvertUtils.dp2px(8f).toFloat()
//刻度线的宽度
private var lineWidth = ConvertUtils.dp2px(2f).toFloat()
//刻度数字距离背景条的间距
private var scaleTextMarginTop = ConvertUtils.dp2px(3f).toFloat()
//滑块
private var sliceSpace = ConvertUtils.dp2px(5f).toFloat()
private var sliceWidth = ConvertUtils.dp2px(50f).toFloat()
private var sliceHeight = ConvertUtils.dp2px(25f).toFloat()
//刻度指示器图标
lateinit var bitmapIndicator: Bitmap
lateinit var bitmapSlide: Bitmap
//百分比单位
private var percentUnit = "%"
var colorArray = intArrayOf(
ContextCompat.getColor(context, R.color.green),
ContextCompat.getColor(context, R.color.tab_select_color),
ContextCompat.getColor(context, R.color.red_light)
)
var positionArray = floatArrayOf(0f, 0.8f, 1f)
var maxValue = 0f
var minValue = 0f
private var customizeViewDefaultHeight = ConvertUtils.dp2px(45f).toFloat() //自定义的控件默认高度
var mContext: Context
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
this.mContext = context
initAttr(context, attrs, defStyleAttr);
initData()
private var scaleTextSize = ConvertUtils.sp2px(15f).toFloat()
private var scaleTextColor = ContextCompat.getColor(context, R.color.white)
private fun initData()
//如果 precision 精度不等于0,changeStep要始终等于1
if (precision > 0)
changeStep = 1;
currentPercentageProgress = (maxScaleValue / 2).toFloat()
number = maxScaleValue / step
//控制条背景画笔
bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
bgPaint.isAntiAlias = true
bgPaint.style = Paint.Style.FILL
//bgPaint.color = ContextCompat.getColor(context, R.color.task_type_bg)
bgSlidePaint = Paint(Paint.ANTI_ALIAS_FLAG)
bgSlidePaint.isAntiAlias = true
bgSlidePaint.alpha = 180
linePaint = Paint(Paint.ANTI_ALIAS_FLAG)
linePaint.isAntiAlias = true
linePaint.style = Paint.Style.STROKE
linePaint.strokeWidth = lineWidth
linePaint.color = ContextCompat.getColor(context, R.color.white)
//刻度文字
scaleTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
scaleTextPaint.isAntiAlias = true
//scaleTextPaint.style = Paint.Style.FILL
//caleTextPaint.strokeWidth = lineWidth
scaleTextPaint.color = scaleTextColor
scaleTextPaint.textSize = scaleTextSize
bitmapIndicator = bitmapScale(
ConvertUtils.drawable2Bitmap(resources.getDrawable(R.mipmap.ic_slide_indicator2)),
1.4f
)
//辅助线画笔
auxiliaryLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
auxiliaryLinePaint.isAntiAlias = true
auxiliaryLinePaint.style = Paint.Style.STROKE
//auxiliaryLinePaint.strokeWidth = ringWidth
auxiliaryLinePaint.color = Color.RED
val rectText = Rect()
scaleTextPaint.getTextBounds("0", 0, 1, rectText)
customizeViewDefaultHeight =
sliceHeight + sliceSpace + bitmapIndicator.height + rectText.height() + scaleTextMarginTop
private fun initAttr(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
val typedArray =
context.obtainStyledAttributes(attrs, R.styleable.SlidingScaleBarView, defStyleAttr, 0)
val indexCount = typedArray.indexCount
for (i in 0 until indexCount)
val index = typedArray.getIndex(i)
when (index)
R.styleable.SlidingScaleBarView_scaleTextSize ->
scaleTextSize = typedArray.getDimension(index, scaleTextSize)
R.styleable.SlidingScaleBarView_scaleTextColor ->
scaleTextColor = typedArray.getColor(index, scaleTextColor)
R.styleable.SlidingScaleBarView_maxScaleValue ->
maxScaleValue = typedArray.getInteger(index, maxScaleValue)
R.styleable.SlidingScaleBarView_step ->
step = typedArray.getInteger(index, step)
R.styleable.SlidingScaleBarView_precision ->
precision = typedArray.getInteger(index, precision)
R.styleable.SlidingScaleBarView_unit ->
percentUnit = typedArray.getString(index).toString()
R.styleable.SlidingScaleBarView_changeStep ->
changeStep = typedArray.getInteger(index, 1)
typedArray.recycle()
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(widthMeasureSpec, measureHeight(heightMeasureSpec))
private fun measureHeight(measureSpec: Int): Int
var result = 0
val mode = MeasureSpec.getMode(measureSpec)
val size = MeasureSpec.getSize(measureSpec)
if (mode == MeasureSpec.EXACTLY) //表示父控件已经确切的指定了子View的大小。
result = size
// System.out.println("YPKTabLayoutView.measureHeight result1=" + result);
else if (mode == MeasureSpec.AT_MOST) //表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。
result = Math.min(customizeViewDefaultHeight.toInt(), size)
// System.out.println("YPKTabLayoutView.measureHeight result2=" + result);
else if (mode == MeasureSpec.UNSPECIFIED) //默认值,父控件没有给子view任何限制,子View可以设置为任意大小。
result = customizeViewDefaultHeight.toInt()
//System.out.println("YPKTabLayoutView.measureHeight result3=" + result);
return result
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int)
super.onSizeChanged(w, h, oldw, oldh)
viewWidth = (w - oldw).toFloat()
viewHeight = (h - oldh).toFloat()
//进度条的高度和指示器一样
bgRectHeight = bitmapIndicator.height.toFloat()
bgRectMarginHorizontal = sliceWidth / 2
spaceWidth = (viewWidth - bgRectMarginHorizontal * 2) / number
//spaceWidth = (viewWidth - bgRectMarginHorizontal * 2 - lineWidth * (number - 1)) / number
//进度条
bgRect = RectF(
bgRectMarginHorizontal,
sliceHeight + sliceSpace,
viewWidth - bgRectMarginHorizontal,
sliceHeight + sliceSpace + bgRectHeight
)
val linearGradient = LinearGradient(
0f,
0f,
bgRect.right,
bgRect.bottom,
colorArray,
positionArray,
Shader.TileMode.MIRROR
)
bgPaint.shader = linearGradient
maxValue = viewWidth - bgRectMarginHorizontal
minValue = bgRectMarginHorizontal
val tv = TextView(context)
// tv.setBackgroundColor(ContextCompat.getColor(context, R.color.gray))
tv.background = ContextCompat.getDrawable(context, R.drawable.shap_corners10_bg)
tv.width = sliceWidth.toInt()
tv.height = sliceHeight.toInt()
bitmapSlide = ConvertUtils.view2Bitmap(tv)
//设置默认值
setSlideProgress(currentPercentageProgress)
override fun onDraw(canvas: Canvas)
super.onDraw(canvas)
//绘制背景圆角矩形,也就是进度条
canvas.drawRoundRect(bgRect, bgRoundedCorners, bgRoundedCorners, bgPaint)
//绘制刻度线
for (i in 1 until number)
canvas.drawLine(
bgRectMarginHorizontal + spaceWidth * i,
sliceHeight + sliceSpace + bgRectHeight - lineHeight,
bgRectMarginHorizontal + spaceWidth * i,
sliceHeight + sliceSpace + bgRectHeight,
linePaint
)
//绘制数字刻度
drawNumberScaleBar(canvas)
//绘制指示器
canvas.drawBitmap(
bitmapIndicator,
currentProgress - (bitmapIndicator.width / 2),
sliceHeight + sliceSpace, null
)
//绘制top滑块
canvas.drawBitmap(
bitmapSlide,
currentProgress - (bitmapSlide.width / 2),
0f, bgSlidePaint
)
//绘制滑块中文字百分比
val formatValue = String.format("%.$precisionf", progressPercent)
val strPercentage = if (precision == 0)
"$formatValue.toInt() * changeStep$percentUnit"
else
"$formatValue$percentUnit"
val rectText = Rect()
scaleTextPaint.getTextBounds(strPercentage, 0, strPercentage.length, rectText)
canvas.drawText(
strPercentage,
currentProgress - rectText.width() / 2,
sliceHeight / 2 + rectText.height() / 2,
scaleTextPaint
)
/**
* 绘制数字刻度
*/
private fun drawNumberScaleBar(canvas: Canvas)
for (i in 0..number)
val scaleText = "$i * step * changeStep"
when (i)
0 ->
val rectText = Rect()
scaleTextPaint.getTextBounds(scaleText, 0, scaleText.length, rectText)
canvas.drawText(
scaleText,
bgRectMarginHorizontal + spaceWidth * i,
viewHeight,
scaleTextPaint
)
number ->
val rectText = Rect()
scaleTextPaint.getTextBounds(scaleText, 0, scaleText.length, rectText)
canvas.drawText(
scaleText,
bgRectMarginHorizontal + spaceWidth * i - rectText.width() / 2,
viewHeight,
scaleTextPaint
)
else ->
val rectText = Rect()
scaleTextPaint.getTextBounds(scaleText, 0, scaleText.length, rectText)
canvas.drawText(
scaleText,
bgRectMarginHorizontal + spaceWidth * i - rectText.width() / 2,
viewHeight,
scaleTextPaint
)
override fun onTouchEvent(event: MotionEvent): Boolean
if (event.x < minValue)
setProgress(minValue)
return true
else if (event.x > maxValue)
setProgress(maxValue)
return true
else
when (event.action)
MotionEvent.ACTION_DOWN ->
setProgress(event.x)
return true
MotionEvent.ACTION_MOVE ->
setProgress(event.x)
return true
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL ->
else ->
return super.onTouchEvent(event)
/**
* 计算触摸点的百分比
*
* @param eventX
* @return
*/
private fun calculateProgress(eventX: Float): Float
val proResult: Float =
(eventX - bgRectMarginHorizontal) / (viewWidth - bgRectMarginHorizontal * 2)
return proResult * maxScaleValue
//滑块的当前进度坐标
var currentProgress = 0f
//进度百分比
private var progressPercent: Float = 0f
private fun setProgress(x: Float)
currentProgress = x
val progress = calculateProgress(currentProgress)
progressPercent = progress
val formatValue = String.format("%.$precisionf", progress)
progressChangeListener?.invoke(formatValue)
invalidate()
// println("ScaleBarView2.setProgress progress=$progress")
var progressChangeListener: ((progress: String) -> Unit)? = null
var currentPercentageProgress = 0f
public fun setSlideProgress(percentageProgress: Float)
currentPercentageProgress = percentageProgress
//百分比进度
if (percentageProgress > maxScaleValue)
currentPercentageProgress = maxScaleValue.toFloat()
else if (percentageProgress < 0)
currentPercentageProgress = 0f
val currentProgress =
currentPercentageProgress / maxScaleValue * (viewWidth - bgRectMarginHorizontal * 2) + bgRectMarginHorizontal
setProgress(currentProgress)
private fun bitmapScale(bm: Bitmap, scale: Float): Bitmap
val m = Matrix()
m.setScale(scale, scale)
val bmpSrc = Bitmap.createBitmap(bm, 0, 0, bm.width, bm.height, m, true)
return bmpSrc
以上是关于自定义view-滑动进度条的主要内容,如果未能解决你的问题,请参考以下文章