Android自定义控件实现折线图
程序员文章站
2022-04-12 11:32:10
本文实例实现一个如下图所示的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编程ProgressBar自定义样式之动画模式实现方法
-
Android在OnCreate中获取控件的宽度和高度的实现代码
-
Android编程使用自定义shape实现shadow阴影效果的方法
-
Android自定义View圆形百分比控件(一)
-
Android自定义ScrollView实现放大回弹效果实例代码
-
Android自定义View圆形进度条控件(三)
-
Android自定义View简易折线图控件(二)
-
android 自定义ScrollView实现背景图片伸缩的实现代码及思路
-
Android自定义View设定到FrameLayout布局中实现多组件显示的方法 分享
-
Android 自定义View实现抽屉效果