安卓自定義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