247 lines
8.3 KiB
Kotlin
247 lines
8.3 KiB
Kotlin
package io.xdrm.lebonprix.anim
|
|
|
|
import android.content.Context
|
|
import android.content.res.AssetManager
|
|
import android.content.res.Resources
|
|
import android.graphics.*
|
|
import android.support.v4.content.res.ResourcesCompat
|
|
import android.util.Log
|
|
import android.view.animation.Animation
|
|
import android.view.animation.BounceInterpolator
|
|
import android.view.animation.CycleInterpolator
|
|
import android.view.animation.Transformation
|
|
import android.widget.ImageView
|
|
import io.xdrm.lebonprix.R
|
|
import kotlin.math.round
|
|
|
|
class ResultRangeAnimation(
|
|
private val ctx: Context,
|
|
private val target: ImageView,
|
|
private val originalWidth: Int,
|
|
private val originalHeight: Int
|
|
) : Animation() {
|
|
|
|
|
|
private var width: Int = 1000
|
|
private var height: Int = 1000
|
|
|
|
private var bitmap : Bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
private var canvas : Canvas = Canvas(bitmap)
|
|
|
|
private var progress: Float = 0F
|
|
private val relativeSliderWidth = 0.75F
|
|
private var w = 0F
|
|
private var h = 0F
|
|
|
|
// actual data to display
|
|
private var size: Float = 0F
|
|
private var min: Float = 0F
|
|
private var avg: Float = 0F
|
|
private var max: Float = 0F
|
|
fun setData(_size: Int, _min: Float, _avg: Float, _max: Float){
|
|
require(_avg > _min); require(_avg < _max)
|
|
|
|
size = _size.toFloat()
|
|
min = _min;
|
|
avg = _avg;
|
|
max = _max
|
|
}
|
|
|
|
init {
|
|
setSize(originalWidth, originalHeight)
|
|
|
|
target.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
|
|
setSize(right-left, bottom-top)
|
|
}
|
|
}
|
|
|
|
override fun start() {
|
|
target.startAnimation(this)
|
|
}
|
|
|
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
|
|
super.applyTransformation(interpolatedTime, t)
|
|
progress = interpolatedTime
|
|
draw()
|
|
}
|
|
|
|
private fun setSize(_width: Int, _height: Int){
|
|
width = _width
|
|
height = _height
|
|
|
|
// unregister previous
|
|
target.setImageBitmap(null)
|
|
canvas.setBitmap(null)
|
|
bitmap.recycle()
|
|
|
|
// build new
|
|
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
canvas = Canvas(bitmap)
|
|
target.setImageBitmap(bitmap)
|
|
}
|
|
|
|
|
|
private fun draw(){
|
|
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
|
|
|
|
w = width * 1F
|
|
h = height * 0.6F
|
|
|
|
drawSlider()
|
|
drawAverage()
|
|
|
|
target.setImageBitmap(bitmap)
|
|
}
|
|
|
|
|
|
private fun drawSlider(){
|
|
|
|
// params
|
|
val sliderThickness = 5F
|
|
val sliderDotRadius = 10F
|
|
val relativeCaptionSize = 0.04F
|
|
val relativeValuesSize = 0.04F
|
|
val relativeCaptionDistance = 0.2F
|
|
val relativeValuesDistance = 0.1F
|
|
|
|
// preprocessed values
|
|
val sliderWidth = w * relativeSliderWidth
|
|
val xoffset = (w - sliderWidth) * 0.5F
|
|
val captionsDistance = sliderDotRadius + h*relativeCaptionDistance
|
|
val valuesDistance = sliderDotRadius + h*relativeValuesDistance
|
|
val average = 0.5F + progress*( (avg-min)/(max-min) - 0.5F)
|
|
val averageX = sliderWidth * average
|
|
// val averageX = sliderWidth * (avg-min) / (max-min)
|
|
|
|
// paints
|
|
val sliderPaint = Paint()
|
|
sliderPaint.style = Paint.Style.STROKE
|
|
sliderPaint.strokeCap = Paint.Cap.ROUND
|
|
sliderPaint.strokeWidth = sliderThickness
|
|
sliderPaint.color = Color.WHITE
|
|
|
|
val dotPaint = Paint()
|
|
dotPaint.color = Color.WHITE
|
|
|
|
val roboto = ResourcesCompat.getFont(ctx, R.font.roboto)
|
|
|
|
val captionLeftPaint = Paint()
|
|
captionLeftPaint.typeface = roboto
|
|
captionLeftPaint.textAlign = Paint.Align.LEFT
|
|
captionLeftPaint.color = ResourcesCompat.getColor(ctx.resources, R.color.soft_grey, null)
|
|
captionLeftPaint.textSize = relativeCaptionSize*w
|
|
|
|
val captionRightPaint = Paint()
|
|
captionRightPaint.typeface = roboto
|
|
captionRightPaint.textAlign = Paint.Align.RIGHT
|
|
captionRightPaint.color = ResourcesCompat.getColor(ctx.resources, R.color.soft_grey, null)
|
|
captionRightPaint.textSize = relativeCaptionSize*w
|
|
|
|
val valuesLeftPaint = Paint()
|
|
valuesLeftPaint.typeface = roboto
|
|
valuesLeftPaint.textAlign = Paint.Align.LEFT
|
|
valuesLeftPaint.color = Color.WHITE
|
|
valuesLeftPaint.textSize = relativeValuesSize*w
|
|
|
|
val valuesRightPaint = Paint()
|
|
valuesRightPaint.typeface = roboto
|
|
valuesRightPaint.textAlign = Paint.Align.RIGHT
|
|
valuesRightPaint.color = Color.WHITE
|
|
valuesRightPaint.textSize = relativeValuesSize*w
|
|
|
|
// (1) draw slider
|
|
val slider = Path()
|
|
slider.moveTo(xoffset, h/2)
|
|
slider.lineTo(xoffset+sliderWidth, h/2)
|
|
canvas.drawPath(slider, sliderPaint)
|
|
|
|
// (2) draw slider dots
|
|
// canvas.drawCircle(xoffset, h/2, sliderDotRadius, dotPaint)
|
|
// canvas.drawCircle(xoffset+sliderWidth, h/2, sliderDotRadius, dotPaint)
|
|
canvas.drawCircle(xoffset+averageX, h/2, sliderDotRadius, dotPaint)
|
|
|
|
// (3) draw min+max captions
|
|
canvas.drawText("min", xoffset, h * 0.5F + captionsDistance, captionLeftPaint)
|
|
canvas.drawText("max", xoffset+sliderWidth, h/2 + captionsDistance, captionRightPaint)
|
|
|
|
// (4) draw min+max values
|
|
canvas.drawText("${min}€", xoffset, h/2 - valuesDistance, valuesLeftPaint)
|
|
canvas.drawText("${max}€", xoffset+sliderWidth, h/2 - valuesDistance, valuesRightPaint)
|
|
|
|
}
|
|
|
|
|
|
private fun drawAverage(){
|
|
val relativeSampleSize = 0.03F
|
|
|
|
// params
|
|
val relativeTriangleWidth = 0.04F
|
|
val relativeTriangleHeight = 0.2F
|
|
val relativeTooltipWidth = 0.2F
|
|
val relativeTooltipHeight = 0.7F
|
|
|
|
// preprocessed values
|
|
val sliderWidth = w * relativeSliderWidth
|
|
val xoffset = (w - sliderWidth) * 0.5F
|
|
val yoffset = height - h
|
|
// val averageX = sliderWidth * (avg-min) / (max-min)
|
|
val average = 0.5F + progress*( (avg-min)/(max-min) - 0.5F)
|
|
val averageX = sliderWidth * average
|
|
|
|
val triangleWidth = relativeTriangleWidth * w
|
|
val triangleHeight = relativeTriangleHeight * yoffset
|
|
|
|
val tooltipWidth = relativeTooltipWidth * w
|
|
val tooltipHeight = relativeTooltipHeight * yoffset
|
|
|
|
// (1) Draw triangle
|
|
val whitePaint = Paint()
|
|
whitePaint.color = Color.WHITE
|
|
whitePaint.flags = Paint.ANTI_ALIAS_FLAG
|
|
|
|
val triangle = Path()
|
|
triangle.moveTo(xoffset+averageX, yoffset)
|
|
triangle.lineTo(xoffset+averageX-triangleWidth, yoffset + triangleHeight + 1F) // add 1 to overlap
|
|
triangle.lineTo(xoffset+averageX+triangleWidth, yoffset + triangleHeight + 1F)
|
|
canvas.drawPath(triangle, whitePaint)
|
|
|
|
// (2) Draw tooltip background
|
|
canvas.drawRoundRect(
|
|
RectF(
|
|
xoffset+averageX - tooltipWidth/2,
|
|
yoffset + triangleHeight,
|
|
xoffset+averageX + tooltipWidth/2,
|
|
yoffset + triangleHeight + tooltipHeight),
|
|
10F, 10F,
|
|
whitePaint)
|
|
|
|
|
|
// (3) Draw sample size
|
|
val sampleSizePaint = Paint()
|
|
sampleSizePaint.typeface = ResourcesCompat.getFont(ctx, R.font.roboto)
|
|
sampleSizePaint.textAlign = Paint.Align.CENTER
|
|
sampleSizePaint.color = ResourcesCompat.getColor(ctx.resources, R.color.soft_grey, null)
|
|
sampleSizePaint.textSize = relativeSampleSize*w
|
|
sampleSizePaint.flags = Paint.ANTI_ALIAS_FLAG
|
|
|
|
canvas.drawText("sur ${size.toInt()} articles", xoffset+averageX, yoffset + triangleHeight + tooltipHeight + relativeSampleSize*w, sampleSizePaint)
|
|
|
|
// (4) Draw average text
|
|
val roboto = ResourcesCompat.getFont(ctx, R.font.roboto)
|
|
|
|
val averageTextPaint = Paint()
|
|
averageTextPaint.typeface = roboto
|
|
averageTextPaint.textAlign = Paint.Align.CENTER
|
|
averageTextPaint.color = ResourcesCompat.getColor(ctx.resources, R.color.colorAccent, null)
|
|
averageTextPaint.textSize = 50F
|
|
|
|
canvas.drawText("${round((min+average*(max-min)) *10F)/10F}€", xoffset+averageX, yoffset+triangleHeight + tooltipHeight*0.6F, averageTextPaint)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} |