最近准备技能大赛,需要将从传感器中读出的数据在移动客户端以图的形式绘制出来,因为平时很少绘图,于是各种查资料,算是勉强做出来了。
以下是大赛理论效果图(左)和实际效果图(右),真的是理想很丰满,现实很骨感啊!
制作的整体思路:
- 创建一个继承与View类自定义类
- 自定义类覆盖其中的onDraw()方法
- 在MainActivity中invalidate()方法来调用onDraw()方法来进行图形的重绘.
绘制一个基本表:
(注意:代码中使用了变量)
1、绘制矩形
Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setStyle(Paint.Style.STROKE); Rect chartRec = new Rect(OFFSET_LEFT, OFFSET_TOP, CHARTW + OFFSET_LEFT, CHARTH + OFFSET_TOP); canvas.drawRect(chartRec, paint);
2、绘制左侧数值标记
canvas.drawText("100", OFFSET_LEFT - TEXT_OFFSET - 15, OFFSET_TOP + 5, paint); for (int i = 9; i > 0; i--) { canvas.drawText("" + 10 * (10 - i), OFFSET_LEFT - TEXT_OFFSET - 15, OFFSET_TOP + CHARTH / 10 * i, paint); } canvas.drawText("0", OFFSET_LEFT - TEXT_OFFSET - 10, OFFSET_TOP + CHARTH, paint);
3、绘制虚线
DashPathEffect是PathEffect类的一个子类,可以使paint画出类似虚线的样子,并且可以任意指定虚实的排列方式。
代码中的float数组,必须是偶数长度,且>=2,指定了多少长度的实线之后再画多少长度的空白.
如本代码中,绘制长度2的实线,再绘制长度2的空白,再绘制长度2的实线,再绘制长度2的空白,依次重复.1是偏移量
PathEffect effects = new DashPathEffect(new float[] { 2, 2, 2, 2 }, 1);
这样一个基本的表格绘制完成。
动态改变界面的方法:
Handler handler=new Handler(); Runnable runnable=new Runnable(){ @Override public void run() { // TODO Auto-generated method stub //要做的事情 handler.postDelayed(this, 1000); } };
在这里我们采用消息传递机制中Handler的postDelayed(Runnable, long) 方法做定时器,每隔一秒钟发送一次Runnable对象(该对象最后将会被封装成Message对象)执行一次子线程中的操作。
最后,贴上所有代码:
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:id="@+id/root" android:orientation = "vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> </LinearLayout> </LinearLayout>
MainActivity.class
public class MainActivity extends Activity { private Handler handler; private DrawTest dtest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); } private void init() { LinearLayout layout = (LinearLayout) findViewById(R.id.root); dtest = new DrawTest(this); dtest.invalidate(); layout.addView(dtest); handler = new Handler(); handler.post(new Runnable() { @Override public void run() { dtest.invalidate(); handler.postDelayed(this, 2000); } }); } }
DrawTest.class
public class DrawTest extends View { private int CHARTH = 600;//表格的高 private int CHARTW = 400;//表格的宽 private int OFFSET_LEFT = 70;//距离左边界距离 private int OFFSET_TOP = 80;//距离右边界距离 private int TEXT_OFFSET = 20;//文本距离设置 private int X_INTERVAL = 20;//X坐标间隔距离 private List<Point> plist;//点集合 public DrawTest(Context context) { super(context); plist = new ArrayList<Point>(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawTable(canvas); preparePoint(); drawPoint(canvas); } /** * 绘制表 * @param canvas */ private void drawTable(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setStyle(Paint.Style.STROKE); Rect chartRec = new Rect(OFFSET_LEFT, OFFSET_TOP, CHARTW + OFFSET_LEFT, CHARTH + OFFSET_TOP); canvas.drawRect(chartRec, paint); Path textPath = new Path();// 选择一块区域,准备写文字“曲线图测试” paint.setStyle(Paint.Style.FILL); textPath.moveTo(200, 30);// 区域开始 textPath.lineTo(400, 30);// 区域结束 paint.setTextSize(20); paint.setAntiAlias(true);// 指定是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。 canvas.drawTextOnPath("曲线图测试", textPath, 0, 0, paint); // 左侧数值标记 canvas.drawText("100", OFFSET_LEFT - TEXT_OFFSET - 15, OFFSET_TOP + 5, paint); for (int i = 9; i > 0; i--) { canvas.drawText("" + 10 * (10 - i), OFFSET_LEFT - TEXT_OFFSET - 15, OFFSET_TOP + CHARTH / 10 * i, paint); } canvas.drawText("0", OFFSET_LEFT - TEXT_OFFSET - 10, OFFSET_TOP + CHARTH, paint); // 绘制虚线 Path path = new Path(); /** * PathEffect是用来控制绘制轮廓(线条)的方式。 * DashPathEffect是PathEffect类的一个子类,可以使paint画出类似虚线的样子,并且可以任意指定虚实的排列方式. * 代码中的float数组,必须是偶数长度,且>=2,指定了多少长度的实线之后再画多少长度的空白. * 如本代码中,绘制长度2的实线,再绘制长度2的空白,再绘制长度2的实线,再绘制长度2的空白,依次重复.1是偏移量, */ PathEffect effects = new DashPathEffect(new float[] { 2, 2, 2, 2 }, 1); paint.setStyle(Paint.Style.STROKE); paint.setAntiAlias(false); paint.setPathEffect(effects);// 用于设置绘制路径时的路径效果,如点划线。 for (int i = 1; i < 10; i++) { path.moveTo(OFFSET_LEFT, OFFSET_TOP + CHARTH / 10 * i); path.lineTo(OFFSET_LEFT + CHARTW, OFFSET_TOP + CHARTH / 10 * i); canvas.drawPath(path, paint); } } /** * 准备绘制点 */ private void preparePoint() { //设置点的Y坐标为30-40 int py = (CHARTH/10)*6+OFFSET_TOP + (int) Math.rint((Math.random() * (CHARTH/10))); Point p = new Point(OFFSET_LEFT + CHARTW, py); if (plist.size() > 21) { plist.remove(0);//控制点的个数 //改变每一个点的X坐标 for (int i = 0; i < 20; i++) { if (i == 0) plist.get(i).x -= (X_INTERVAL - 2); else plist.get(i).x -= X_INTERVAL; } plist.add(p); } else { for (int i = 0; i < plist.size() - 1; i++) { plist.get(i).x -= X_INTERVAL; } plist.add(p); } } /** * 绘制点 * * @param canvas */ private void drawPoint(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setStrokeWidth(3);// 设置笔触的宽度 if (plist.size() >= 2) { for (int i = 0; i < plist.size() - 1; i++) { canvas.drawPoint(plist.get(i).x, plist.get(i).y, paint); } } } }
【注:invalidate ()和postInvalidate()方法的选择:文档中已经写的很清楚了,如果要使用invalidate()必须要在UI主线程当中,如果不在UI主线程中,就要去调用postInValidate()】
如果大家有什么更好的方法或该文中有什么不足,希望大家指点。