最近准備技能大賽,需要將從傳感器中讀出的數據在移動客戶端以圖的形式繪制出來,因為平時很少繪圖,於是各種查資料,算是勉強做出來了。
以下是大賽理論效果圖(左)和實際效果圖(右),真的是理想很豐滿,現實很骨感啊!
制作的整體思路:
- 創建一個繼承與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()】
如果大家有什么更好的方法或該文中有什么不足,希望大家指點。