拿到上边的UI作用图,给我的榜首印象便是这完成起来也太简略了吧,SeekBar轻轻松松就搞定了,换个thumb,加个突变不就完成了,说搞就搞,搞着搞着就郁闷了,底部坐标尺还能搞,等份额分割后,在SeekBar下面多设置几个TextView就行了,中心的等份额小分割线怎么搞?而且滑动前滑动后都需求有,并且,左右的分割线还要留出一小段间距,突变色彩要跟着滑动的间隔进行展现,而不是整个宽度展现,在多种条件下,SeekBar就很难满意这个需求了,怎么办?只能自定义了。
还是依照惯例,粗略的列一个纲要:
1、剖析要素,确认完成方案
2、首要代码进行刨析
3、开源地址及运用方法
4、总结
一、剖析要素,确认完成方案
Canvas制作这样的一个可拖拽坐标尺,基本上能够拆分出四部分,榜首部分便是布景和默许的离散间隔,第二部分是移动的布景和离散间隔,第三部分是移动的图片也便是thumb,最终一部分是底部的文字坐标。
四部分基本上就制作出来了,但是除了制作之外,还需求考虑一下其他的要素,比方高度,比方手指的移动事情等。
1、设置默许高度
设置默许高度的原因,是为了让View更好的展现一个适宜的尺度,不至于设置wrap_content时不展现,详细的设置能够依据当时设置的形式来操控,关于形式呢,有三种,这个在之前的文章中介绍过,这儿就不详细介绍了,当控件设置wrap_content时,此时的形式为MeasureSpec.AT_MOST,在这个形式下,咱们就要给一个默许的高度。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
var windowHeight = heightMeasureSpec
if (heightMode == MeasureSpec.AT_MOST) {
windowHeight = mDefaultHeight.toInt()//默许的高度
}
setMeasuredDimension(widthMeasureSpec, windowHeight)
}
2、拖动事情
完成拖动作用,咱们就需求监听用户的手指移动事情了,也便是在自定义View中咱们要重写onTouchEvent方法,在这个方法里,需求针对手指的按下、抬起、移动做相应的处理。
在onTouchEvent里我做了如下处理,一是直接回来,不履行事情的消费,目的是让自定义View可完成静态展现和动态展现两种作用,通过一个变量mProgressIsIntercept来操控;第二个是处理与父View的滑动冲突事情,在有横向或者纵向滑动事情时,在拖动的时候,难免会有冲突,那么就需求告诉父View不要消费事情,也便是履行requestDisallowInterceptTouchEvent方法。
一切的拖拽作用,都是在move事情,不断的改动坐标履行更新UI的方法完成的,mMoveProgress便是手指移动的坐标。
onTouchEvent(event: MotionEvent?): Boolean {
super.onTouchEvent(event)
//假如为true直接回来,不进行拖拽
if (mProgressIsIntercept) {
return mProgressIsIntercept
}
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
parent.requestDisallowInterceptTouchEvent(mDisallowIntercept)
val downX = getChangeX(event.x)
val startX = mMoveOldX - mProgressMarginLeftRight
val endX = mMoveOldX + mProgressMarginLeftRight
return downX in startX..endX
}
MotionEvent.ACTION_MOVE -> {
//移动
var moveX = getChangeX(event.x)
//滑动至最右边
//核算最终边的坐标
val viewWidth = getViewWidth()
if (moveX >= viewWidth) {
moveX = viewWidth
}
mMoveProgress = moveX
invalidate()
}
MotionEvent.ACTION_UP -> {
//手指谈起
mMoveOldX = getChangeX(event.x)
val viewWidth = getViewWidth()
if (mMoveOldX >= viewWidth) {
mMoveOldX = viewWidth
}
}
}
return true
}
二、首要代码进行刨析
1、制作布景
布景没什么好说的,便是一个简略的圆角矩形,运用drawRoundRect制作即可,需求确认的是左上右下的间距。
/**
* AUTHOR:AbnerMing
* INTRODUCE:制作布景
*/
private fun canvasBackground(canvas: Canvas) {
mPaint!!.color = mProgressBackground
val rect = RectF().apply {
left = mProgressMarginLeftRight
top = mProgressMarginTopBottom
right = width.toFloat() - mProgressMarginLeftRight
bottom = mProgressHeight + mProgressMarginTopBottom
}
canvas.drawRoundRect(rect, mProgressRadius, mProgressRadius, mPaint!!)
}
2、制作离散间隔
离散间隔,需求确认,间隔数,然后依据间隔数量,动态的核算每个间隔的方位,能够运用drawLine制作一个小小的竖线,竖线也需求确认间隔上下的间隔和自身的宽度;特殊情况下,离散间隔,在滑动前后的色彩是不相同的,所以这儿也做了一个动态改动色彩的判别。
/**
* AUTHOR:AbnerMing
* INTRODUCE:制作离散间隔
*/
private fun canvasIntervalLine(canvas: Canvas, isCanvas: Boolean) {
val rect =
(width - mProgressMarginLeftRight * 2 - mIntervalParentLeftRight * 2) / mIntervalSize
if (isCanvas) {
mPaint!!.color = mIntervalSelectColor
} else {
mPaint!!.color = mIntervalColor
}
mPaint!!.strokeWidth = mIntervalWidth
for (a in 0..mIntervalSize) {
val x = (rect * a) + mProgressMarginLeftRight + mIntervalParentLeftRight
val y = mIntervalMarginTopBottom + mProgressMarginTopBottom
canvas.drawLine(
x,
y,
x,
mProgressHeight + mProgressMarginTopBottom - mIntervalMarginTopBottom,
mPaint!!
)
}
}
3、制作移动thumb
关于thumb,首先要确认的便是巨细,假如设置了宽高,那么就需求运用Bitmap从头设置高度,改动thumb的坐标,只需求不断的改动图片的left坐标点即可,也便是通过上述的Move事情的中移动坐标来设置。
/**
* AUTHOR:AbnerMing
* INTRODUCE:制作移动的图标
*/
private fun canvasMoveIcon(canvas: Canvas) {
mProgressThumb?.let {
var decodeResource = BitmapFactory.decodeResource(resources, it)
mProgressThumbWidth = decodeResource.width
if (mThumbWidth != 0f) {
val height: Int = decodeResource.height
// 设置想要的巨细
val newWidth = mThumbWidth
val newHeight = mThumbHeight
// 核算缩放份额
val scaleWidth = newWidth / width
val scaleHeight = newHeight / height
// 取得想要缩放的matrix参数
val matrix = Matrix()
matrix.postScale(scaleWidth, scaleHeight)
// 得到新的图片
decodeResource =
Bitmap.createBitmap(decodeResource, 0, 0, width, height, matrix, true)
}
var mThumpLeft = mMoveProgress
if (mThumpLeft < (mProgressThumbWidth / 2 - mIntervalParentLeftRight + mProgressThumbSpacing)) {
mThumpLeft =
mProgressThumbWidth / 2 - mIntervalParentLeftRight + mProgressThumbSpacing
}
if (mThumpLeft > (getViewWidth() - mIntervalParentLeftRight + mProgressThumbSpacing)) {
mThumpLeft = getViewWidth() - mIntervalParentLeftRight + mProgressThumbSpacing
}
canvas.drawBitmap(
decodeResource, mThumpLeft, mThumbMarginTop, mIconPaint!!
)
}
}
4、制作移动进展
移动的进展,和布景的制作是相同的,只不过需求依照手指的坐标一点一点的移动间隔,也便是不断的改动右边的坐标值,相同的,也是通过Move事情中的mMoveProgress进展来动态的核算。进展的突变比较简略,运用的是画笔的shader特点,当时运用的是横向的线性突变LinearGradient。
/**
* AUTHOR:AbnerMing
* INTRODUCE:制作进展
*/
private fun canvasMoveProgress(canvas: Canvas) {
//为空
if (mColorArray.isEmpty()) {
mColorArray = intArrayOf(
ContextCompat.getColor(context, R.color.text_ff3e3e93),
ContextCompat.getColor(context, R.color.text_ff8548d2),
)
}
val linearShader = LinearGradient(
0f,
0f,
mMoveProgress + mProgressMarginLeftRight,
mProgressHeight,
mColorArray,
floatArrayOf(0f, 1f),
Shader.TileMode.CLAMP
)
mProgressPaint!!.shader = linearShader
//等于0时
val rect = RectF()
rect.left = mProgressMarginLeftRight
rect.top = mProgressMarginTopBottom
rect.right = mMoveProgress + mProgressMarginLeftRight
rect.bottom = mProgressHeight + mProgressMarginTopBottom
canvas.drawRoundRect(rect, mProgressRadius, mProgressRadius, mProgressPaint!!)
//核算份额
mGraduationResult =
((mMoveProgress / getViewWidth()) * mMaxProgress).roundToInt()//(endProgress * mMaxProgress).roundToInt()
if (mGraduationResult < 1) {
mGraduationResult = if (mGraduationSectionZero) {
0
} else {
1
}
}
if (mGraduationResult >= mMaxProgress) {
mGraduationResult = mMaxProgress
}
mMoveProgressCallback?.invoke(mGraduationResult)
}
5、制作文字刻度
其实咱们能够发现,离散间隔和底部的坐标文字刻度,其实是一一对应的,既然是相互相关,咱们直接放到一起就能够,也便是在遍历离散间隔的时候,咱们直接制作底部的坐标尺刻度。
坐标刻度,有四种作用,榜首种是不要刻度值,第二种是只需开端和结尾刻度值,第三种是展现一切的刻度值,第四种是刻度值是从0还是从1开端。
mIsGraduation是用于判别是否需求刻度值的变量,为true则需求制作,不然就不制作,也便是不需求刻度值。mHideGraduationSectionCenter为躲藏中心刻度的变量,为true躲藏,不然为不躲藏,详细的代码如下:
/**
* AUTHOR:AbnerMing
* INTRODUCE:制作离散间隔
*/
private fun canvasIntervalLine(canvas: Canvas, isCanvas: Boolean) {
val rect =
(width - mProgressMarginLeftRight * 2 - mIntervalParentLeftRight * 2) / mIntervalSize
if (isCanvas) {
mPaint!!.color = mIntervalSelectColor
} else {
mPaint!!.color = mIntervalColor
}
mPaint!!.strokeWidth = mIntervalWidth
for (a in 0..mIntervalSize) {
val x = (rect * a) + mProgressMarginLeftRight + mIntervalParentLeftRight
val y = mIntervalMarginTopBottom + mProgressMarginTopBottom
canvas.drawLine(
x,
y,
x,
mProgressHeight + mProgressMarginTopBottom - mIntervalMarginTopBottom,
mPaint!!
)
//制作刻度值
if (mIsGraduation && isCanvas) {
if (mHideGraduationSectionCenter && (a != 0 && a != mIntervalSize)) {
//躲藏中心
continue
}
var graduation = a * mGraduationSection
//是否从0开端记录
if (graduation == 0 && !mGraduationSectionZero) {
graduation = 1
}
//假如移动到了,改动色彩
if (mGraduationResult >= graduation && mGraduationResult < graduation + mGraduationSection) {
mGraduationPaint?.color = mGraduationSelectTextColor
} else {
mGraduationPaint?.color = mGraduationTextColor
}
val text = graduation.toString()
val rectText = Rect()
mGraduationPaint!!.getTextBounds(text, 0, text.length, rectText)
val textWidth = rectText.width()
val textHeight = rectText.height()
canvas.drawText(
text,
x - textWidth / 2,
mProgressHeight + mProgressMarginTopBottom * 2 + textHeight + mGraduationMarginTop,
mGraduationPaint!!
)
}
}
}
三、开源地址及运用方法
目前现已上传到了Github,本身就一个简略的类,没多少东西,需求的铁子,能够直接检查源码即可。
地址:github.com/AbnerMing88…
假如懒得下载源码,想直接运用,没得问题,我现已上传到了远程Maven,咱们能够依赖运用。
1、在你的根项目下的build.gradle文件下,引进maven。
allprojects {
repositories {
maven { url "https://gitee.com/AbnerAndroid/almighty/raw/master" }
}
}
2、在你需求运用的Module中build.gradle文件下,引进依赖。
dependencies {
implementation 'com.vip:moveprogress:1.0.0'
}
3、XML引进即可
<com.vip.moveprogress.MoveProgress
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:ms_graduation_hide_center="true" />
相关特点
特点 | 类型 | 概述 |
---|---|---|
ms_height | dimension | View视图的高度 |
ms_progress_height | dimension | 进展条的高度 |
ms_progress_thumb | reference | 进展条的Icon |
ms_progress_margin_top_bottom | dimension | 进展条间隔icon的上下间隔 |
ms_progress_margin_left_right | dimension | 进展条间隔左右的边距 |
ms_progress_radius | dimension | 进展条的圆角 |
ms_progress_background | color | 进展条的布景色彩 |
ms_interval_color | color | 间隔线色彩 |
ms_interval_select_color | color | 间隔线选中色彩 |
ms_interval_parent_margin_left_right | dimension | 间隔线间隔父左右 |
ms_interval_size | integer | 间隔线数量 |
ms_interval_width | dimension | 间隔线宽度 |
ms_interval_margin_top_bottom | dimension | 间隔线上下边距 |
ms_progress_move_color | reference | 定义的移动色彩 |
ms_progress_max | integer | 最大进展 |
ms_progress_default | integer | 默许进展 |
ms_is_graduation | boolean | 是否显现刻度尺 |
ms_graduation_text_size | dimension | 刻度尺文字巨细 |
ms_graduation_text_color | color | 刻度尺文字色彩 |
ms_graduation_select_text_color | color | 刻度尺文字选中色彩 |
ms_graduation_section | integer | 刻度值段 |
ms_graduation_section_zero | boolean | 刻度值段从零开端 |
ms_graduation_hide_center | boolean | 刻度值段中心是否躲藏 |
ms_graduation_margin_top | dimension | 刻度值间隔上边的间隔 |
ms_progress_thumb_width | dimension | icon的宽 |
ms_progress_thumb_height | dimension | icon的高 |
ms_progress_thumb_margin_top | dimension | icon间隔上边的高度 |
ms_progress_thumb_spacing | dimension | icon的内边距 |
ms_progress_disallow_intercept | boolean | 是否阻拦 |
ms_progress_is_intercept | boolean | 是否禁止拖拽 |
相关方法
方法 | 参数 | 概述 |
---|---|---|
getProgress | 无参 | 回来当时进展 |
changeProgress | Int | 改动当时进展 |
getMoveProgress | 回来Int | 回调函数 |
setProgressIsIntercept | Boolean | 设置是否进行阻拦 |
四、总结
关于突变,需求留意,突变的规模不是默许的从左到右固定的间隔,而是从左到手指滑动的间隔,这一点需求留意,也便是在设置突变的时候,终止的X坐标需求依据手势的左边动态设置。
从这个简略的拖拽进展条,咱们能够了解到,canvas制作线,圆角矩形,图片以及和手势结合的相关知识点,本身并没有难点。