Android自定义View简易折线图控件(二)
程序员文章站
2023-11-17 12:41:22
继续练习自定义view,这次带来的是简易折线图,支持坐标点点击监听,效果如下:
画坐标轴、画刻度、画点、连线。。x、y轴的数据范围是写死的 1 <= x <...
继续练习自定义view,这次带来的是简易折线图,支持坐标点点击监听,效果如下:
画坐标轴、画刻度、画点、连线。。x、y轴的数据范围是写死的 1 <= x <= 7 ,1 <= y <= 70 。。写活的话涉及到坐标轴刻度的动态计算、坐标点的坐标修改,想想就头大,这里只练习自定义view。
1、在res/values文件夹下新建attrs.xml文件,编写自定义属性:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="linechartview"> <attr name="textcolor" format="color" /> <attr name="linecolor" format="color" /> <attr name="pointcolor" format="color" /> </declare-styleable> </resources>
2、新建linechartview继承view,重写构造方法:
public linechartview(context context) { this(context, null); } public linechartview(context context, attributeset attrs) { this(context, attrs, 0); } public linechartview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); }
3、在第三个构造方法中获取自定义属性的值:
typedarray ta = context.obtainstyledattributes(attrs, r.styleable.linechartview, defstyleattr, 0); mtextcolor = ta.getcolor(r.styleable.linechartview_textcolor, 0xff381a59); mlinecolor = ta.getcolor(r.styleable.linechartview_linecolor, 0xff8e29fa); mpointcolor = ta.getcolor(r.styleable.linechartview_pointcolor, 0xffff5100); mpointradius = densityutils.dp2px(context, 3); ta.recycle();
4、创建画图所使用的对象,如paint、path:
mtextpaint = new paint(paint.anti_alias_flag); mtextpaint.setstyle(paint.style.fill); mtextpaint.setcolor(mtextcolor); mtextpaint.settextsize(40); mlinepaint = new paint(paint.anti_alias_flag); mlinepaint.setstyle(paint.style.stroke); mlinepaint.setcolor(mlinecolor); mlinepaint.setstrokewidth(densityutils.dp2px(context, 2)); mlinepaint.setstrokecap(paint.cap.round); mxypath = new path(); mpointpaint = new paint(paint.anti_alias_flag); mpointpaint.setstyle(paint.style.fill); mpointpaint.setcolor(mpointcolor); mpointcirclepaint = new paint(paint.anti_alias_flag); mpointcirclepaint.setstyle(paint.style.stroke); mpointcirclepaint.setstrokewidth(densityutils.dp2px(context, 2)); mpointcirclepaint.setcolor(mlinecolor);
5、重写onmeasure()方法,计算自定义view的宽高:
@override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { setmeasureddimension(measureddimension(widthmeasurespec), measureddimension(heightmeasurespec)); } private int measureddimension(int measurespec) { int result; int mode = measurespec.getmode(measurespec); int size = measurespec.getsize(measurespec); if (mode == measurespec.exactly) { result = size; } else { result = 500; if (mode == measurespec.at_most) { result = math.min(result, size); } } return result; }
6、暴露一个设置x、y数据集合的方法:
/** * 设置数据 * * @param xlist x轴数据集合 * @param ylist y轴数据集合 */ public void setdatalist(list<integer> xlist, list<integer> ylist) { if (xlist == null || ylist == null || xlist.size() == 0 || ylist.size() == 0) { throw new illegalargumentexception("没有数据"); } if (xlist.size() != ylist.size()) { throw new illegalargumentexception("x、y轴数据长度不一致"); } setpointdata(xlist, ylist); setpointanimator(); } /** * 设置坐标点的数据、坐标 * * @param xlist x轴数据集合 * @param ylist y轴数据集合 */ private void setpointdata(list<integer> xlist, list<integer> ylist) { mpointlist = new arraylist<>(); for (int i = 0; i < xlist.size(); i++) { chartpoint point = new chartpoint(); //设置坐标点的xy数据 point.setxdata(xlist.get(i)); point.setydata(ylist.get(i)); //计算坐标点的横纵坐标 point.setx(xymargin + xlist.get(i) * (getwidth() - 2 * xymargin) / maxx); point.sety(getheight() - xymargin - (getheight() - 2 * xymargin) * ylist.get(i) / maxy); mpointlist.add(point); } } /** * 设置坐标点移动的动画 */ private void setpointanimator() { for (int i = 0; i < mpointlist.size(); i++) { final chartpoint point = mpointlist.get(i); valueanimator anim; if (mlastpointlist != null && mlastpointlist.size() > 0) { anim = valueanimator.ofint(mlastpointlist.get(i).gety(), point.gety()); } else { anim = valueanimator.ofint(getheight() - xymargin, point.gety()); } anim.setduration(500); anim.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { int value = (int) animation.getanimatedvalue(); point.sety(value); invalidate(); } }); anim.start(); } //储存坐标点集合 mlastpointlist = mpointlist; }
7、重写ondraw()方法,绘制坐标轴、刻度,画点连线,注意坐标的计算:
@override protected void ondraw(canvas canvas) { super.ondraw(canvas); if (mpointlist == null || mpointlist.size() == 0) { return; } mxypath.reset(); mxypath.moveto(xymargin, 0); mxypath.lineto(xymargin, getheight() - xymargin); mxypath.lineto(getwidth(), getheight() - xymargin); canvas.drawpath(mxypath, mlinepaint);//画x、y坐标轴 for (int i = 0; i < mpointlist.size(); i++) { //画x轴刻度线 int x = xymargin + (i + 1) * (getwidth() - 2 * xymargin) / mpointlist.size(); canvas.drawline(x, getheight() - xymargin - graduatedlinelength, x, getheight() - xymargin, mlinepaint); //画y轴刻度线 int y = getheight() - xymargin - (i + 1) * (getheight() - 2 * xymargin) / mpointlist.size(); canvas.drawline(xymargin, y, xymargin + graduatedlinelength, y, mlinepaint); //画坐标轴刻度文本 canvas.drawtext(string.valueof(mpointlist.get(i).getxdata()), x, getheight() - mtextpaint.gettextsize() / 4, mtextpaint); canvas.drawtext(string.valueof((i + 1) * 10), 0, y + mtextpaint.gettextsize() / 2, mtextpaint); } //画连接线 for (int i = 0; i < mpointlist.size(); i++) { if (i != mpointlist.size() - 1) { chartpoint lastp = mpointlist.get(i); chartpoint nextp = mpointlist.get(i + 1); canvas.drawline(lastp.getx(), lastp.gety(), nextp.getx(), nextp.gety(), mlinepaint); } } //画坐标点 for (int i = 0; i < mpointlist.size(); i++) { chartpoint point = mpointlist.get(i); canvas.drawcircle(point.getx(), point.gety(), mpointradius, mpointpaint); canvas.drawcircle(point.getx(), point.gety(), mpointradius, mpointcirclepaint); } }
8、设置坐标点点击事件:
private onpointclicklistener monpointclicklistener; /** * 坐标点点击监听 */ public interface onpointclicklistener { /** * @param index 当前坐标点在数据集中的下标 * @param point 当前坐标点对象 */ void onpointclick(int index, chartpoint point); } public void setonpointclicklistener(onpointclicklistener onpointclicklistener) { monpointclicklistener = onpointclicklistener; }
9、重写ontouchevent()方法,判断当前点击的点是不是在坐标点范围内:
@override public boolean ontouchevent(motionevent event) { switch (event.getaction()) { case motionevent.action_down: //判断当前点击的点是否在坐标点范围内 int curx = (int) event.getx(); int cury = (int) event.gety(); for (int i = 0; i < mpointlist.size(); i++) { chartpoint point = mpointlist.get(i); double d1 = math.pow(curx - point.getx(), 2); double d2 = math.pow(cury - point.gety(), 2); //√ ̄(curx - cx)² + (cury - cy)² < r if (math.sqrt(d1 + d2) < mpointradius + 10) {//为了方便点击,把坐标点范围增大了10像素 if (monpointclicklistener != null) { monpointclicklistener.onpointclick(i, point); } } } break; } return super.ontouchevent(event); }
10、在activity_main.xml布局文件中使用该view:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:lcv="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" tools:context=".mainactivity"> <com.monkey.linechartview.linechartview android:id="@+id/chartview" android:layout_width="250dp" android:layout_height="250dp" android:layout_margintop="@dimen/activity_vertical_margin" lcv:linecolor="#8e29fa" lcv:pointcolor="#ff5100" lcv:textcolor="#000000" /> <button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margintop="@dimen/activity_vertical_margin" android:text="set data" android:textallcaps="false" /> </linearlayout>
11、在mainactivity.java中传入数据集合,并设置坐标点点击监听:
btn.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { list<integer> xlist = new arraylist<>(); list<integer> ylist = new arraylist<>(); for (int i = 0; i < 7; i++) { xlist.add(i + 1); int y = (int) (math.random() * 70 + 1); ylist.add(y); } chartview.setdatalist(xlist, ylist); } }); chartview.setonpointclicklistener(new linechartview.onpointclicklistener() { @override public void onpointclick(int position, chartpoint point) { tv.settext("position:" + position + "\nx:" + point.getxdata() + "\ny:" + point.getydata()); } });
致此大致步骤完成了,发现和步骤差不多。。代码已上传github:
https://github.com/monkeymushroom/linechartview/tree/master
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: ThinkPHP模板IF标签用法详解
下一篇: ThinkPHP模板比较标签用法详解