1. 程式人生 > >Android開發自定義控制元件實現一個折線圖

Android開發自定義控制元件實現一個折線圖

實現一個如下圖所示的折線圖



首先是控制元件繪圖區域的劃分,控制元件左邊取一小部分(控制元件總寬度的八分之一)繪製表頭,右邊剩餘的部分繪製表格

確定表格的行列數,首先繪製一個三行八列的網格,設定好行列的座標後開始繪製

/*繪製三條橫線*/
for(int i=0;i<3;i++){
    canvas.drawLine(textWide, mLineYs[i], totalWidth, mLineYs[i], mPaintLine);
}
/*繪製八條豎線*/
for(int i=0;i<8;i++){
    canvas.drawLine(mLineXs[i], 0, mLineXs[i
], totalHeight, mPaintLine); }

網格繪製完成後,開始繪製折線圖

根據輸入的節點資料,分別繪製兩條折線

通過canvas的drawLine方法依次連線兩點即可

在每個資料節點處繪製一個小圓,突出顯示

/*繪製第一條折線的路徑*/
for (int i = 0; i < mPerformance_1.length - 1; i++) {
    /*折線圖的折線的畫筆設定粗一點*/
mPaintLine.setStrokeWidth(5);
    /*計算當前節點的座標值*/
float prePointX =mLineXs[i];
    float prePointY 
=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i].type]) * animCurrentValue; /*計算下一個節點的座標值*/ float nextPointX=mLineXs[i + 1]; float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i + 1].type]) * animCurrentValue; /*連線當前座標和下一個座標,繪製線段*/ canvas.drawLine(prePointX, prePointY, nextPointX
, nextPointY, mPaintLine1); /*當前節點座標處繪製小圓*/ canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint); }

兩條折線重合的地方,需要特殊考慮,比如希望兩條折線重合的地方折線變為白色

設定下兩條折線的畫筆即可

mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));


測試程式碼及效果;

final Random random=new Random();
final LineChartView myView=(LineChartView)findViewById(R.id.custom_view);
final LineChartView.Performance[] performances1=new LineChartView.Performance[8];
final LineChartView.Performance[] performances2=new LineChartView.Performance[8];
myView.setOnClickListener(new View.OnClickListener(){
    @Override
public void onClick(View v){
        for(int i=0;i<performances1.length;i++){
            switch (random.nextInt(2016)%3){
                case 0:
performances1[i]= LineChartView.Performance.WIN;
                    break;
                case 1:
performances1[i]= LineChartView.Performance.DRAW;
                    break;
                case 2:
performances1[i]= LineChartView.Performance.LOSE;
                    break;
                default:
performances1[i]= LineChartView.Performance.LOSE;
                    break;
            }
            switch (random.nextInt(2016)%3){
                case 0:
performances2[i]= LineChartView.Performance.WIN;
                    break;
                case 1:
performances2[i]= LineChartView.Performance.DRAW;
                    break;
                case 2:
performances2[i]= LineChartView.Performance.LOSE;
                    break;
                default:
performances1[i]= LineChartView.Performance.LOSE;
                    break;
            }
        }
        myView.setPerformances(performances1,performances2);
    }
});

完整程式碼如下:

public class LineChartView extends View {
    private Context context;
    /*動畫插值器*/
    DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
    /*動畫重新整理的次數*/
    private int mDuration = 10;
    /*當前動畫進度值*/
    private int mCurrentTime = 0;
    private Performance[] mPerformance_1, mPerformance_2;
    /*兩條折線的顏色*/
    private int mLineColor1, mLineColor2;
    /*繪製表頭文字畫筆*/
    private Paint mPaintText = new Paint();
    /*繪製表格的畫筆*/
    private Paint mPaintLine = new Paint();
    /*第一條折線的畫筆*/
    private Paint mPaintLine1 =new Paint();
    /*第二條折線的畫筆*/
    private Paint mPaintLine2 =new Paint();
    /*座標點的小圓點畫筆*/
    private Paint mPointPaint = new Paint();
    private float mSmallDotRadius = 4;
    private TypedValue typedValue;
    private int mPaintClolor;
    /*Handler重新整理介面產生動畫效果*/
    private Handler mHandler = new Handler();
    private Runnable mAnimation = new Runnable() {
        @Override
        public void run() {
            if (mCurrentTime < mDuration) {
                mCurrentTime++;
                LineChartView.this.invalidate();
            }
        }
    };

    public LineChartView(Context context) {
        super(context);
        this.context=context;
        init();
    }

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

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

    public enum Performance {
        WIN(0),
        DRAW(1),
        LOSE(2);
        public int type;
        Performance(int type) {
            this.type = type;
        }
    }

    public void setPerformances(Performance[] performance1, Performance[] performance2) {
        if (performance1 == null) {
            performance1 = new Performance[0];
        }
        if (performance2 == null) {
            performance2 = new Performance[0];
        }
        mPerformance_1 = Arrays.copyOf(performance1, performance1.length > 8 ? 8 : performance1.length);
        mPerformance_2 = Arrays.copyOf(performance2, performance2.length > 8 ? 8 : performance2.length);
        if (isShown()) {
            mCurrentTime = 0;
            this.invalidate();
        }
    }

    /**
     * 設定折線1的顏色
     *
     * @param mLineColor1
     */
    public void setLineColor1(int mLineColor1) {
        this.mLineColor1 = mLineColor1;
    }

    /**
     * 設定折線2的顏色
     *
     * @param mLineColor2
     */
    public void setLineColor2(int mLineColor2) {
        this.mLineColor2 = mLineColor2;
    }

    private void init() {
        mLineColor1=Color.BLUE;
        mLineColor2 = Color.GREEN;
        typedValue=new TypedValue();
        context.getTheme().resolveAttribute(R.attr.title_bar,typedValue,true);
        mPaintClolor =getResources().getColor(typedValue.resourceId);

        final LineChartView.Performance[] performances1=new LineChartView.Performance[8];
        final LineChartView.Performance[] performances2=new LineChartView.Performance[8];
        final Random random=new Random();
        for(int i=0;i<performances1.length;i++){
            switch (random.nextInt(2016)%3){
                case 0:
                    performances1[i]= LineChartView.Performance.WIN;
                    break;
                case 1:
                    performances1[i]= LineChartView.Performance.DRAW;
                    break;
                case 2:
                    performances1[i]= LineChartView.Performance.LOSE;
                    break;
                default:
                    performances1[i]= LineChartView.Performance.LOSE;
                    break;
            }
            switch (random.nextInt(2016)%3){
                case 0:
                    performances2[i]= LineChartView.Performance.WIN;
                    break;
                case 1:
                    performances2[i]= LineChartView.Performance.DRAW;
                    break;
                case 2:
                    performances2[i]= LineChartView.Performance.LOSE;
                    break;
                default:
                    performances1[i]= LineChartView.Performance.LOSE;
                    break;
            }
        }
        setPerformances(performances1,performances2);
    }


    /**
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /*獲取控制元件總寬高*/
        float totalWidth = getWidth();
        float totalHeight = getHeight();
        /*左邊取總寬度的八分之一繪製表格頭部*/
        float textWide = totalWidth / 8;
        /*左邊留一點空白*/
        float left_offset = 10;
        /*折線圖的總寬度等於控制元件的總寬度減去表頭和留白*/
        float chartWide = totalWidth - textWide - left_offset;
        /*一共三行,設定每一行的垂直座標*/
        float[] mLineYs = new float[]{totalHeight / 8, totalHeight / 2, totalHeight * 7 / 8};
        /*一共八列,設定每一列的水平座標*/
        float[] mLineXs = new float[]{
                textWide + left_offset + chartWide * 0 / 8,
                textWide + left_offset + chartWide * 1 / 8,
                textWide + left_offset + chartWide * 2 / 8,
                textWide + left_offset + chartWide * 3 / 8,
                textWide + left_offset + chartWide * 4 / 8,
                textWide + left_offset + chartWide * 5 / 8,
                textWide + left_offset + chartWide * 6 / 8,
                textWide + left_offset + chartWide * 7 / 8,
        };

        /*繪製表頭文字*/
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setColor(mPaintClolor);
        mPaintText.setAlpha(226);
        mPaintText.setTextSize(28);
        /*從中間開始繪製*/
        mPaintText.setTextAlign(Paint.Align.CENTER);
        /*測量文字大小,並計算偏移量*/
        Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();
        float textBaseLineOffset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        canvas.drawText("勝場", textWide / 2, mLineYs[0] + textBaseLineOffset, mPaintText);
        canvas.drawText("平局", textWide / 2, mLineYs[1] + textBaseLineOffset, mPaintText);
        canvas.drawText("負場", textWide / 2, mLineYs[2] + textBaseLineOffset, mPaintText);

        /*繪製表格畫筆設定*/
        mPaintLine.setStyle(Paint.Style.STROKE);
        mPaintLine.setAntiAlias(true);
        mPaintLine.setColor(mPaintClolor);
        mPaintLine.setAlpha(80);
        mPaintLine.setStrokeWidth(1);
        /*開始繪製表格*/
        /*繪製三條橫線*/
        for(int i=0;i<3;i++){
            canvas.drawLine(textWide, mLineYs[i], totalWidth, mLineYs[i], mPaintLine);
        }
        /*繪製八條豎線*/
        for(int i=0;i<8;i++){
            canvas.drawLine(mLineXs[i], 0, mLineXs[i], totalHeight, mPaintLine);
        }
        /*折線圖畫筆設定*/
        mPaintLine1.setStyle(Paint.Style.STROKE);
        /*設定透明度,取值範圍為0~255,數值越小越透明,0表示完全透明*/
        mPaintLine1.setAlpha(0);
        mPaintLine1.setAntiAlias(true);
        mPaintLine1.setColor(mLineColor1);
        mPaintLine1.setStrokeWidth(5);
        mPaintLine2.setStyle(Paint.Style.STROKE);
        /*設定透明度,取值範圍為0~255,數值越小越透明,0表示完全透明*/
        mPaintLine2.setAlpha(0);
        mPaintLine2.setAntiAlias(true);
        mPaintLine2.setColor(mLineColor2);
        mPaintLine2.setStrokeWidth(5);
        if (typedValue.resourceId==R.color.white){
            /*PorterDuff.Mode.SCREEN 上下層都顯示。*/
            mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
            mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
        }else {
            /*PorterDuff.Mode.DARKEN 上下層都顯示。變暗*/
            mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
            mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
        }
        /*畫節點處的小圓點的畫筆設定*/
        mPointPaint.setStyle(Paint.Style.STROKE);
        mPointPaint.setAntiAlias(true);
        mPointPaint.setColor(mPaintClolor);


        /*計算當前動畫進度對應的數值*/
        float animCurrentValue = mDecelerateInterpolator.getInterpolation(1.0f * mCurrentTime / mDuration);
        mPaintLine.setColor(mLineColor1);
        /*繪製第一條折線的路徑*/
        for (int i = 0; i < mPerformance_1.length - 1; i++) {
            /*折線圖的折線的畫筆設定粗一點*/
            mPaintLine.setStrokeWidth(5);
            /*計算當前節點的座標值*/
            float prePointX =mLineXs[i];
            float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i].type]) * animCurrentValue;
            /*計算下一個節點的座標值*/
            float nextPointX=mLineXs[i + 1];
            float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i + 1].type]) * animCurrentValue;
            /*連線當前座標和下一個座標,繪製線段*/
            canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine1);
            /*當前節點座標處繪製小圓*/
            canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint);
        }
        /*第一個折線圖的最後一個節點的座標*/
        float lastPointX=mLineXs[mPerformance_1.length - 1];
        float lastPointY= mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[mPerformance_1.length - 1].type]) * animCurrentValue;
        /*繪製最後一個節點的外圍小圓*/
        canvas.drawCircle(lastPointX,lastPointY ,mSmallDotRadius, mPointPaint);

        /*繪製第二條折線*/
        mPaintLine.setColor(mLineColor2);
        for (int i = 0; i < mPerformance_2.length - 1; i++) {
            /*折線圖的折線的畫筆設定粗一點*/
            mPaintLine.setStrokeWidth(5);
            /*計算當前節點的座標值*/
            float prePointX =mLineXs[i];
            float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[i].type]) * animCurrentValue;
            /*計算下一個節點的座標值*/
            float nextPointX=mLineXs[i + 1];
            float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[i + 1].type]) * animCurrentValue;
            /*連線當前座標和下一個座標,繪製線段*/
            canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine2);
            /*當前節點座標處繪製小圓*/
            canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint);
        }
         /*第一個折線圖的最後一個節點的座標*/
        lastPointX=mLineXs[mPerformance_2.length - 1];
        lastPointY= mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[mPerformance_2.length - 1].type]) * animCurrentValue;
        /*繪製最後一個節點的外圍小圓*/
        canvas.drawCircle(lastPointX,lastPointY ,mSmallDotRadius, mPointPaint);
        mHandler.postDelayed(mAnimation, 20);
    }
}

相關推薦

Android發自定義控制元件實現一個折線

實現一個如下圖所示的折線圖 首先是控制元件繪圖區域的劃分,控制元件左邊取一小部分(控制元件總寬度的八分之一)繪製表頭,右邊剩餘的部分繪製表格 確定表格的行列數,首先繪製一個三行八列的網格,設定好行列的座標後開始繪製 /*繪製三條橫線*/ for(int i=0;i&l

Android發自定義控制元件實現一個圓形進度條【帶數值和動畫】

實現一個如下圖所示的自定義控制元件,可以直觀地展示某個球隊在某個賽季的積分數和勝場、負場、平局數 首先對畫布進行區域劃分,整個控制元件分上下兩部分 上邊是個大的圓環,圓環中間兩行文字,沒什麼難度,選好圓心座標和半徑後直接繪製即可,繪製文字也是如此。 下部分是三個小的圓弧進

Android發自定義控制元件實現一個餅狀

實現一個如圖所示的控制元件,包括兩部分,左邊的餅狀圖和中間的兩個小方塊,及右邊的兩行文字 實現起來比較簡單,只是一些繪圖API的呼叫 核心程式碼在onDraw函式裡邊,,對靜態控制元件進行繪製即可 @Override protected void onDraw(Canv

Android】自定義控制元件實現可滑動的開關(switch)

介紹 昨天晚上寫了一個Android的滑動開關, 即SlideSwitch。效果如下: 實現 實現的思路其實很簡單,監聽控制元件上的touch事件,並不斷重新整理,讓滑塊在手指的位置上繪出,達到滑塊跟著手指滑動的顯示效果。 先看一下程式碼

Android運用自定義控制元件實現閃爍字

專案中要用到閃爍字來標識一些重要的提醒以及醒目的文字等,這裡Android並沒有為我們提供一些元件,這裡可以使用自定義的TextView,運用paint設定漸變渲染器LinearGradient來實現。 先看一下實現的效果,由於寫的倉促就沒有做動圖了。。。就是一個藍色的漸變

通過Xcode7程式碼實時預覽功能快速發自定義控制元件(一)

     通過程式碼實時預覽功能開發自定義控制元件能夠使開發者能夠直觀的修改控制元件的屬性,達到快速自定義控制元件的效果,我的Xcode版本是7.2正式版,下面開始講述如何通過程式碼實時預覽功能開發自

Android】自定義控制元件實現帶百分比顯示進度條,可自定義顏色

介紹 前天做了一個帶百分比顯示的條形進度條,效果如下: 實現 這個自定義進度條, 看起來簡單, 做起來。。。其實也很簡單: 主要通過繼承View類, 並重寫其onDraw方法實現。 思路分為3步: 1. 畫進圖條背景(圖中灰色部分 2. 根據

Android定義控制元件之《折線的繪製》

金融軟體裡的行情分時圖,這是我們最常見的折線圖,當然了,折線圖的用途並不僅僅侷限於此,像一般在一定區間內,為了更好的能顯示出幅度的變化,那麼用折線圖來展示無疑是最符合效果的,當然了,網上也有很多的第

定義控制元件 實現一個繞圓圈的箭頭

自定義的類 import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import

定義控制元件 實現一個繞圓圈的箭頭

自定義的類 import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphic

Android右滑關閉Activity介面功能-自定義控制元件實現

引言Android右滑關閉Activity介面功能,網上已經有好多優秀的開原始碼和專案,不過大部分都是結合ViewDragHelper來實現的,這裡要討論的是通過結合ValueAnimator來實現,目前我的控制元件只實現了從左往右(上往下)滑關閉介面。需求在介面上從左往右滑

[Android例項] 自定義控制元件一百行程式碼實現微信朋友圈九宮格圖片顯示

package com.weixinninegridlayout;import android.content.Context;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android

Android實戰簡易教程-第六十九槍(自定義控制元件實現雪花飄落效果)

現在APP要求越來越高了,不只是要求實現功能,顏值的要求也越來越高,下面我們通過自定義控制元件來實現雪花飄落的效果,可以作為介面背景哦。1.自定義控制元件:package com.test.a; import java.util.Random; import androi

Android定義控制元件實現圓形進度CircleProgressBar

近日有朋友問我有沒有如下圖效果的開源控制元件 , 相信大家無論是用IOS還是Android,都對這種效果不陌生,很多主流APP都會有這樣或類似的效果,之前也打算研究一下這類控制元件的程式碼,苦於一直不知道應該怎麼搜尋這種效果(就是關鍵詞)或者所搜的結

Android完全自定義控制元件並且實現監聽事件

本篇文章帶來Android的完全自定義控制元件。載體是自定義一個開關的控制元件,並且能夠響應事件,首先我們先創一個專案,名字就叫ToggleView,修改MainActivity public class MainActivity extends Acti

Android 通過自定義控制元件方式實現帶開關效果的左右切換選擇器。

通過自定義控制元件方式實現帶開關效果的左右切換選擇器。 1、先上效果圖 2、佈局檔案gender_select_button.xml 佈局檔案很簡單,左右各一個RelativeLayout,佈局裡再巢狀一個TextView,用來顯

定義控制元件之輪播實現

public class BannerView extends RelativeLayout { private boolean startAndclose; private List<String> images = new ArrayList

基於flume1.7發自定義Sink元件-一鍵打包

概要 開始 pom檔案   ide使用idea神器,工程組織使用maven,下面是工程的pom檔案: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.a

定義控制元件 實現 抽獎轉盤功能

先來看看佈局頁面 so easy 簡簡單單的佈局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.

定義控制元件--實現步驟

前言:       在android開發中我們常常會用到自定義控制元件,可是為什麼要自定義呢?自定義控制元件又該怎麼實現呢?下面我們來了解下自定義控制元件的常用知識。 1:為什麼要自定義控制元件   &nb