自定义高仿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地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Uniapp是一款多终端开发框架,可以方便地开发出同时支持iOS和Android等多个平台的应用程序。而高德地是一款功能强大的地应用,其中包含了地展示、导航、搜索等多种功能,用户使用广泛。基于Uniapp框架,我们可以进行高仿高德地的开发。 首先,我们可以使用Uniapp框架的跨平台特性,将高德地的相关功能进行封装,并提供给多个平台的用户使用。通过使用Uniapp的H5页面技术,我们可以将地展示功能嵌入到Web页面中,让用户可以通过浏览器直接访问,并实现高仿的效果。 其次,Uniapp框架支持原生插件的集成,我们可以通过引入高德地的原生SDK,并与Uniapp进行深度集成,实现更加强大的地功能。比如,可以调用高德地的导航接口,让用户实现实时导航的功能;也可以利用高德地的搜索接口,实现地点搜索和周边设施查询的功能。 另外,Uniapp还提供了丰富的组件库和UI样式库,我们可以借助这些库来实现高仿高德地的界面设计。比如,可以使用Uniapp的地组件来展示地,并通过自定义样式实现与高德地相似的界面效果;也可以使用Uniapp的列表组件和搜索框组件,实现高仿的地点搜索功能。 总之,借助Uniapp的跨平台特性和原生插件集成能力,我们可以很好地实现高德地高仿效果。同时,Uniapp的丰富组件库和UI样式库也能帮助我们设计出与高德地相似的用户界面。通过这些功能的组合应用,我们可以开发出功能强大、用户体验优秀的高仿高德地应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值