1. 程式人生 > >安卓自定義View實現簡單折線圖

安卓自定義View實現簡單折線圖

自定義View實現折線圖:

執行效果:
這裡寫圖片描述

少說廢話,實現起來還是比較簡單的,無非就是使用canvas進行繪圖,以及座標的計算,下面直接貼程式碼:

ChartView.java

/**
 * Created by wangke on 2017/2/20.
 * 自定義折線圖
 */

public class ChartView extends View{

    private int mViewHeight; //當前View的高
    private int mViewWidth; //當前View的寬

    private Paint mPaintCdt;// 繪製座標系的畫筆
    private
Paint mPaintSysPoint; //繪製座標系上刻度點 private Paint mPaintLinePoint; //繪製折線上的點 private Paint mPaintText; //繪製文字 private Paint mPaintLine; //繪製折線 private Paint mPaintDash; //繪製虛線 private Paint mPaintSys; //x,y軸 private Rect mXBound; private Rect mYBound; private ArrayList<Point> pointList = null
; private int X_MAX; //傳入點的X的最大座標 private int Y_MAX; //傳入點的Y的最大座標 private float mScreenXdistance; //x軸刻度在螢幕上的間隔 private float mScreenYdistance; //y軸刻度在螢幕上的間隔 //折線圖距離四周的畫素大小 private int Margin = 80; private int coordinateSystemColor; private float coordinateSystemSize; private
int lineColor; private float lineSize; private int lineColorPoint; private float lineColorPointRadius; private int scalePointColor; private float scalePointRadius; private boolean isShowDash; private int xScale; private int yScale; private float dashSize; private int dashColor; public ChartView(Context context) { super(context); InitPaint(); } //設定點的資料 public void setPoint(ArrayList<Point> points) { pointList = new ArrayList(); pointList = points; int []xPointArray = new int[100]; int []yPointArray = new int[100]; //遍歷傳入的點的座標,獲取最大的x,y點的座標,用來計算刻度 for(int i=0;i<pointList.size();i++){ Point point = pointList.get(i); xPointArray[i] = point.x; yPointArray[i] = point.y; } Arrays.sort(xPointArray); Arrays.sort(yPointArray); X_MAX = xPointArray[xPointArray.length-1]; Y_MAX = yPointArray[yPointArray.length-1]; Log.i("wk","X的最大座標:"+xPointArray[xPointArray.length-1]); Log.i("wk","y的最大座標:"+yPointArray[yPointArray.length-1]); //呼叫繪製 invalidate(); } //初始化畫筆 private void InitPaint() { mPaintCdt = new Paint(Paint.ANTI_ALIAS_FLAG); //設定畫線 mPaintCdt.setStyle(Paint.Style.STROKE); //設定線的寬度 mPaintCdt.setStrokeWidth(lineSize); mPaintCdt.setColor(lineColor); mPaintSysPoint = new Paint(Paint.ANTI_ALIAS_FLAG); //設定填充 mPaintSysPoint.setStyle(Paint.Style.FILL); mPaintSysPoint.setColor(scalePointColor); mPaintLinePoint = new Paint(Paint.ANTI_ALIAS_FLAG); //設定填充 mPaintLinePoint.setStyle(Paint.Style.FILL); Log.i("wk","線上點的顏色:"+lineColor); mPaintLinePoint.setColor(lineColorPoint); //繪製xy軸 mPaintSys = new Paint(Paint.ANTI_ALIAS_FLAG); //設定畫線 mPaintSys.setStyle(Paint.Style.STROKE); //設定線的寬度 mPaintSys.setStrokeWidth(coordinateSystemSize); mPaintSys.setColor(coordinateSystemColor); mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintText.setTextAlign(Paint.Align.CENTER); mPaintText.setColor(Color.WHITE); mPaintText.setTextSize(30); mPaintLine = new Paint(Paint.ANTI_ALIAS_FLAG); //設定畫線 mPaintLine.setStyle(Paint.Style.STROKE); //設定線的寬度 mPaintLine.setStrokeWidth(lineSize); //設定畫筆的顏色 mPaintLine.setColor(lineColor); mPaintDash = new Paint(); mPaintDash.setStyle(Paint.Style.STROKE); mPaintDash.setStrokeWidth(dashSize); mPaintDash.setColor(dashColor); mPaintDash.setPathEffect(new DashPathEffect(new float[]{10,10},0)); mXBound = new Rect(); mYBound = new Rect(); invalidate(); } public ChartView(Context context, AttributeSet attrs) { super(context, attrs); //獲取屬性值 getAttrs(context,attrs); InitPaint(); } //獲取設定的屬性 private void getAttrs(Context context,AttributeSet attrs) { TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ChartView); //座標系顏色 coordinateSystemColor = ta.getColor(R.styleable.ChartView_coordinateSystemColor, Color.RED); coordinateSystemSize = ta.getDimension(R.styleable.ChartView_coordinateSystemLineSize, 3f); //折線顏色 lineColor = ta.getColor(R.styleable.ChartView_lineColor, Color.BLACK); lineSize = ta.getDimension(R.styleable.ChartView_lineSize, 2f); //折線上座標點顏色 lineColorPoint = ta.getColor(R.styleable.ChartView_linePointColor, Color.RED); //折線上座標點的半徑 lineColorPointRadius = ta.getDimension(R.styleable.ChartView_linePointRadius,6f); //刻度點顏色 scalePointColor = ta.getColor(R.styleable.ChartView_scalePointColor, Color.RED); //刻度點半徑 scalePointRadius = ta.getDimension(R.styleable.ChartView_scalePointRadius, 6); //設定是否顯示虛線 isShowDash = ta.getBoolean(R.styleable.ChartView_showDash,false); dashSize = ta.getDimension(R.styleable.ChartView_setDashSize,2f); dashColor = ta.getColor(R.styleable.ChartView_setDashColor, Color.WHITE); xScale = ta.getInt(R.styleable.ChartView_setXScale,5); yScale = ta.getInt(R.styleable.ChartView_setYScale,5); ta.recycle(); Log.i("wk","coordinateSystemColor:"+ coordinateSystemColor +"\n coordinateSystemSize:"+ coordinateSystemSize +"\n"+"lineColor:"+ lineColor +"\n"+"lineSize:"+ lineSize); } public ChartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); getAttrs(context,attrs); InitPaint(); } //測量view @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //獲取當前View的寬高 mViewWidth = w; mViewHeight = h; Log.i("wk","寬度:"+w); Log.i("wk","高度:"+h); } //繪製 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //繪製X軸Y軸 以及原點 canvas.drawLine(Margin,mViewHeight-Margin, Margin,5, mPaintSys); canvas.drawLine(Margin,mViewHeight-Margin,mViewWidth-5,mViewHeight-Margin, mPaintSys); //繪製原點 canvas.drawCircle(Margin,mViewHeight-Margin,scalePointRadius,mPaintSysPoint); // /** * 計算兩個刻度之間的間距: * * 1.刻度的個數 = 傳入座標最大的座標點/座標軸間距 * 2.兩個刻度之間的間距 = 螢幕的寬或高 /刻度的個數 * */ int num_x = X_MAX/xScale; //x軸上需要繪製的刻度的個數 mScreenXdistance = (mViewWidth- Margin *2)/(num_x*1f); Log.i("wk","需要繪製的刻度個數==>"+num_x+"兩個刻度間間隔:"+ mScreenXdistance); int num_y = Y_MAX/yScale; mScreenYdistance = (mViewHeight-Margin*2)/(num_y*1f); Log.i("wk","需要繪製的刻度個數==>"+num_x+"兩個刻度間間隔:"+ mScreenXdistance); //繪製 X,y軸刻度標記 for(int i=0;i<pointList.size();i++){ canvas.drawCircle(Margin +(i* mScreenXdistance),mViewHeight-Margin,scalePointRadius,mPaintSysPoint); canvas.drawCircle(Margin,mViewHeight-Margin-(i* mScreenYdistance),scalePointRadius,mPaintSysPoint); //計算刻度字型的寬高 String index_x = String.valueOf(i*xScale); String index_y = String.valueOf(i*yScale); mPaintText.getTextBounds(index_x,0,index_x.length(),mXBound); mPaintText.getTextBounds(index_y,0,index_x.length(),mYBound); int indexWidth_x = mXBound.width(); int indexHeight_x = mXBound.height(); int indexWidth_y = mYBound.width(); int indexHeight_y = mYBound.height(); Log.i("wk","字型的寬度:"+indexWidth_x+"字型的高度:"+indexHeight_x); canvas.drawText(index_x, Margin +(i* mScreenXdistance),mViewHeight-Margin+indexHeight_x+indexHeight_x/2,mPaintText); if(i!=0) { canvas.drawText(index_y, Margin - indexHeight_y-indexWidth_y/2, mViewHeight - Margin - (i * mScreenYdistance), mPaintText); } } /** * 繪製折線 */ Point LastPoint = new Point(); //記錄上一個座標點 LastPoint.x = Margin; LastPoint.y = mViewHeight-Margin; for(int i=1;i<pointList.size();i++){ /** * 計算繪製點的座標位置 * 繪製點的座標 = (傳入點的的最大的xy座標/座標軸上的間隔) * 座標間隔對應的螢幕上的間隔 */ // canvas.drawCircle(LastPoint.x,LastPoint.y,4f,mPaintPoint); //計算出脫離座標系的點所處的位置 float point_x = (pointList.get(i).x/(xScale*1f))* mScreenXdistance; float point_y = (pointList.get(i).y/(yScale*1f))* mScreenYdistance; //座標系內的點的位置 float startX = LastPoint.x; float startY = LastPoint.y; float endX = Margin +point_x; float endY = mViewHeight-Margin-point_y; //需要計算此處 canvas.drawLine(startX,startY,endX,endY,mPaintLine); //記錄上一個座標點的位置 LastPoint.x = (int) endX; LastPoint.y = (int) endY; if(isShowDash) { //繪製橫向虛線 canvas.drawLine(Margin, mViewHeight - Margin - point_y -lineColorPointRadius/2, Margin + point_x - lineColorPointRadius/2, mViewHeight - Margin - point_y -lineColorPointRadius/2, mPaintDash); //繪製豎向虛線 canvas.drawLine(LastPoint.x, LastPoint.y, LastPoint.x, mViewHeight - Margin - lineColorPointRadius, mPaintDash); } canvas.drawCircle(LastPoint.x, LastPoint.y, lineColorPointRadius, mPaintLinePoint); } } //測量view高度 private int measureHeight(int heightMeasureSpec) { int result = 0; int specSize = MeasureSpec.getSize(heightMeasureSpec); //獲取高的高度 單位 為px int specMode = MeasureSpec.getMode(heightMeasureSpec);//獲取測量的模式 //如果是精確測量,就將獲取View的大小設定給將要返回的測量值 if(specMode == MeasureSpec.EXACTLY){ Log.i("wk","高度:精確測量 + specSize:==>"+specSize); result = specSize; }else{ Log.i("wk","高度:UNSPECIFIED + specSize:==>"+specSize); result = 400; //如果設定成wrap_content時,給高度指定一個值 if(specMode == MeasureSpec.AT_MOST){ Log.i("wk","高度:最大值模式 + specSize:==>"+specSize); result = Math.min(result,specSize); } } return result; } //測量view寬度 private int measureWidth(int widthMeasureSpec) { int result = 0; int specSize = MeasureSpec.getSize(widthMeasureSpec); //獲取高的高度 int specMode = MeasureSpec.getMode(widthMeasureSpec);//獲取測量的模式 //如果是精確測量,就將獲取View的大小設定給將要返回的測量值 if(specMode == MeasureSpec.EXACTLY){ Log.i("wk","寬度:精確測量 + specSize:==>"+specSize); result = specSize; }else{ Log.i("wk","寬度:UNSPECIFIED + specSize:==>"+specSize); result = 400; //如果設定成wrap_content時,給高度指定一個值 if(specMode == MeasureSpec.AT_MOST){ Log.i("wk","寬度:最大值模式 + specSize:==>"+specSize); result = Math.min(result,specSize); } } return result; } }

attrs.xml
自定義屬性:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="ChartView">
        <!--設定座標系的顏色-->
        <attr name="coordinateSystemColor" format="color" />
        <!--設定座標系線條的尺寸-->
        <attr name="coordinateSystemLineSize" format="dimension" />

        <!--設定刻度點的顏色-->
        <attr name="scalePointColor" format="color" />
        <!--設定刻度點的半徑-->
        <attr name="scalePointRadius" format="dimension" />

        <!--設定折線的顏色-->
        <attr name="lineColor" format="color" />
        <!--設定著折線的寬度-->
        <attr name="lineSize" format="dimension" />

        <!--設定折線點的顏色-->
        <attr name="linePointColor" format="color" />
        <!--設定折線點的半徑-->
        <attr name="linePointRadius" format="dimension" />

        <!--設定是否顯示虛線-->
        <attr name="showDash" format="boolean" />
        <attr name="setDashSize" format="dimension"/>
        <attr name="setDashColor" format="color"/>

        <!--設定座標軸上刻度的間隔-->
        <attr name="setXScale" format="integer" />
        <attr name="setYScale" format="integer" />


    </declare-styleable>


</resources>

activity_main.xml


    <com.merpyzf.studymultimedia.ChartView
        android:id="@+id/myChartView"
        android:layout_marginTop="20dp"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="600dp"
        app:linePointColor="@color/colorAccent"
        app:scalePointColor="#f76"
        app:linePointRadius="5dp"
        app:lineColor="#fff600"
        app:lineSize="2dp"
        app:coordinateSystemColor="#000000"
        app:coordinateSystemLineSize="2dp"
        app:scalePointRadius="6dp"
        app:setDashSize="1dp"
        app:showDash="true"
        />

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private MyService.MyBinder myBinder;
    private ChartView MyChartView;
    private ArrayList<Point> pointList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyChartView = (ChartView) findViewById(R.id.myChartView);
        Random random = new Random();
        pointList = new ArrayList<Point>();

        for(int i=0;i<=10;i++){
            Point p = new Point(i*5,random.nextInt(30));
            pointList.add(p);
        }

        //給ChartView設定座標
        MyChartView.setPoint(pointList);

    }
}

後面要花一段時間仔細的研究一下自定義View的知識,當然上面的這個折線圖只是腦子一熱敲出來的,不具備實用能力(⊙o⊙)…

量變引起質變

撒花 撒花 ^^O(∩∩)O哈哈~

相關推薦

定義View實現簡單折線

自定義View實現折線圖: 執行效果: 少說廢話,實現起來還是比較簡單的,無非就是使用canvas進行繪圖,以及座標的計算,下面直接貼程式碼: ChartView.java /** * Created by wangke on 2017/2/2

Android定義View實現簡單折線、柱狀

首先說第一個柱狀圖,實現很簡單。一個自定義View,重現裡面的OnDraw方法。然後利用paint,canvas繪製帶填充的長方形即可。每個長方形的X軸平方View的x軸即可,長方形的高度通過簡單的計算即可得到。下面上柱狀圖程式碼 package com.hrules.

淺談定義view(一):製作一個最最最簡單定義view

對於安卓程式設計師來說,自定義view簡直不要太重要,畢竟有很多功能,譬如圓形頭像這些,用單純的原生非常難以實現,而用自定義view,簡直分分鐘。 在這裡,我嘗試用最簡單方式跟初學者說一下如何自定義一個自己的view~ 首先,最簡單最簡單的自定義view,有

定義View進階-手勢檢測(GestureDecetor)

Android 手勢檢測,主要是 GestureDetector 相關內容的用法和注意事項,本文依舊屬於事件處理這一體系,部分內容會涉及到之前文章提及過的知識點,如果你沒看過之前的文章,可以到 自定義 View 系列 來檢視這些內容。 在開發 Android 手機應用過程中,可

定義View進階-多點觸控詳解

Android 多點觸控詳解,在前面的幾篇文章中我們大致瞭解了 Android 中的事件處理流程和一些簡單的處理方案,本次帶大家瞭解 Android 多點觸控相關的一些知識。 多點觸控 ( Multitouch,也稱 Multi-touch ),即同時接受螢幕上多個點的人機互動

定義View進階-特殊控制元件的事件處理方案

本文帶大家瞭解 Android 特殊形狀控制元件的事件處理方式,主要是利用了 Region 和 Matrix 的一些方法,超級實用的事件處理方案,相信看完本篇之後,任何奇葩控制元件的事件處理都會變得十分簡單。 不得不說,Android 對事件體系封裝的非常棒,即便對事件體系不太

定義View進階-MotionEvent詳解

Android MotionEvent 詳解,之前用了兩篇文章 事件分發機制原理 和 事件分發機制詳解 來講解事件分發,而作為事件分發主角之一的 MotionEvent 並沒有過多的說明,本文就帶大家瞭解 MotionEvent 的相關內容,簡要介紹觸控事件,主要包括 單點觸控、多點

定義View進階-事件分發機制詳解

Android 事件分發機制詳解,在上一篇文章 事件分發機制原理 中簡要分析了一下事件分發機制的原理,原理是十分簡單的,一句話就能總結:責任鏈模式,事件層層傳遞,直到被消費。 雖然原理簡單,但是隨著 Android 不斷的發展,實際運用場景也越來越複雜,所以想要徹底玩轉事件分發機制還

定義View進階-Matrix Camera

本篇依舊屬於Matrix,主要講解Camera,Android下有很多相機應用,其中的美顏相機更是不少,不過今天這個Camera可不是我們平時拍照的那個相機,而是graphic包下的Camera,專業給View拍照的相機,不過既然是相機,作用都是類似的,主要是將3D的內容拍扁變成2D

定義View進階-Matrix詳解

這應該是目前最詳細的一篇講解Matrix的中文文章了,在上一篇文章Matrix原理中,我們對Matrix做了一個簡單的瞭解,偏向理論,在本文中則會詳細的講解Matrix的具體用法,以及與Matrix相關的一些實用技巧。 ⚠️ 警告:測試本文章示例之前請關閉硬體加速。

定義View進階-Matrix原理

本文內容偏向理論,和 畫布操作 有重疊的部分,本文會讓你更加深入的瞭解其中的原理。 本篇的主角Matrix,是一個一直在後臺默默工作的勞動模範,雖然我們所有看到View背後都有著Matrix的功勞,但我們卻很少見到它,本篇我們就看看它是何方神聖吧。 由於Goog

定義View進階-PathMeasure

可以看到,在經過 Path之基本操作 Path之貝塞爾曲線 和 Path之完結篇 後, Path中各類方法基本上都講完了,表格中還沒有講解到到方法就是矩陣變換了,難道本篇終於要講矩陣了? 非也,矩陣這一部分仍在後面單獨講解,本篇主要講解 PathMeasure 這個類與 Path 的

定義View進階-Path之貝塞爾曲線

在上一篇文章Path之基本操作中我們瞭解了Path的基本使用方法,本次瞭解Path中非常非常非常重要的內容-貝塞爾曲線。 一.Path常用方法表 為了相容性(偷懶) 本表格中去除了在API21(即安卓版本5.0)以上

定義View進階-Path之基本操作

在上一篇Canvas之圖片文字中我們瞭解瞭如何使用Canvas中繪製圖片文字,結合前幾篇文章,Canvas的基本操作已經差不多完結了,然而Canvas不僅僅具有這些基本的操作,還可以更加炫酷,本次會了解到path(路徑)這個Canvas中的神器,有了這個神器,就能創造出更多炫(zhu

定義View進階-Canvas之圖片文字

在上一篇文章Canvas之畫布操作中我們瞭解了畫布的一些基本操作方法,本次瞭解一些繪製圖片文字相關的內容。如果你對前幾篇文章講述的內容熟練掌握的話,那麼恭喜你,本篇結束之後,大部分的自定義View已經難不倒你了,當然了,這並不是終點,接下來還會有更加炫酷的技能。 一.Canva

定義View進階-分類與流程

本章節為什麼要叫進階篇?(雖然講的是基礎內容),因為從本篇開始,將會逐漸揭開自定義View的神祕面紗,每一篇都將比上一篇內容更加深入,利用所學的知識能夠製作更加炫酷自定義View,就像在臺階上一樣,每一篇都更上一層,幫助大家一步步走向人生巔峰,出任CEO,迎娶白富美。 誤

定義View進階-Path之完結篇

經歷過前兩篇 Path之基本操作 和 Path之貝塞爾曲線 的講解,本篇終於進入Path的收尾篇,本篇結束後Path的大部分相關方法都已經講解完了,但Path還有一些更有意思的玩法,應該會在後續的文章中出現。 一.Path常用方法表 為了相容性(偷懶) 本表格中去除

定義View進階-縮放手勢檢測(ScaleGestureDecetor)

0. 前言 Android 縮放手勢檢測,ScaleGestureDetector 相關內容的用法和注意事項,本文依舊屬於事件處理這一體系,在大多數的情況下,縮放手勢都不是單獨存在的,需要配合其它的手勢來使用,所以推薦配合 手勢檢測(GestureDetector) 一

定義View進階-Canvas之畫布操作

Canvas之畫布操作 上一篇Canvas之繪製基本形狀中我們瞭解瞭如何使用Canvas繪製基本圖形,本次瞭解一些基本的畫布操作。 本來想把畫布操作放到後面部分的,但是發現很多圖形繪製都離不開畫布操作,於是先講解一下畫布的基本操作方法。

定義View基礎-繪製點、線、矩形、圓形等

為什麼要自定義View?因為我們在開發中,經常有各種各樣的需求,但是原生的控制元件畢竟只能滿足我們常用的需求,所以我們需要根據自身當前的需求來定製我們的View,話不多說,一步一步來吧。 1.建立類: 建立一個類,暫且將這個類命名為CustomV