Android開發(fā)自定義控件之折線圖實現(xiàn)方法詳解
本文實例講述了Android開發(fā)自定義控件之折線圖實現(xiàn)方法。分享給大家供大家參考,具體如下:
前言
折線圖是Android開發(fā)中經(jīng)常會碰到的效果,但由于涉及自定義View的知識,對許多剛?cè)腴T的小白來說會覺得很高深。其實不然,接下來我就以盡量通俗的語言來說明下圖折線圖效果的實現(xiàn)過程。
效果圖

實現(xiàn)過程
首先,選擇自定義控件的方式。
自定義控件的實現(xiàn)有四種方式:
1.繼承View,重寫onDraw、onMeasure等方法。
2.繼承已有的View(比如TextView)。
3.繼承ViewGroup實現(xiàn)自定義布局。
4.繼承已有的ViewGroup(比如LinearLayout)。
由于我們不需要多個控件進行組合,也不需要在原有控件基礎(chǔ)上改造,故我們采用第1種方式即繼承View來實現(xiàn)。代碼如下,新建一個ChartView類繼承自View,并實現(xiàn)他的幾個構(gòu)造方法,并重寫onDraw和onMeasure方法,因為我們要在onDraw方法里面進行繪制工作,并且我希望這個控件的長寬是相等的,所以在onMeasure方法設(shè)置寬高相等。設(shè)置長寬相等的方式很簡單,我們不需要自己去測量實現(xiàn),只需要調(diào)用父類的onMeasure方法,傳參數(shù)(寬高值)時將都傳入寬度(或者高度)即可。
public class ChartView extends View {
public ChartView(Context context) {
super(context);
}
public ChartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
其次,繪制簡單圖形并顯示出來。
在進行繪制之前,我們要進行若干初始化工作,其中就包括畫筆的初始化。然后就可以進行繪制了,我們先繪制一個簡單的圓圈,然后將控件放到布局文件中,運行看看效果。
ChartView代碼
public class ChartView extends View {
// 畫筆
private Paint paint;
/**
* 構(gòu)造函數(shù)
*/
public ChartView(Context context) {
super(context);
initWork();
}
/**
* 構(gòu)造函數(shù)
*/
public ChartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initWork();
}
/**
* 構(gòu)造函數(shù)
*/
public ChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initWork();
}
/**
* 初始化工作
*/
private void initWork() {
initPaint();
}
/**
* 畫筆設(shè)置
*/
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 畫筆樣式為填充
paint.setStyle(Paint.Style.FILL);
// 顏色設(shè)為紅色
paint.setColor(Color.RED);
// 寬度為3像素
paint.setStrokeWidth(3);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 畫圓
canvas.drawCircle(300,300,100,paint);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<com.toprs.linechart.ChartView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
效果:

然后,繪制圖表。
到目前為止,已經(jīng)實現(xiàn)了最簡單的一個自定義控件,雖然它什么功能都沒有,只是簡單顯示一個紅色圓圈,但本質(zhì)都是一樣的。接下來就開始圖表的繪制。
1.初始化一些需要使用的值。
// 刻度之間的距離 private int degreeSpace;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 控件上下左右邊界四至及控件的寬度(同時也是高度!)
int left = getLeft();
int right = getRight();
int top = getTop();
int bottom = getBottom();
int w = getWidth();
// 圖表距離控件邊緣的距離
int graphPadding = w / 10;
// 圖表上下左右四至
int graphLeft = left + graphPadding;
int graphBottom = bottom - graphPadding;
int graphRight = right - graphPadding;
int graphTop = top + graphPadding;
// 圖表寬度(也等同高度奧~)
int graphW = graphRight - graphLeft;
// 刻度之間的距離
degreeSpace = graphW / 8;
}
2.灰色背景
// 背景 canvas.drawColor(Color.LTGRAY);
3.坐標系
// 畫筆設(shè)置樣式為STROKE樣式,即只劃線不填充 paint.setStyle(Paint.Style.STROKE); // 坐標系繪制 Path pivotPath = new Path(); //Y軸 pivotPath.moveTo(graphLeft, graphBottom); pivotPath.lineTo(graphLeft, graphTop); //Y軸箭頭 pivotPath.lineTo(graphLeft - 12, graphTop + 20); pivotPath.moveTo(graphLeft, graphTop); pivotPath.lineTo(graphLeft + 12, graphTop + 20); //X軸 pivotPath.moveTo(graphLeft, graphBottom); pivotPath.lineTo(graphRight, graphBottom); //X軸箭頭 pivotPath.lineTo(graphRight - 20, graphBottom + 12); pivotPath.moveTo(graphRight, graphBottom); pivotPath.lineTo(graphRight - 20, graphBottom - 12); canvas.drawPath(pivotPath, paint);
4.刻度虛線及數(shù)字
// Y軸刻度虛線
for (int i = 1; i < 8; i++) {
Path yKeduPath = new Path();
// 線
paint.setColor(Color.WHITE);
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(new DashPathEffect(new float[]{5,5},0));
yKeduPath.moveTo(graphLeft, graphBottom - i * degreeSpace);
yKeduPath.lineTo(graphRight, graphBottom - i * degreeSpace);
canvas.drawPath(yKeduPath, paint);
// 數(shù)字
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(25);
paint.setPathEffect(null);
canvas.drawText(i + "", graphPadding / 2, graphBottom - i * degreeSpace, paint);
}
// X軸刻度虛線
for (int i = 1; i < 8; i++) {
Path xKeduPath = new Path();
// 線
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setPathEffect(new DashPathEffect(new float[]{5,5},0));
xKeduPath.moveTo(graphLeft + i * degreeSpace, graphBottom);
xKeduPath.lineTo(graphLeft + i * degreeSpace, graphTop);
canvas.drawPath(xKeduPath, paint);
// 數(shù)字
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(25);
paint.setPathEffect(null);
canvas.drawText(i + "", graphLeft + i * degreeSpace, graphBottom + graphPadding / 2, paint);
}
5.折線
在繪制折線之前,我們先要初始化幾個參數(shù)。
// 模擬數(shù)據(jù)
private float[] data = {3.2f, 4.3f, 2.5f, 3.2f, 3.8f, 7.1f, 1.3f, 5.6f};
// 當前顯示的數(shù)據(jù)數(shù)量
private int showNum=1;
// 折線
Path linePath = new Path();
for (int i = 0; i < showNum; i++) {
int toPointX = graphLeft + i * degreeSpace;
int toPointY = graphBottom - ((int) (data[i] * degreeSpace));
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.STROKE);
if (i==0){
linePath.moveTo(toPointX,toPointY);
}else {
linePath.lineTo(toPointX, toPointY);
}
// 節(jié)點圓圈
canvas.drawCircle(toPointX, toPointY,10,paint);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(toPointX,toPointY,7,paint);
}
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
canvas.drawPath(linePath, paint);
6.讓圖表動起來
為了實現(xiàn)數(shù)據(jù)依次顯現(xiàn)的動畫,我們開啟一個線程是當前顯示的數(shù)據(jù)數(shù)量即showNum變量不斷加一,并間隔時間0.5秒。然后postInvalidate()重繪即可。
private void initWork() {
initPaint();
// 開啟線程,沒隔0.5秒showNum加一
new Thread(new Runnable() {
@Override
public void run() {
while (true){
if (showNum<data.length){
showNum++;
}else {
showNum=1;
}
// 重繪
postInvalidate();
// 休眠0.5秒
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
好了,運行一下,便會實現(xiàn)上面的效果了。如果你覺得效果不夠炫酷或者功能太少,那就自己完善吧~~
結(jié)語
由于自定義控件是Android進階路上必然要碰到的知識,所以希望大家重視。其實自定義控件說難也難說簡單也簡單。實現(xiàn)一些普通的效果還是很方便的,像這次舉的例子,但如果要實現(xiàn)各種炫酷效果并且要完善各種功能的話,就需要各種知識的配合了,包括數(shù)學(xué)、物理、繪圖等知識。所以還是需要平時不斷積累的,看到別人的控件很棒的時候自己可以試著去實現(xiàn)一下,對自己的知識庫不斷進行補充,自然會嫻熟的運用。本人也是菜鳥一枚,望共勉!!
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android控件用法總結(jié)》、《Android開發(fā)入門與進階教程》、《Android視圖View技巧總結(jié)》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android數(shù)據(jù)庫操作技巧總結(jié)》及《Android資源操作技巧匯總》
希望本文所述對大家Android程序設(shè)計有所幫助。
相關(guān)文章
Android程序開發(fā)通過HttpURLConnection上傳文件到服務(wù)器
這篇文章主要介紹了Android程序開發(fā)通過HttpURLConnection上傳文件到服務(wù)器的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android中Fragment與Activity的生命周期對比
這篇文章主要介紹了Android中Fragment與Activity的生命周期對比,Fragment是在Activity的基礎(chǔ)之上進行設(shè)計的,比Activity多出幾個控制生命周期的回調(diào)函數(shù),需要的朋友可以參考下2016-02-02
Android ViewPager無限循環(huán)滑動并可自動滾動完整實例
對于Android ViewPager廣告頁可無限循環(huán)滑動并可自動滾動帶有小圓點的這個功能很多APP都有這個功能,這里為大家提供了完整的實例代碼2018-03-03
Android Activity之間相互調(diào)用與傳遞參數(shù)的原理與用法分析
這篇文章主要介紹了Android Activity之間相互調(diào)用與傳遞參數(shù)的原理與用法,較為詳細的分析了Android組件的構(gòu)成以及Activity的創(chuàng)建、調(diào)用、切換等相關(guān)操作技巧,需要的朋友可以參考下2016-08-08

