一、SpeedView
<com.github.anastr.speedviewlib.SpeedView
android:id="@+id/speedView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_withIndicatorLight="true"/>
<Button
android:id="@+id/b_open_control"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="control" />
speedView = findViewById(R.id.speedView)
ok = findViewById(R.id.ok)
ok.setOnClickListener {
// 设置值
speedView.speedTo(25f)
}
// 数值回调监听
speedView.speedTextListener = { speed: Float? -> String.format(Locale.getDefault(), "lol%.0f", speed) }
speedView.clearSections()
// 设置外圆样式,从0-0.1颜色为LTGRAY,宽度为30f,样式默认为BUTT,也可设置为Style.ROUND/Style.BUTT
// 源码分析:
// class Section @JvmOverloads constructor(
// startOffset: Float, 开始的偏移量
// endOffset: Float, 结束的偏移量
// color: Int, 颜色
// width: Float = 0f, 宽度
// style: Style = Style.BUTT, 样式
//): Parcelable {
speedView.addSections(
Section(0f, .1f, Color.LTGRAY, speedView.dpTOpx(30f)),
Section(.1f, .2f, Color.GRAY, speedView.dpTOpx(30f), Style.ROUND),
Section(.2f, .3f, Color.DKGRAY, speedView.dpTOpx(30f)),
Section(.3f, .4f, Color.BLACK, speedView.dpTOpx(30f)),
Section(.4f, .5f, Color.CYAN, speedView.dpTOpx(30f), Style.ROUND),
Section(.5f, .6f, Color.BLUE, speedView.dpTOpx(30f)),
Section(.6f, .7f, Color.GREEN, speedView.dpTOpx(30f)),
Section(.7f, .8f, Color.YELLOW, speedView.dpTOpx(30f)),
Section(.8f, .9f, Color.MAGENTA, speedView.dpTOpx(30f))
)
// Section的监听回调previousSection是指针滑动过的部分,newSection是被指定的部分
speedView.onSectionChangeListener = { previousSection: Section?, newSection: Section? ->
if (previousSection != null) {
// previousSection.setPadding(10);
previousSection.width = speedView.dpTOpx(3f)
}
if (newSection != null) {
// newSection.setPadding(0);
newSection.width = speedView.dpTOpx(15f)
}
Unit
}
setSpeedAt:该函数 setSpeedAt 用于立即设置速度值,不带动画效果,具体逻辑如下:
限制速度范围:将传入的 speed 限制在 maxSpeed 和 minSpeed 之间;
判断加速方向:更新 isSpeedIncrease 表示当前是否为加速状态;
更新速度值:同时更新 speed 和 currentSpeed;
取消动画:调用 cancelSpeedAnimator() 停止可能存在的动画;
刷新视图并抖动:调用 invalidate() 刷新界面,tremble() 执行抖动效果。
speedPercentTo:该函数 speedPercentTo 的功能是将指定的百分比值转换为实际速度值,并在给定的动画时长内平滑过渡到该速度。
参数说明:
percent:百分比值(0 到 100)。
moveDuration:动画持续时间,默认为 2000 毫秒。
功能逻辑:
调用 getSpeedValue(percent.toFloat()) 将百分比转为实际速度值。
再调用 speedTo(...) 执行带动画的速度变化。
注解作用:
@JvmOverloads 支持在 Java 中调用时使用默认参数。
speedTo:该函数 speedTo 用于平滑地将速度从当前值动画过渡到目标速度,并确保速度始终在 [minSpeed, maxSpeed] 范围内。
具体逻辑如下:
限制速度范围:若传入的 speed 超出 [minSpeed, maxSpeed],则自动修正为边界值。
判断是否变化:如果新速度与当前速度相同,则直接返回不执行动画。
设置动画方向:记录是加速还是减速(isSpeedIncrease)。
取消旧动画:调用 cancelSpeedAnimator() 停止正在进行的速度动画。
创建并启动动画:使用 ValueAnimator 实现平滑过渡,使用 DecelerateInterpolator 实现减速效果,并通过监听器更新当前速度和刷新界面。
stop():该 stop() 函数用于停止速度表动画,并根据条件触发震动效果。具体逻辑如下:
如果 speedAnimator 和 realSpeedAnimator 都不在运行,直接返回;
将当前速度保存到 speed;
取消速度动画;
调用 tremble() 方法产生震动效果。
realSpeedTo :该函数 realSpeedTo 用于模拟速度变化的真实感动画,具体功能如下:
限制速度范围:确保目标速度不超过最大/最小值。
判断加速或减速:根据当前速度与目标速度比较,决定是加速还是减速。
创建属性动画:使用 ValueAnimator 实现平滑过渡效果。
动态更新当前速度:
加速时:按 accelerate 值缓慢增加;
减速时:按 decelerate 值快速减少;
刷新界面并结束动画:当速度达到目标值时停止动画并重绘视图。
speedView.clearSections(): 该函数用于移除所有仪表盘中的“section”区域,并刷新视图。具体逻辑如下:
遍历 _sections 列表,调用每个元素的 clearGauge() 方法,清除对应区域的仪表数据;
清空 _sections 列表;
调用 invalidateGauge() 方法,触发视图重绘。
其他设置
设置最大值:
speedView.maxSpeed = 50f
设置仪表圆外边框的宽度:
speedView.speedometerWidth = 5
设置数值文本的字体大小:
speedView.speedTextSize = 5f
设置指针颜色:
speedView.indicator.color = Color.parseColor("#000000")
设置中间圆点的颜色:
speedView.centerCircleColor = Color.parseColor("#000000")
设置区间样式:
private fun setLowSpeedColor() {
val lowSpeedColor = findViewById<EditText>(R.id.lowSpeedColor)
try {
speedView.sections[0].startOffset = 0f
speedView.sections[0].endOffset = .1f
speedView.sections[0].color = Color.parseColor(lowSpeedColor.text.toString())
} catch (e: Exception) {
lowSpeedColor.error = e.message
}
}
private fun setMediumSpeedColor() {
val mediumSpeedColor = findViewById<EditText>(R.id.mediumSpeedColor)
try {
speedView.sections[1].startOffset = .1f
speedView.sections[1].endOffset = 0.8f
speedView.sections[1].color = Color.parseColor(mediumSpeedColor.text.toString())
} catch (e: Exception) {
mediumSpeedColor.error = e.message
}
}
private fun setHighSpeedColor() {
val highSpeedColor = findViewById<EditText>(R.id.highSpeedColor)
try {
speedView.sections[2].startOffset = .8f
speedView.sections[2].endOffset = 1f
speedView.sections[2].color = Color.parseColor(highSpeedColor.text.toString())
} catch (e: Exception) {
highSpeedColor.error = e.message
}
}
是否启用抖动效果:
speedView.withTremble = true
是否启用其他特效:
speedView.isWithEffects = true
设置刻度线密度(数量):
speedView.setDegreeBetweenMark(10)
设置刻度线宽度:
speedView.rayMarkWidth = 10f
二、Pointer Speedometer
<com.github.anastr.speedviewlib.PointerSpeedometer
android:id="@+id/pointerSpeedometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_unitTextSize="15sp"
app:sv_indicatorLightColor="@android:color/white"
app:sv_withIndicatorLight="true"
app:sv_speedTextTypeface="fonts/good-times.ttf" />
// 监听回调
pointerSpeedometer.onSpeedChangeListener =
{ gauge: Gauge, _: Boolean?, _: Boolean? ->
textSpeedChange.text = String.format(
Locale.getDefault(), "onSpeedChange %d", gauge.currentIntSpeed
)
}
三、Tube Speedometer
<com.github.anastr.speedviewlib.TubeSpeedometer
android:id="@+id/speedometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_speedTextSize="22sp"
app:sv_speedTextTypeface="fonts/James-Almacen.ttf"/>
四、Image Speedometer
<com.github.anastr.speedviewlib.ImageSpeedometer
android:id="@+id/imageSpeedometer"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_image="@drawable/for_image_speedometer"
app:sv_indicator="LineIndicator"
app:sv_indicatorColor="#a750bcff"
app:sv_speedTextColor="#fff"
app:sv_unitTextColor="#fff"/>
四、Progressive Gauge
<com.github.anastr.speedviewlib.ProgressiveGauge
android:id="@+id/gauge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_speedTextPosition="BOTTOM_RIGHT"
app:sv_unitUnderSpeedText="false"
app:sv_speedTextSize="25dp"
app:sv_unitTextSize="18dp"
app:sv_speedTextColor="#fff"
app:sv_unitTextColor="#fff"
app:sv_speedometerBackColor="#b3b3b3"
app:sv_speedometerColor="#dc4ae1" />
五、Image Linear Gauge
<com.github.anastr.speedviewlib.ImageLinearGauge
android:id="@+id/gauge"
android:layout_width="wrap_content"
android:layout_height="300dp"
android:layout_gravity="center_horizontal"
app:sv_orientation="HORIZONTAL"
app:sv_speedTextPosition="BOTTOM_CENTER"
app:sv_unitUnderSpeedText="false"
app:sv_speedometerBackColor="#9e9e9e"
app:sv_image="@drawable/fire"/>
checkBoxOrientation.setOnCheckedChangeListener { _, b ->
if (b) imageLinearGauge.orientation =
LinearGauge.Orientation.VERTICAL else imageLinearGauge.orientation =
LinearGauge.Orientation.HORIZONTAL
}
六、指针设置
<com.github.anastr.speedviewlib.SpeedView
android:id="@+id/speedometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_speedTextSize="11sp"
app:sv_unitTextSize="8sp"
app:sv_textSize="8sp"
app:sv_startDegree="110"
app:sv_endDegree="430"
app:sv_speedometerWidth="45dp"
app:sv_indicator="NormalIndicator"
app:sv_indicatorWidth="20dp"
app:sv_centerCircleColor="#0000"
app:sv_indicatorColor="#df4346" />
设置指针样式:
speedometer.setIndicator(Indicator.Indicators.NoIndicator)
设置指针宽度:
speedometer.indicator.width = 10f
设置自定义指针样式:
val imageIndicator = ImageIndicator(applicationContext, ContextCompat.getDrawable(this, R.drawable.image_indicator1)!! )
speedometer.indicator = imageIndicator
七、刻度线设置
<com.github.anastr.speedviewlib.SpeedView
android:id="@+id/speedometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_markColor="#414141"
app:sv_markWidth="3dp"/>
设置刻度线样式:
speedometer.markStyle = if(isChecked) Style.ROUND else Style.BUTT
设置刻度线数量:
speedometer.marksNumber = 10
设置刻度线高度:
speedometer.markHeight = 10f
设置刻度线宽度:
speedometer.markWidth = 10f
八、引导提示
<com.github.anastr.speedviewlib.SpeedView
android:id="@+id/speedView"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:padding="20dp"
app:sv_speedTextSize="23dp"
app:sv_indicatorColor="#03A9F4"
app:sv_centerCircleColor="#795548"/>
findViewById<View>(R.id.b_center).setOnClickListener { noteCenter() }
findViewById<View>(R.id.b_center_indicator).setOnClickListener { noteCenterIndicator() }
findViewById<View>(R.id.b_top_center).setOnClickListener { noteTopIndicator() }
findViewById<View>(R.id.b_image).setOnClickListener { noteImageNote() }
findViewById<View>(R.id.b_spannable).setOnClickListener { noteSpannableString() }
private fun noteCenter() {
val type = Typeface.createFromAsset(assets, "fonts/lhandw.ttf")
val note = TextNote(applicationContext, "Wait !")
.setPosition(Note.Position.CenterSpeedometer)
.setTextTypeFace(type)
.setTextSize(speedView.dpTOpx(20f))
speedView.addNote(note, 2000)
}
private fun noteCenterIndicator() {
val note = TextNote(applicationContext, "Stop !!")
.setPosition(Note.Position.CenterIndicator)
.setTextTypeFace(Typeface.create(Typeface.DEFAULT, Typeface.BOLD))
.setTextSize(speedView.dpTOpx(13f))
speedView.addNote(note, 2000)
}
private fun noteTopIndicator() {
val note = TextNote(applicationContext, "TOP")
.setPosition(Note.Position.TopIndicator)
.setAlign(Note.Align.Bottom)
.setTextSize(speedView.dpTOpx(13f))
speedView.addNote(note, 2000)
}
private fun noteImageNote() {
val imageNote = ImageNote(
applicationContext, R.mipmap.ic_launcher
)
.setPosition(Note.Position.BottomIndicator)
speedView.addNote(imageNote, 2000)
}
private fun noteSpannableString() {
val s = SpannableString("Speedometer")
s.setSpan(RelativeSizeSpan(1.2f), 0, 11, 0)
s.setSpan(RelativeSizeSpan(1.7f), 0, 1, 0)
s.setSpan(ForegroundColorSpan(Color.parseColor("#64DD17")), 0, 1, 0)
s.setSpan(ForegroundColorSpan(Color.parseColor("#FF1744")), 1, 5, 0)
s.setSpan(ForegroundColorSpan(Color.parseColor("#AA00FF")), 5, 6, 0)
s.setSpan(RelativeSizeSpan(1.4f), 5, 6, 0)
s.setSpan(ForegroundColorSpan(Color.parseColor("#2196F3")), 6, 11, 0)
val note = TextNote(applicationContext, s)
.setBackgroundColor(Color.parseColor("#EEFF41"))
.setPosition(Note.Position.QuarterSpeedometer)
.setTextSize(speedView.dpTOpx(10f))
speedView.addNote(note, 2000)
}
九、刻度数值
<com.github.anastr.speedviewlib.SpeedView
android:id="@+id/speedometer"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_tickNumber="6"/>
设置刻度数值是否跟随指针旋转:
speedometer.isTickRotation = true
设置刻度数值显示数量:
speedometer.tickNumber = 10
设置刻度数值位置:
speedometer.tickPadding = 10f
十、偏移量X,Y
<com.github.anastr.speedviewlib.SpeedView
android:id="@+id/speedometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:sv_centerCircleRadius="2dp"
app:sv_withTremble="false"
app:sv_indicator="NeedleIndicator" />
//该函数用于设置指示器旋转的支点位置,参数 xOffset 和 yOffset 表示在 X 和 Y 轴上的比例位置(范围为 [0f, 1f])
speedometer.setFulcrum(fulcrumX, fulcrumY)
speedometer.setFulcrum(.5f, .55f);
其他
设置圆边框部分样式(圆角还是平角):
app:sv_sectionStyle="ROUND"
设置圆边框部分样式颜色:
speedView.doOnSections { it.color = Color.rgb((0..255).random(), (0..255).random(), (0..255).random()) }
demo:
<RelativeLayout
android:layout_width="@dimen/dp_300"
android:layout_height="@dimen/dp_300">
<ImageView
android:layout_width="@dimen/dp_300"
android:layout_height="@dimen/dp_300"
android:src="@mipmap/icon_speed_panel"/>
<com.zn.core.panel.SpeedView
android:id="@+id/svSpeed"
app:sv_speedometerWidth="0dp"
app:sv_centerCircleColor="#123456"
app:sv_centerCircleRadius="@dimen/dp_25"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:sv_endDegree="380"
app:sv_indicator="KiteIndicator"
app:sv_indicatorColor="#eb1912"
app:sv_indicatorWidth="@dimen/dp_5"
app:sv_marksNumber="0"
app:sv_maxSpeed="40"
app:sv_minSpeed="0"
app:sv_speedTextColor="@color/white"
app:sv_speedTextFormat="INTEGER"
app:sv_speedTextSize="@dimen/dp_20"
app:sv_startDegree="160"
app:sv_textColor="@color/white"
app:sv_tickNumber="0"
app:sv_unit="x100 r/min"
app:sv_speedTextPadding="@dimen/dp_60"
app:sv_unitTextColor="@color/white"
app:sv_unitTextSize="@dimen/dp_20"
app:sv_unitUnderSpeedText="true"
app:sv_withTremble="false" />
</RelativeLayout>
<com.zn.core.panel.ImageSpeedometer
android:id="@+id/svSpeed"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:sv_endDegree="380"
app:sv_image="@mipmap/icon_speed_panel"
app:sv_indicator="KiteIndicator"
app:sv_indicatorColor="#eb1912"
app:sv_indicatorWidth="@dimen/dp_5"
app:sv_marksNumber="0"
app:sv_maxSpeed="40"
app:sv_minSpeed="0"
app:sv_speedTextColor="@color/white"
app:sv_speedTextFormat="INTEGER"
app:sv_speedTextPosition="CENTER_BOTTOM"
app:sv_speedTextSize="@dimen/dp_20"
app:sv_startDegree="160"
app:sv_textColor="@color/white"
app:sv_tickNumber="0"
app:sv_unit="x100 r/min"
app:sv_unitTextColor="@color/white"
app:sv_unitTextSize="@dimen/dp_20"
app:sv_unitUnderSpeedText="false"
app:sv_withTremble="false" />