Android簡(jiǎn)易折線(xiàn)圖的實(shí)現(xiàn)思路

Android簡(jiǎn)易折線(xiàn)圖的實(shí)現(xiàn)方案

萬(wàn)事開(kāi)頭難违柏,不說(shuō)內(nèi)容如何,關(guān)鍵在于我終于邁出這一步了香椎!

對(duì)于折線(xiàn)圖來(lái)說(shuō)漱竖,github已經(jīng)有太多優(yōu)秀的開(kāi)源框架,如MPAndroidCharts畜伐,使用起來(lái)很方便馍惹,簡(jiǎn)單的調(diào)用其API方法即可。然在懂得運(yùn)用的情況下我們更要理解折線(xiàn)圖表實(shí)現(xiàn)的原理玛界。
最近寫(xiě)了一個(gè)簡(jiǎn)單的折線(xiàn)圖万矾,在此分享一下,接下來(lái)會(huì)逐漸完善與優(yōu)化慎框,主要就是模仿一些APP已經(jīng)實(shí)現(xiàn)的簡(jiǎn)易功能良狈。說(shuō)了那么多的廢話(huà),開(kāi)始行動(dòng)吧笨枯!
說(shuō)到折線(xiàn)圖薪丁,我相信大家都不陌生,在很多地方都遇到過(guò)如Excel.還有股票走勢(shì)等等馅精,所以說(shuō)大家也一定知道折現(xiàn)圖長(zhǎng)什么樣子严嗜。那么接下來(lái)我們會(huì)才想,它是怎么實(shí)現(xiàn)的呢洲敢?漫玄?
構(gòu)思:
折現(xiàn)圖的形式:
簡(jiǎn)而言之,一條X軸压彭,一條Y軸睦优,X軸與Y軸組成區(qū)域內(nèi)的一些點(diǎn),線(xiàn)哮塞,以及這些點(diǎn)、線(xiàn)或坐標(biāo)軸的文字描述凳谦。
猜想:應(yīng)該怎么實(shí)現(xiàn)呢忆畅?既然知道了折線(xiàn)圖的表現(xiàn)形式,由兩條坐標(biāo)軸,若干點(diǎn)與線(xiàn)組成家凯,那應(yīng)該就會(huì)想到缓醋,怎么畫(huà)坐標(biāo)軸?怎么畫(huà)點(diǎn)绊诲?怎么畫(huà)線(xiàn)送粱?怎么把它們給鏈接起來(lái)?

-13_19h48_51.png

實(shí)現(xiàn)步驟

  1. 畫(huà)坐標(biāo)軸
  2. 畫(huà)點(diǎn)
  3. 畫(huà)線(xiàn)
    Action:
    假如現(xiàn)在有一個(gè)2維數(shù)組掂之,
protected float[][] points = new float[][]{{1,10}, {2,47}, {3,11}, {4,38}, {5,9},{6,52}, {7,14}, {8,37}, {9,29}, {10,31}};

我們將通過(guò)拆線(xiàn)圖的形式把每個(gè)點(diǎn)繪制出來(lái)抗俄。
執(zhí)行自定義View的3個(gè)常用方法
OnMeassure OnLayout OnDraw ,在這主要主是OnDraw.

  • 第一步世舰,平移坐標(biāo)原點(diǎn):
//平移坐標(biāo)原點(diǎn)
canvas.translate(50,height-50);
  • 第二步动雹,畫(huà)X軸及X軸上顯示的文本描述:
    注意
    具體怎么畫(huà)有很多種方法,這里采用的是每次畫(huà)一小段跟压,一段連接著下一段胰蝠,最終形成一條完整的線(xiàn)段,
    也可以直接先畫(huà)一條直線(xiàn)(寬度有限震蒋,也可以叫線(xiàn)段茸塞,怎么叫無(wú)所謂了,實(shí)現(xiàn)才是最關(guān)鍵的)查剖。
    當(dāng)然在畫(huà)線(xiàn)的時(shí)候會(huì)畫(huà)一個(gè)點(diǎn)钾虐,及關(guān)于這個(gè)點(diǎn)的描述,具體也可偏移一些以方便顯示文本梗搅。
private void drawLineXAxis(Canvas canvas) {
    int startX = 0;
    int startY = 0;
    int spaceing = (width-100)/points.length;
    //每次畫(huà)一小段 也可以直接畫(huà)一條線(xiàn)禾唁,然后再線(xiàn)上繪制具體的各個(gè)點(diǎn),其原理一樣无切,都是等分每次變化一個(gè)spaceing
    for (int i = 0;(startX+spaceing*i)<width-50;i++){
     //繪制X軸上的線(xiàn)段了荡短,0-1 1-2 2-3.。哆键。這個(gè)地方應(yīng)該搞個(gè)中間變量來(lái)表示 要不重復(fù)畫(huà)了0-1 0-2 0-3 代碼問(wèn)題
        canvas.drawLine(startX,startY,startX+spaceing*i ,startY,linePaint);    
         //繪制X軸各坐標(biāo)點(diǎn) 小圓
        canvas.drawCircle(startX+spaceing*i, startY, 5, linePaint);
       //繪制X軸各坐標(biāo)點(diǎn)的描述掘托,在這偷懶了直接就用0 1 2 3...表示了,實(shí)際運(yùn)用可靈活變化 取真正有意義應(yīng)該顯示的內(nèi)容
        canvas.drawText(i+0+"",startX+spaceing*i,startY+30,textPaint);
    }
}
  • 第三步籍嘹,畫(huà)Y軸及Y軸上對(duì)應(yīng)顯示的文本描述:
    注意
    Y軸相對(duì)X軸來(lái)說(shuō)相對(duì)稍微復(fù)雜一點(diǎn)闪盔,因?yàn)閅軸上的坐標(biāo)點(diǎn)刻度不是像X軸一樣有什么顯示什么,而是顯示的一個(gè)區(qū)間辱士,
    這個(gè)區(qū)間可以直接用數(shù)據(jù)源中的最大值最小值表示泪掀,也可以通過(guò)動(dòng)態(tài)設(shè)定。在這里也偷懶了(人為的分成了6份颂碘,
    因?yàn)榭梢灾庇^(guān)的知道數(shù)據(jù)的大小异赫,真實(shí)應(yīng)用時(shí)應(yīng)該根據(jù)前面所說(shuō),用數(shù)據(jù)源中最大值最小值表示,還是其他設(shè)定)塔拳。
    這一點(diǎn)我們搞明白了鼠证,Y軸也就簡(jiǎn)單了。同理X軸靠抑,要多少個(gè)坐標(biāo)點(diǎn) 就分成多少份量九,這里也是height/數(shù)據(jù)長(zhǎng)度代表每一區(qū)間的高度
    ,繪制Y坐標(biāo)文本時(shí)應(yīng)該是動(dòng)態(tài)按比例計(jì)算颂碧,此處直接寫(xiě)6不合理荠列,
    舉個(gè)例子按照最大最小分6份:
    最小值+(最大值-最小值)/6i //i表示第幾段了 或者這么說(shuō) 起始值 + 段值段數(shù) 來(lái)表示Y坐標(biāo)軸點(diǎn)的描述
private void drawLineYAxis(Canvas canvas) {
    int startX = 0;
    int startY = 0;
    int spaceing = (height-100)/points.length;
    //每次畫(huà)一小段
    for (int i = 0;(startY+spaceing*i)<height-50;i++){

        canvas.drawLine(startX,startY,startX ,startY-spaceing*i,linePaint);
        canvas.drawCircle(startX, startY-spaceing*i, 5, linePaint);
     //畫(huà)Y軸關(guān)鍵點(diǎn)就在于Y軸上的坐標(biāo)點(diǎn)怎么計(jì)算與顯示
        canvas.drawText(6*i+"",startX-30,startY-spaceing*i,textPaint);
    }
}
  • 第四步,畫(huà)點(diǎn)與點(diǎn)與點(diǎn)連接成的線(xiàn)
    注意 最關(guān)鍵一最后一步了稚伍。
    根據(jù)數(shù)據(jù)源弯予,計(jì)算出每個(gè)點(diǎn)應(yīng)該在坐標(biāo)軸上的哪個(gè)地方,然后繪制點(diǎn)再連接成線(xiàn)个曙。
    在這里每個(gè)點(diǎn)的橫坐標(biāo)簡(jiǎn)單锈嫩,同理這也有2各思路,但實(shí)質(zhì)是一樣的垦搬。
    橫坐標(biāo)是多少 就乘以sapceingWith(X軸分成了多少份)呼寸,這樣就能計(jì)算出橫坐標(biāo)點(diǎn)的位置
    縱坐標(biāo)的話(huà)大體相同,因?yàn)榭v坐標(biāo)值表示與X不一樣猴贰,應(yīng)做一些改進(jìn)对雪。
spaceingHeightUnit = height/(最大值-最小值)或者h(yuǎn)eight/(終值-起始值)

,這樣可以得到每單位的值占有多少像素
然后spaceHeightUnit*Y值(數(shù)據(jù)源中每條數(shù)據(jù)的值)
另外就是按比例了 和每單位值多少一樣米绕。
代碼中直接寫(xiě)了60也是直接寫(xiě)了偷懶原因瑟捣。可做改進(jìn)

float pointX = 0;
    float pointY = 0;
    private void drawLinePoints(Canvas canvas) {
        float pointXTemp = 0;
        float pointYTemp = 0;

        for(int i =0;i<points.length;i++){

            float temp =  points[i][0]%points.length;

            if(temp==0){
                pointX = 0+(points[i][0])*((width-100)/points.length);
            }else{
                pointX = 0+(points[i][0]%points.length)*((width-100)/points.length);
            }

//            pointY = pointY - points[i][1]*((height-100)/points.length);
            pointY = 0-(points[i][1]/60)*((height-100));
//            canvas.drawPoint(pointX,pointY,pointPaint);
            //pointY = 0-points[i][1]*6;
            canvas.drawCircle(pointX, pointY, 5, pointPaint);
            canvas.drawText(i+1+"",pointX-10,pointY-10,pointPaint);
            canvas.drawText("("+((int)points[i][0]+","+(int)points[i][1])+")",pointX-20,pointY-20,textPaint);
            if( i!=0) {
                canvas.drawLine(pointXTemp, pointYTemp, pointX, pointY, linePaint);
            }
            pointXTemp = pointX;
            pointYTemp = pointY;

        }
    }

主要就是以上4步栅干,畫(huà)X軸迈套、Y軸、點(diǎn)及點(diǎn)點(diǎn)之間連接形成的線(xiàn)碱鳞。
這是最終形成的效果圖桑李。


-13_19h48_51.png

當(dāng)然這只是自己之前第一次寫(xiě)的,測(cè)試demo,但最終做成的是這樣窿给,
數(shù)據(jù)全部動(dòng)態(tài)了贵白,代碼也優(yōu)化成更靈活的了,估計(jì)后期還可能加上各種事件或者其他效果崩泡,慢慢改進(jìn)吧.

一直閱讀別人寫(xiě)的博客禁荒,閱讀起來(lái)感覺(jué)很簡(jiǎn)單,但是自己執(zhí)行時(shí)才發(fā)現(xiàn)角撞,代碼寫(xiě)起來(lái)容易呛伴,寫(xiě)這個(gè)難啊寥掐。
自己頭一次 寫(xiě)東西,搞了好久才勉強(qiáng)弄出來(lái)了磷蜀,雖說(shuō)仍有各種問(wèn)題,但我終于邁出了這一步百炬!

package com.example.demochart.chart;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.example.demochart.util.Tool;

public class SelfLineChartView extends View {

    private int width;// 寬
    private int height;// 高
    private Paint alphaLinePaint;// 半透明畫(huà)筆
    private Paint linePaint;// 線(xiàn)條畫(huà)筆
    private Paint textPaint;// 橫坐標(biāo)畫(huà)筆
    private Paint pointPaint;//點(diǎn)的坐標(biāo)
    private Paint textEmptyPaint;// 空提示
    private Context context;
    public SelfLineChartView(Context context) {
        super(context);
    }

    public SelfLineChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    private void init() {
        //畫(huà)筆 X軸
        linePaint = new Paint();
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setColor(context.getResources().getColor(
                android.R.color.holo_red_dark));
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth((float) 2.0);

        //文字畫(huà)筆
        textPaint = new Paint();
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setColor(context.getResources().getColor(
                android.R.color.holo_red_dark));
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(Tool.dip2px(context, 10));

        //文字畫(huà)筆
        pointPaint = new Paint();
        pointPaint.setTextAlign(Paint.Align.CENTER);
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setColor(context.getResources().getColor(
                android.R.color.holo_blue_light));
        pointPaint.setAntiAlias(true);
        pointPaint.setTextSize(Tool.dip2px(context, 20));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        int heightSpec = MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY);
        setMeasuredDimension(widthSpec, heightSpec);
    }
    protected float[][] points = new float[][]{{1,10}, {2,47}, {3,11}, {4,38}, {5,9},{6,52}, {7,14}, {8,37}, {9,29}, {10,31}};

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //平移坐標(biāo)原點(diǎn)
        canvas.translate(50,height-50);

        //有多少條數(shù)據(jù)褐隆,把X軸分成多少份,
        drawLineXAxis(canvas);

        drawLineYAxis(canvas);

        drawLinePoints(canvas);
    }
    float pointX = 0;
    float pointY = 0;
    private void drawLinePoints(Canvas canvas) {
        float pointXTemp = 0;
        float pointYTemp = 0;

        for(int i =0;i<points.length;i++){

            float temp =  points[i][0]%points.length;

            if(temp==0){
                pointX = 0+(points[i][0])*((width-100)/points.length);
            }else{
                pointX = 0+(points[i][0]%points.length)*((width-100)/points.length);
            }

//            pointY = pointY - points[i][1]*((height-100)/points.length);
            pointY = 0-(points[i][1]/60)*((height-100));
//            canvas.drawPoint(pointX,pointY,pointPaint);
            //pointY = 0-points[i][1]*6;
            canvas.drawCircle(pointX, pointY, 5, pointPaint);
            canvas.drawText(i+1+"",pointX-10,pointY-10,pointPaint);
            canvas.drawText("("+((int)points[i][0]+","+(int)points[i][1])+")",pointX-20,pointY-20,textPaint);
            if( i!=0) {
                canvas.drawLine(pointXTemp, pointYTemp, pointX, pointY, linePaint);
            }
            pointXTemp = pointX;
            pointYTemp = pointY;

        }
    }

    private void drawLineYAxis(Canvas canvas) {
        int startX = 0;
        int startY = 0;
        int spaceing = (height-100)/points.length;
        //每次畫(huà)一小段
        for (int i = 0;(startY+spaceing*i)<height-50;i++){

            canvas.drawLine(startX,startY,startX ,startY-spaceing*i,linePaint);
            canvas.drawCircle(startX, startY-spaceing*i, 5, linePaint);

            canvas.drawText(6*i+"",startX-30,startY-spaceing*i,textPaint);
        }
    }

    private void drawLineXAxis(Canvas canvas) {
        int startX = 0;
        int startY = 0;
        int spaceing = (width-100)/points.length;
        //每次畫(huà)一小段
        for (int i = 0;(startX+spaceing*i)<width-50;i++){

            canvas.drawLine(startX,startY,startX+spaceing*i ,startY,linePaint);
            canvas.drawCircle(startX+spaceing*i, startY, 5, linePaint);

            canvas.drawText(i+0+"",startX+spaceing*i,startY+30,textPaint);
        }
    }
}
<com.example.demochart.chart.SelfLineChartView
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:id="@+id/selfLineChartView"/>

接下來(lái) 不知道怎么寫(xiě) 了 中斷了剖踊,也算第一篇文章不管好壞完成了吧庶弃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市德澈,隨后出現(xiàn)的幾起案子歇攻,更是在濱河造成了極大的恐慌,老刑警劉巖梆造,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缴守,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡镇辉,警方通過(guò)查閱死者的電腦和手機(jī)屡穗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)忽肛,“玉大人村砂,你說(shuō)我怎么就攤上這事∫俟洌” “怎么了础废?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)罕模。 經(jīng)常有香客問(wèn)我评腺,道長(zhǎng),這世上最難降的妖魔是什么手销? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任歇僧,我火速辦了婚禮,結(jié)果婚禮上锋拖,老公的妹妹穿的比我還像新娘诈悍。我一直安慰自己,他們只是感情好兽埃,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布侥钳。 她就那樣靜靜地躺著,像睡著了一般柄错。 火紅的嫁衣襯著肌膚如雪舷夺。 梳的紋絲不亂的頭發(fā)上苦酱,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音给猾,去河邊找鬼疫萤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛敢伸,可吹牛的內(nèi)容都是我干的扯饶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼池颈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尾序!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起躯砰,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤每币,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后琢歇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兰怠,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年李茫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了痕慢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涌矢,死狀恐怖掖举,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情娜庇,我是刑警寧澤塔次,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站名秀,受9級(jí)特大地震影響励负,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匕得,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一继榆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汁掠,春花似錦略吨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至乞榨,卻和暖如春秽之,著一層夾襖步出監(jiān)牢的瞬間当娱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工考榨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跨细,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓河质,卻偏偏與公主長(zhǎng)得像扼鞋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子愤诱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容