自定义高仿LOL经济曲线对比图

文章目录

背景

最近LoL s10全球总决赛正在如火如荼的进行,作为一名老玩家,笔者在观看比赛的同时,注意到每场比赛的经济对比图实时的展示,并且红蓝双方的经济曲线对比呈现不同的颜色,于是笔者来了兴趣,能否在安卓上自定义控件实现相似的功能呢?说干就干,经过笔者不懈的努力,终于实现了相似的功能。

实现效果

Alt

需求分析

LoL实时仅仅对比图需实现以下功能:按照阵营分为红蓝双方,蓝方在基准线以上,红方在基准线一下,曲线显示双方的经济差,当蓝方经济领先,曲线在基线上方,且曲线为蓝色,当红方经济领先,在基线一下,变为红色。当用户选择某个时间段,能显示出当前时间段的经济坐标。

实现思路

首先,我们需要将数据封装成对应的x,y 坐标的集合,,然后在绘制x轴,y轴,接着将点与点之间用贝塞尔曲线联合。如何实现在两点之间绘制一条三阶贝塞尔曲线,并且实现基线以上是蓝色,基线一下是红色,成为难点,这里我们的思路是分段绘制的思路,即计算计算出两点连线与基线的焦点,然后分段绘制,这里在分段绘制时,采用三阶贝塞尔曲线。如何控制锚点,使其连线看起来更加平滑成为难题。
如果读者对三阶贝塞尔曲线不甚了解,可以去查看相关文档。
经过笔者多番调试,终于调出较为满意的效果。

代码实现

绘制x轴

 // 画x轴
    private fun drawXAxis(canvas: Canvas) {
        val rate = returnRate()
        val y = layoutRect.bottom - textHeight
        mTimeTextPaint.textAlign = Paint.Align.CENTER
        mTimeTextPaint.strokeWidth = 2f
        mTimeTextPaint.style = Paint.Style.FILL

        val showNum = currentDescArray.size
        for (i in 0 until showNum) {
            val x =
                (rate * i) + lineStartX.toFloat() + tempPaddingStartX.toFloat()
            canvas.drawText(currentDescArray[i], x, y, mTimeTextPaint)
        }
    }

绘制y轴

 // 画Y轴及线
    private fun drawYAxis(canvas: Canvas) {
        if (txtYArray.isEmpty()) return
        mPaint.style = Paint.Style.FILL
        mPaint.strokeWidth = 5f
        mPaint.color = Color.BLACK
        val keyValueY = (distanceH / (lineNumY + 1)).toFloat()
        var yValue: Float      //记录y的值
        var showStr: String?
        for (i in 0 until lineNumY) {
            yValue = keyValueY * (i + 2) - subtractPadding
            if (txtYArray[i] > 0) {
                showStr = (txtYArray[i] / 1000).toString() + "k"
                mPaint.color = resources.getColor(R.color.color_team_blue)
                canvas.drawText(
                    showStr,
                    (lineStartX - showPadding).toFloat(),
                    yValue + 0.25f * textHeight,
                    mPaint
                )
            } else if (txtYArray[i] == 0F) {
                showStr = txtYArray[i].toString()
                mPaint.color = resources.getColor(R.color.color_999)
                canvas.drawText(
                    showStr,
                    (lineStartX - showPadding).toFloat(),
                    yValue + 0.25f * textHeight,
                    mPaint
                )
            } else {
                showStr = (Math.abs(txtYArray[i] / 1000)).toString() + "k"
                mPaint.color = resources.getColor(R.color.color_team_red)
                canvas.drawText(
                    showStr,
                    (lineStartX - showPadding).toFloat(),
                    yValue + 0.25f * textHeight,
                    mPaint
                )
            }
            canvas.drawLine(
                (lineStartX - showPadding).toFloat() + yleftWidth / 2 + 20,
                yValue,
                (distanceW - lineStartX + showPadding).toFloat(),
                yValue,
                mLinePaint
            )
            if (i == 0) {
                topLineY = yValue
            }
            if (i == lineNumY - 1) {
                btmLinY = yValue
            }
        }

    }

绘制贝塞尔曲线


    private fun drawBeizer(canvas: Canvas) {
        if (pointList.isNotEmpty()) {
            mPaint.style = Paint.Style.STROKE
            mPaint.strokeWidth = 8F
            mPaint.color = resources.getColor(R.color.color_team_red)
            var startp: Point
            var endp: Point
            for (i in 0 until pointList.size - 1) {
                startp = pointList[i]
                endp = pointList[i + 1]
                //两个点同时在基线上
                if (startp.py < baseZeroy && endp.py < baseZeroy) {
                    mPaint.color = resources.getColor(R.color.color_team_blue)
                    val wt = (startp.px + endp.px) / 2
                    path.reset()
                    path.moveTo(startp.px, startp.py)
                    path.cubicTo(wt, startp.py, wt, endp.py, endp.px, endp.py)
                    canvas.drawPath(path, mPaint)
                }
                //两个点同时在基线下
                else if (startp.py > baseZeroy && endp.py > baseZeroy) {
                    mPaint.color = resources.getColor(R.color.color_team_red)
                    val wt = (startp.px + endp.px) / 2
                    path.reset()
                    path.moveTo(startp.px, startp.py)
                    path.cubicTo(wt, startp.py, wt, endp.py, endp.px, endp.py)
                    canvas.drawPath(path, mPaint)
                }
                //一个点在基线上,一个点在基线下,此时分成两段去绘制
                else {
                    //当前基线的与该线段较短的x坐标
                    var baseZeroX = getZeroX(startp.px, endp.px, startp.py, endp.py)
                    //起点在基线一下
                    if (startp.py > baseZeroy) {
                        mPaint.color = resources.getColor(R.color.color_team_red)
                        val wtleft = (startp.px + baseZeroX) / 2
                        val htLeft= (startp.py + baseZeroy) / 2
                        var leftRateX=(baseZeroX-startp.px)/4
                        var leftRateY=(baseZeroy-startp.py)/4
                        path.reset()
                        path.moveTo(startp.px, startp.py)
                        path.cubicTo(wtleft, startp.py, wtleft+leftRateX, htLeft-leftRateY, baseZeroX, baseZeroy)
                        canvas.drawPath(path, mPaint)

                        mPaint.color = resources.getColor(R.color.color_team_blue)
                        val wtRight = (baseZeroX + endp.px) / 2
                        val htRight= (baseZeroy + endp.py) / 2
                        var rightRateX=(endp.px-baseZeroX)/4
                        var rightRateY=(endp.py-baseZeroy)/4
                        path.reset()
                        path.moveTo(baseZeroX, baseZeroy)   //为了保持裁点时曲线的平滑
                        path.cubicTo(wtRight-rightRateX, htRight+rightRateY, wtRight, endp.py, endp.px, endp.py)
                        canvas.drawPath(path, mPaint)
                    } else {
                        mPaint.color = resources.getColor(R.color.color_team_blue)
                        val wtleft = (startp.px + baseZeroX) / 2
                        val htLeft= (startp.py + baseZeroy) / 2
                        var leftRateX=(baseZeroX-startp.px)/4
                        var leftRateY=(baseZeroy-startp.py)/4
                        path.reset()
                        path.moveTo(startp.px, startp.py)
                        path.cubicTo(wtleft, startp.py, wtleft+leftRateX, htLeft-leftRateY, baseZeroX, baseZeroy)
                        canvas.drawPath(path, mPaint)


                        mPaint.color = resources.getColor(R.color.color_team_red)
                        val wtRight = (baseZeroX + endp.px) / 2
                        val htRight= (baseZeroy + endp.py) / 2
                        var rightRateX=(endp.px-baseZeroX)/4
                        var rightRateY=(endp.py-baseZeroy)/4
                        path.reset()
                        path.moveTo(baseZeroX, baseZeroy)
                        path.cubicTo(wtRight-rightRateX, htRight+rightRateY, wtRight, endp.py, endp.px, endp.py)
                        canvas.drawPath(path, mPaint)
                    }
                }
            }
        }
    }

绘制触摸效果


    private fun drawTouch(canvas: Canvas) {
        if (pointList.size > 0 && linex >= 0 && linex < pointList.size) {
            var currentPoint = pointList[linex]
            //基线一下
            if (currentPoint.py >= baseZeroy) {
                mValueLinePaint?.color = resources.getColor(R.color.color_team_red)
                canvas.drawCircle(
                    pointList[linex].px,
                    pointList[linex].py,
                    14F,
                    mValueLinePaint!!
                )// 小圆
                mValueLinePaint?.isAntiAlias = true
                mPointLine.color = resources.getColor(R.color.color_team_red)
                canvas.drawLine(
                    pointList[linex].px,
                    topLineY,
                    pointList[linex].px,
                    btmLinY,
                    mPointLine
                )

            } else {
                mValueLinePaint?.color = resources.getColor(R.color.color_team_blue)
                canvas.drawCircle(
                    pointList[linex].px,
                    pointList[linex].py,
                    14F,
                    mValueLinePaint!!
                )// 小圆
                mValueLinePaint?.isAntiAlias = true
                mPointLine.color = resources.getColor(R.color.color_team_blue)
                canvas.drawLine(
                    pointList[linex].px,
                    topLineY,
                    pointList[linex].px,
                    btmLinY,
                    mPointLine
                )
            }


            if (listener == null) return
            dataLists[linex].avg.toString()
            listener?.onPointSelect(
                buleTeam,
                redTeam,
                currentPoint.px,
                currentPoint.py,
                dataLists[linex].timeStamp,
                dataLists[linex].avg.toString(),
                distanceW
            )
        }
    }


小结

附上GitHub的地址,大家如果需要可以进去clone下来,仔细研读,如果觉得还行,能对你有所帮助,别忘了给我个star哦

github地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值