英文:
What is the best way to create a view in android that looks like a progress bar but the progress can be scatered depending on where you want it to be
问题
我有一个返回这种类型数据的 API:
"timeRanges": [
{
"start": 25201,
"end": 32399
},
{
"start": 68401,
"end": 82799
},
{
"start": 111601,
"end": 118799
},
这些数据表示以秒为单位的时间间隔,我需要以这种方式表示:
[![点击这里查看图片描述](https://i.stack.imgur.com/cbgAU.png)](https://i.stack.imgur.com/cbgAU.png)
我的问题是:如何创建这个视图,并使每个绿线可点击
我尝试过使用 RangeSlider,但它不支持添加多个范围
我还尝试过使用水平堆叠条形图,但结果不符合我的需求
英文:
I have an api that returns this type of data :
"timeRanges": [
{
"start": 25201,
"end": 32399
},
{
"start": 68401,
"end": 82799
},
{
"start": 111601,
"end": 118799
},
this data represent time intervals in seconds and i need to represent it in this way:
My question is: how can i create that view and make each green line clickable
i tried to use RangeSlider but it doesn't support adding multiple ranges
and i tried to use horizontal stacked bar graph but the results wasn't like what i need
答案1
得分: 0
我写了一个自定义视图,绘制了条形和其中的范围
英文:
i wrote a custom view that draws the bar and the ranges inside it
package com.example.material3app
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.core.content.ContextCompat
/**
* Created by Khmaies Hassen on 15,March,2023
*/
class BarView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint()
private var barHeight = 20
private var valueFrom = 0
private var valueTo = 100
private val ranges = mutableListOf<Range>()
private var backgroundColor = Color.WHITE
private var cornerRadius = 10f
private var selectedRange: Range? = null
private var onRangeSelectedListener: OnRangeSelectedListener? = null
private var trackDrawable : Drawable? = null
init {
paint.style = Paint.Style.FILL
if (trackDrawable == null) {
trackDrawable = ContextCompat.getDrawable(
getContext(),
R.drawable.multislider_track_material
)
}
}
fun setBarHeight(height: Int) {
barHeight = height
}
fun getRanges(): List<Range> {
return ranges
}
fun setValueFrom(value: Int) {
valueFrom = value
}
fun setValueTo(value: Int) {
valueTo = value
}
fun addRange(start: Int, end: Int, color: Int) {
ranges.add(Range(start, end, color))
}
override fun setBackgroundColor(color: Int) {
backgroundColor = color
}
fun setTrackBackground(track: Drawable?) {
trackDrawable = track
}
fun setCornerRadius(radius: Float) {
cornerRadius = radius
}
fun setOnRangeSelectedListener(listener: OnRangeSelectedListener) {
onRangeSelectedListener = listener
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = barHeight.toFloat().toInt()
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw the track
trackDrawable?.let {
it.setBounds( 0, 0, width, height)
it.draw(canvas)
}
for (range in ranges) {
paint.color = range.color
// Calculate range left and right coordinates, clamping them to the view bounds
val rangeLeft = ((range.start - valueFrom).toFloat() / (valueTo - valueFrom) * width).clamp(0f, width.toFloat())
val rangeRight = ((range.end - valueFrom).toFloat() / (valueTo - valueFrom) * width).clamp(0f, width.toFloat())
// Draw rounded rectangle for range
range.rangeRect.set(rangeLeft, 0f, rangeRight, height.toFloat())
canvas.drawRoundRect(range.rangeRect, cornerRadius, cornerRadius, paint)
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
trackDrawable?.setBounds(0, 0, w, h)
}
override fun setBackground(background: Drawable?) {
super.setBackground(background)
trackDrawable = background
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
for (range in ranges) {
if(range.rangeRect.contains(event.x,event.y)) {
onRangeSelectedListener?.onRangeSelected(range.start, range.end)
selectedRange = range
invalidate()
return true
}
}
}
}
return super.onTouchEvent(event)
}
inner class Range(val start: Int, val end: Int, val color: Int) {
val rangeRect = RectF()
}
interface OnRangeSelectedListener {
fun onRangeSelected(start: Int, end: Int)
}
}
fun Float.clamp(minValue: Float, maxValue: Float): Float {
return when {
this < minValue -> minValue
this > maxValue -> maxValue
else -> this
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论