自定義View之kotlin繪制折線圖實(shí)例教程
什么是Kotlin
Kotlin,它是JetBrains開(kāi)發(fā)的基于JVM的面向?qū)ο蟮恼Z(yǔ)言。2017年的時(shí)候被Google推薦Android的官方語(yǔ)言,同時(shí)Android studio 3.0正式支持這門(mén)語(yǔ)言,在這個(gè)編譯器上創(chuàng)建一個(gè)Kotlin項(xiàng)目,非常方便,甚至可以Java轉(zhuǎn)為Kotlin。
引言
早上看到有個(gè)童鞋在群里面發(fā)牢騷,說(shuō)這個(gè)自定義view怎么畫(huà),不太會(huì),ok,正好我也沒(méi)事,那我就花兩個(gè)小時(shí)幫你搞定他吧,先看下他要的效果;
再來(lái)看下我實(shí)現(xiàn)的效果
實(shí)現(xiàn)過(guò)程
主要分為x軸和y軸,由效果圖可以看出,x軸主要分為7份,y軸主要分為4份,這樣劃分就比較簡(jiǎn)單的知道每條線的位置,每個(gè)位置的位置了,繪制起來(lái)就很簡(jiǎn)單;
先繪制y軸的四條線和文字
文字有三個(gè),放到一個(gè)list里面,然后我們均分高度,然后遍歷文字集合,根絕不同的高度繪制文字和橫線
看下代碼:
/** * 繪制邊框線和邊框文本 */ private fun drawBorderLineAndText(canvas: Canvas) { when { valueTextY.size > 0 -> { val averageHeight = mNeedDrawHeight / (valueTextY.size + 1) (1..valueTextY.size + 1).forEach { i -> val nowadayHeight = averageHeight * (valueTextY.size + 1 - i) canvas.drawLine(mBrokenLineLeft, nowadayHeight + mBrokenLineTop, mNeedDrawWidth, nowadayHeight + mBrokenLineTop, mBorderLinePaint) if (i < valueTextY.size + 1) { val fm = mTextPaint.fontMetrics val mTxtHeight = Math.ceil((fm.leading - fm.ascent).toDouble()).toInt() canvas.drawText(valueTextY[valueTextY.size - i].toString() + "萬(wàn)", mBrokenLineLeft, nowadayHeight + mBrokenLineTop - averageHeight / 2 + mTxtHeight / 2, mTextPaint) } } } } }
然后繪制x軸的文字
由于文字有6個(gè),我們分為七份,從第一份之后開(kāi)始繪制:
代碼如下:
private fun drawMonthText(canvas: Canvas) { when { valueOld.size > 0 -> { var month = defaultStartMonth for (i in 1..valueOld.size) { val averageWidth = (mNeedDrawWidth / (valueOld.size + 1)).toInt() val fm = mTextPaint.fontMetrics val mTxtHeight = Math.ceil((fm.leading - fm.ascent).toDouble()).toInt() canvas.drawText(month.toString() + "月", (averageWidth * i).toFloat(), mNeedDrawHeight - mTxtHeight / 2, mTextPaint) month++ } } } }
最后繪制折線和折現(xiàn)上面的小球
這里我們需要把數(shù)據(jù)放進(jìn)兩個(gè)集合傳入,然后根據(jù)數(shù)據(jù)算出每個(gè)點(diǎn)的坐標(biāo),最后根據(jù)path把每個(gè)點(diǎn)連接起來(lái)就ok了;
代碼如下:
/** * 設(shè)置點(diǎn)的值 */ fun setPointValues(valueNew: ArrayList<Int>, valueOld: ArrayList<Int>) { this.valueNew = valueNew this.valueOld = valueOld } /** * 根據(jù)值計(jì)算在該值的 x,y坐標(biāo) */ fun getPoints(list: ArrayList<Int>): ArrayList<Point> { val avaregwidth = mNeedDrawWidth / (list.size + 1) val points = ArrayList<Point>(list.size) list.indices.forEach { i -> val valueY = list[i].toFloat() val averaHeight = (mNeedDrawHeight * 3 / 4 / maxValue).toDouble() val drawHeight = mNeedDrawHeight * 3 / 4 - (valueY * averaHeight).toFloat() + mBrokenLineTop val pointY = drawHeight.toInt() val pointX = ((avaregwidth * (i + 1)).toInt() + mBrokenLineLeft).toInt() val point = Point(pointX, pointY) points.add(point) } return points } /** * 根據(jù)值繪制折線 */ private fun drawBrokenLine(canvas: Canvas) { when { valueOld.size > 0 && valueNew.size > 0 -> { val mPathOld = Path() val mPathNew = Path() val mPathOldshadow = Path() val mPathNewshadow = Path() val pointsOld = getPoints(valueOld) val pointsNew = getPoints(valueNew) for (i in 0 until mAnimatorValue) { val pointOld = pointsOld[i] val pointNew = pointsNew[i] if (i == 0) { mPathOld.moveTo(pointOld.x.toFloat(), pointOld.y.toFloat()) mPathNew.moveTo(pointNew.x.toFloat(), pointNew.y.toFloat()) mPathOldshadow.moveTo(pointOld.x.toFloat(), pointOld.y.toFloat()) mPathNewshadow.moveTo(pointNew.x.toFloat(), pointNew.y.toFloat()) } else { mPathOld.lineTo(pointOld.x.toFloat(), pointOld.y.toFloat()) mPathNew.lineTo(pointNew.x.toFloat(), pointNew.y.toFloat()) mPathOldshadow.lineTo(pointOld.x.toFloat(), pointOld.y.toFloat()) mPathNewshadow.lineTo(pointNew.x.toFloat(), pointNew.y.toFloat()) } } mBrokenLinePaint.color = Color.parseColor("#ff5400") canvas.drawPath(mPathOld, mBrokenLinePaint) mBrokenLinePaint.color = Color.parseColor("#ffff00") canvas.drawPath(mPathNew, mBrokenLinePaint) } } } /** * 繪制線上的圓 */ private fun drawLineCircle(canvas: Canvas) { when { valueOld.size > 0 && valueNew.size > 0 -> { val pointsOld = getPoints(valueOld) val pointsNew = getPoints(valueNew) for (i in 0 until mAnimatorValue) { val pointOld = pointsOld[i] val pointNew = pointsNew[i] mCirclePaint.color = Color.parseColor("#ff5400") canvas.drawCircle(pointOld.x.toFloat(), pointOld.y.toFloat(), radius, mCirclePaint) mCirclePaint.color = Color.parseColor("#ffff00") canvas.drawCircle(pointNew.x.toFloat(), pointNew.y.toFloat(), radius, mCirclePaint) } } } }
最后就是添加動(dòng)畫(huà)了,可有可無(wú),看需求吧
代碼如下:
private fun initAnimator( size:Int) { valueAnimator = ValueAnimator.ofInt(0, size).setDuration(defaultDuration.toLong()) mUpdateListener = ValueAnimator.AnimatorUpdateListener { animation -> mAnimatorValue = animation.animatedValue as Int invalidate() } valueAnimator.addUpdateListener(mUpdateListener) valueAnimator.start() } fun setPointValues(valueNew: ArrayList<Int>, valueOld: ArrayList<Int>) { this.valueNew = valueNew this.valueOld = valueOld initAnimator(valueNew.size) }
這樣大體效果就已經(jīng)實(shí)現(xiàn)了,最后加陰影,就更簡(jiǎn)單了,就直接給畫(huà)筆設(shè)置陰影就行了
喜歡請(qǐng)到github點(diǎn)個(gè)贊啦
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android開(kāi)發(fā)中常見(jiàn)問(wèn)題
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)中常見(jiàn)問(wèn)題,主要涉及了七個(gè)問(wèn)題,希望能幫助到大家,感興趣的小伙伴們可以參考一下2016-06-06Android游戲開(kāi)發(fā)學(xué)習(xí)①?gòu)椞∏驅(qū)崿F(xiàn)方法
這篇文章主要介紹了Android游戲開(kāi)發(fā)學(xué)習(xí)①?gòu)椞∏驅(qū)崿F(xiàn)方法,涉及Android通過(guò)物理引擎BallThread類模擬小球運(yùn)動(dòng)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android編程實(shí)現(xiàn)打勾顯示輸入密碼功能
這篇文章主要介紹了Android編程實(shí)現(xiàn)打勾顯示輸入密碼功能,涉及Android控件布局及屬性相關(guān)操作技巧,需要的朋友可以參考下2017-02-02RXjava網(wǎng)絡(luò)獲取圖片數(shù)據(jù)的方法
這篇文章主要為大家詳細(xì)介紹了RXjava網(wǎng)絡(luò)獲取圖片數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Android WebView 應(yīng)用界面開(kāi)發(fā)教程
WebView組件本身就是一個(gè)瀏覽器實(shí)現(xiàn),開(kāi)發(fā)者可以直接在WebView中使用聚合(Polymer)和Material設(shè)計(jì)。接下來(lái)通過(guò)本文給大家介紹Android WebView 應(yīng)用界面開(kāi)發(fā)教程,一起看下吧2016-08-08android 手機(jī)SD卡讀寫(xiě)操作(以txt文本為例)實(shí)現(xiàn)步驟
要完成SD卡讀寫(xiě)操作首先對(duì)manifest注冊(cè)SD卡讀寫(xiě)權(quán)限其次是創(chuàng)建一個(gè)對(duì)SD卡中文件讀寫(xiě)的類寫(xiě)一個(gè)用于檢測(cè)讀寫(xiě)功能的的布局然后就是UI的類了,感興趣的朋友可以參考下,希望可以幫助到你2013-02-02Android 判斷是否是是全漢字、全字母、全數(shù)字、數(shù)字和字母等(代碼)
這篇文章主要介紹了Android 判斷是否是是全漢字、全字母、全數(shù)字、數(shù)字和字母等的實(shí)例代碼,需要的朋友可以參考下2016-12-12Android 使用ViewPager實(shí)現(xiàn)輪播圖效果
這篇文章主要介紹了Android 使用ViewPager實(shí)現(xiàn)輪播圖效果,通過(guò)實(shí)例代碼給大家講解了適配器和各個(gè)方法的作用介紹,需要的朋友可以參考下2017-05-05