lebonprix.apk/java/io/xdrm/lebonprix/anim/ResultRangeAnimation.kt

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)
}
}