Android自定義控件實(shí)現(xiàn)折線圖
本文實(shí)例實(shí)現(xiàn)一個(gè)如下圖所示的Android折線圖,供大家參考,具體內(nèi)容如下



首先是控件繪圖區(qū)域的劃分,控件左邊取一小部分(控件總寬度的八分之一)繪制表頭,右邊剩余的部分繪制表格
確定表格的行列數(shù),首先繪制一個(gè)三行八列的網(wǎng)格,設(shè)置好行列的坐標(biāo)后開始繪制
/*繪制三條橫線*/
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);
}
網(wǎng)格繪制完成后,開始繪制折線圖
根據(jù)輸入的節(jié)點(diǎn)數(shù)據(jù),分別繪制兩條折線
通過canvas的drawLine方法依次連接兩點(diǎn)即可
在每個(gè)數(shù)據(jù)節(jié)點(diǎn)處繪制一個(gè)小圓,突出顯示
/*繪制第一條折線的路徑*/
for (int i = 0; i < mPerformance_1.length - 1; i++) {
/*折線圖的折線的畫筆設(shè)置粗一點(diǎn)*/
mPaintLine.setStrokeWidth(5);
/*計(jì)算當(dāng)前節(jié)點(diǎn)的坐標(biāo)值*/
float prePointX =mLineXs[i];
float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i].type]) * animCurrentValue;
/*計(jì)算下一個(gè)節(jié)點(diǎn)的坐標(biāo)值*/
float nextPointX=mLineXs[i + 1];
float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i + 1].type]) * animCurrentValue;
/*連接當(dāng)前坐標(biāo)和下一個(gè)坐標(biāo),繪制線段*/
canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine1);
/*當(dāng)前節(jié)點(diǎn)坐標(biāo)處繪制小圓*/
canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint);
}
兩條折線重合的地方,需要特殊考慮,比如希望兩條折線重合的地方折線變?yōu)榘咨?/p>
設(shè)置下兩條折線的畫筆即可
mPaintLine2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); mPaintLine1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));

測(cè)試代碼及效果;
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;
/*動(dòng)畫插值器*/
DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
/*動(dòng)畫刷新的次數(shù)*/
private int mDuration = 10;
/*當(dāng)前動(dòng)畫進(jìn)度值*/
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();
/*坐標(biāo)點(diǎn)的小圓點(diǎn)畫筆*/
private Paint mPointPaint = new Paint();
private float mSmallDotRadius = 4;
private TypedValue typedValue;
private int mPaintClolor;
/*Handler刷新界面產(chǎn)生動(dòng)畫效果*/
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();
}
}
/**
* 設(shè)置折線1的顏色
*
* @param mLineColor1
*/
public void setLineColor1(int mLineColor1) {
this.mLineColor1 = mLineColor1;
}
/**
* 設(shè)置折線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;
/*左邊留一點(diǎn)空白*/
float left_offset = 10;
/*折線圖的總寬度等于控件的總寬度減去表頭和留白*/
float chartWide = totalWidth - textWide - left_offset;
/*一共三行,設(shè)置每一行的垂直坐標(biāo)*/
float[] mLineYs = new float[]{totalHeight / 8, totalHeight / 2, totalHeight * 7 / 8};
/*一共八列,設(shè)置每一列的水平坐標(biāo)*/
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);
/*測(cè)量文字大小,并計(jì)算偏移量*/
Paint.FontMetrics fontMetrics = mPaintText.getFontMetrics();
float textBaseLineOffset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
canvas.drawText("勝場(chǎng)", textWide / 2, mLineYs[0] + textBaseLineOffset, mPaintText);
canvas.drawText("平局", textWide / 2, mLineYs[1] + textBaseLineOffset, mPaintText);
canvas.drawText("負(fù)場(chǎng)", textWide / 2, mLineYs[2] + textBaseLineOffset, mPaintText);
/*繪制表格畫筆設(shè)置*/
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);
}
/*折線圖畫筆設(shè)置*/
mPaintLine1.setStyle(Paint.Style.STROKE);
/*設(shè)置透明度,取值范圍為0~255,數(shù)值越小越透明,0表示完全透明*/
mPaintLine1.setAlpha(0);
mPaintLine1.setAntiAlias(true);
mPaintLine1.setColor(mLineColor1);
mPaintLine1.setStrokeWidth(5);
mPaintLine2.setStyle(Paint.Style.STROKE);
/*設(shè)置透明度,取值范圍為0~255,數(shù)值越小越透明,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));
}
/*畫節(jié)點(diǎn)處的小圓點(diǎn)的畫筆設(shè)置*/
mPointPaint.setStyle(Paint.Style.STROKE);
mPointPaint.setAntiAlias(true);
mPointPaint.setColor(mPaintClolor);
/*計(jì)算當(dāng)前動(dòng)畫進(jìn)度對(duì)應(yīng)的數(shù)值*/
float animCurrentValue = mDecelerateInterpolator.getInterpolation(1.0f * mCurrentTime / mDuration);
mPaintLine.setColor(mLineColor1);
/*繪制第一條折線的路徑*/
for (int i = 0; i < mPerformance_1.length - 1; i++) {
/*折線圖的折線的畫筆設(shè)置粗一點(diǎn)*/
mPaintLine.setStrokeWidth(5);
/*計(jì)算當(dāng)前節(jié)點(diǎn)的坐標(biāo)值*/
float prePointX =mLineXs[i];
float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i].type]) * animCurrentValue;
/*計(jì)算下一個(gè)節(jié)點(diǎn)的坐標(biāo)值*/
float nextPointX=mLineXs[i + 1];
float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[i + 1].type]) * animCurrentValue;
/*連接當(dāng)前坐標(biāo)和下一個(gè)坐標(biāo),繪制線段*/
canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine1);
/*當(dāng)前節(jié)點(diǎn)坐標(biāo)處繪制小圓*/
canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint);
}
/*第一個(gè)折線圖的最后一個(gè)節(jié)點(diǎn)的坐標(biāo)*/
float lastPointX=mLineXs[mPerformance_1.length - 1];
float lastPointY= mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_1[mPerformance_1.length - 1].type]) * animCurrentValue;
/*繪制最后一個(gè)節(jié)點(diǎn)的外圍小圓*/
canvas.drawCircle(lastPointX,lastPointY ,mSmallDotRadius, mPointPaint);
/*繪制第二條折線*/
mPaintLine.setColor(mLineColor2);
for (int i = 0; i < mPerformance_2.length - 1; i++) {
/*折線圖的折線的畫筆設(shè)置粗一點(diǎn)*/
mPaintLine.setStrokeWidth(5);
/*計(jì)算當(dāng)前節(jié)點(diǎn)的坐標(biāo)值*/
float prePointX =mLineXs[i];
float prePointY =mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[i].type]) * animCurrentValue;
/*計(jì)算下一個(gè)節(jié)點(diǎn)的坐標(biāo)值*/
float nextPointX=mLineXs[i + 1];
float nextPointY=mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[i + 1].type]) * animCurrentValue;
/*連接當(dāng)前坐標(biāo)和下一個(gè)坐標(biāo),繪制線段*/
canvas.drawLine(prePointX, prePointY, nextPointX, nextPointY, mPaintLine2);
/*當(dāng)前節(jié)點(diǎn)坐標(biāo)處繪制小圓*/
canvas.drawCircle(prePointX, prePointY, mSmallDotRadius, mPointPaint);
}
/*第一個(gè)折線圖的最后一個(gè)節(jié)點(diǎn)的坐標(biāo)*/
lastPointX=mLineXs[mPerformance_2.length - 1];
lastPointY= mLineYs[2] - (mLineYs[2] - mLineYs[mPerformance_2[mPerformance_2.length - 1].type]) * animCurrentValue;
/*繪制最后一個(gè)節(jié)點(diǎn)的外圍小圓*/
canvas.drawCircle(lastPointX,lastPointY ,mSmallDotRadius, mPointPaint);
mHandler.postDelayed(mAnimation, 20);
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Android圖表 MPAndroidChart折線圖
- MPAndroidChart開源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖
- Android MPAndroidChart開源庫(kù)圖表之折線圖的實(shí)例代碼
- Android自定義View實(shí)現(xiàn)折線圖效果
- Android繪制動(dòng)態(tài)折線圖
- Android HelloChart開源庫(kù)圖表之折線圖的實(shí)例代碼
- Android開發(fā)之天氣趨勢(shì)折線圖
- Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
- Android自定義View簡(jiǎn)易折線圖控件(二)
- Android開發(fā)RecyclerView實(shí)現(xiàn)折線圖效果
相關(guān)文章
android實(shí)現(xiàn)按鈕獲取焦點(diǎn)延遲加載
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)按鈕獲取焦點(diǎn)延遲加載,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Android中AlertDilog顯示簡(jiǎn)單和復(fù)雜列表的方法
這篇文章主要介紹了Android中AlertDialog顯示簡(jiǎn)單和復(fù)雜列表的方法,結(jié)合實(shí)例形式分析了Android的AlertDialog創(chuàng)建列表顯示對(duì)話框的相關(guān)方法與常見操作技巧,需要的朋友可以參考下2016-08-08
Android重力傳感器實(shí)現(xiàn)滾動(dòng)的彈球
所謂傳感器能夠探測(cè)如光、熱、溫度、重力、方向 等等的功能,本文給大家介紹Android傳感器應(yīng)用之重力傳感器實(shí)現(xiàn)滾動(dòng)的彈球,對(duì)android重力傳感器相關(guān)知識(shí)感興趣的朋友一起看看吧2015-12-12
Android IPC機(jī)制Messenger實(shí)例詳解
這篇文章主要介紹了 Android IPC機(jī)制Messenger實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07
Android學(xué)習(xí)筆記之ActionBar Item用法分析
這篇文章主要介紹了Android學(xué)習(xí)筆記之ActionBar Item用法,結(jié)合實(shí)例形式分析了ActionBar Item的具體功能與相關(guān)使用技巧,需要的朋友可以參考下2017-05-05
Android 設(shè)置主題實(shí)現(xiàn)點(diǎn)擊波紋效果的示例
本篇文章主要介紹了Android 設(shè)置主題實(shí)現(xiàn)點(diǎn)擊波紋效果的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11

