Android之自訂View以及畫一個時鐘
概述:
當Android內建的View滿足不了開發人員時,自訂View就發揮了很好的作用。
建立一個自訂View,需要繼承於View類,並且實現其中的至少一個建構函式和兩個方法:onMeasure()和onDraw();
onMeasure()用於設定自訂View的尺寸,onDraw()用於繪製View中的內容。
在onDraw()方法中,需要調用畫筆繪製圖形或文本,繪製的模板時Canvas對象, Canvas類中用來繪製圖形文本的方法有:
drawRect(RectF rect, Paint paint) //繪製地區,參數一為RectF一個地區
drawPath(Path path, Paint paint) //繪製一個路徑,參數一為Path路徑對象
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //貼圖,參數一就是我們常規的Bitmap對象,參數二是來源區域(這裡是bitmap),參數三是目的地區域(應該在canvas的位置和大小),參數四是Paint畫刷對象,因為用到了縮放和展開的可能,當原始Rect不等於目標Rect時效能將會有大幅損失。
drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) //畫線,參數一起始點的x軸位置,參數二起始點的y軸位置,參數三終點的x軸水平位置,參數四y軸垂直位置,最後一個參數為Paint 畫刷對象。
drawPoint(float x, float y, Paint paint) //畫點,參數一水平x軸,參數二垂直y軸,第三個參數為Paint對象。
drawText(String text, float x, floaty, Paint paint) //渲染文本,Canvas類除了上面的還可以描繪文字,參數一是String類型的文本,參數二x軸,參數三y軸,參數四是Paint對象。
drawOval(RectF oval, Paint paint)//畫橢圓,參數一是掃描地區,參數二為paint對象;
drawCircle(float cx, float cy, float radius,Paint paint)// 繪製圓,參數一是中心點的x軸,參數二是中心點的y軸,參數三是半徑,參數四是paint對象;
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//畫弧,參數一是RectF對象,一個矩形地區橢圓形的界限用於定義在形狀、大小、電弧,參數二是起始角(度)在電弧的開始,參數三掃掠角度(度)開始順時針測量的,參數四是如果這是真的話,包括橢圓中心的電弧,並關閉它,如果它是假這將是一個弧線,參數五是Paint對象。
繪製圖形需要畫筆Paint對象,Paint類中的方法有:
setARGB(int a, int r, int g, int b) // 設定 Paint對象顏色,參數一為alpha透明值
setAlpha(int a) // 設定alpha不透明度,範圍為0~255
setAntiAlias(boolean aa) // 是否消除鋸齒
setColor(int color) // 設定顏色,這裡Android內部定義的有Color類包含了一些常見顏色定義
setTextScaleX(float scaleX) // 設定文本縮放倍數,1.0f為原始
setTextSize(float textSize) // 設定字型大小
setUnderlineText(booleanunderlineText) // 設定底線
Demo
一個自訂時鐘視圖的寫法:
public class MyView extends View { private int width; private int height; private Paint mPaintLine; private Paint mPaintCircle; private Paint mPaintHour; private Paint mPaintMinute; private Paint mPaintSec; private Paint mPaintText; private Calendar mCalendar; public static final int NEED_INVALIDATE = 0X23; //每隔一秒,在handler中調用一次重新繪製方法 private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case NEED_INVALIDATE: mCalendar = Calendar.getInstance(); invalidate();//告訴UI主線程重新繪製 handler.sendEmptyMessageDelayed(NEED_INVALIDATE,1000); break; default: break; } } }; public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); mCalendar = Calendar.getInstance(); mPaintLine = new Paint(); mPaintLine.setColor(Color.BLUE); mPaintLine.setStrokeWidth(10); mPaintCircle = new Paint(); mPaintCircle.setColor(Color.GREEN);//設定顏色 mPaintCircle.setStrokeWidth(10);//設定線寬 mPaintCircle.setAntiAlias(true);//設定是否消除鋸齒 mPaintCircle.setStyle(Paint.Style.STROKE);//設定繪製風格 mPaintText = new Paint(); mPaintText.setColor(Color.BLUE); mPaintText.setStrokeWidth(10); mPaintText.setTextAlign(Paint.Align.CENTER); mPaintText.setTextSize(40); mPaintHour = new Paint(); mPaintHour.setStrokeWidth(20); mPaintHour.setColor(Color.BLUE); mPaintMinute = new Paint(); mPaintMinute.setStrokeWidth(15); mPaintMinute.setColor(Color.BLUE); mPaintSec = new Paint(); mPaintSec.setStrokeWidth(10); mPaintSec.setColor(Color.BLUE); handler.sendEmptyMessage(NEED_INVALIDATE);//向handler發送一個訊息,讓它開啟重繪 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int circleRadius = 400; //畫出大圓 canvas.drawCircle(width / 2, height / 2, circleRadius, mPaintCircle); //畫出圓中心 canvas.drawCircle(width / 2, height / 2, 20, mPaintCircle); //依次旋轉畫布,畫出每個刻度和對應數字 for (int i = 1; i <= 12; i++) { canvas.save();//儲存當前畫布 canvas.rotate(360/12*i,width/2,height/2); //左起:起始位置x座標,起始位置y座標,終止位置x座標,終止位置y座標,畫筆(一個Paint對象) canvas.drawLine(width / 2, height / 2 - circleRadius, width / 2, height / 2 - circleRadius + 30, mPaintCircle); //左起:常值內容,起始位置x座標,起始位置y座標,畫筆 canvas.drawText(+i, width / 2, height / 2 - circleRadius + 70, mPaintText); canvas.restore();// } int minute = mCalendar.get(Calendar.MINUTE);//得到當前分鐘數 int hour = mCalendar.get(Calendar.HOUR);//得到當前小時數 int sec = mCalendar.get(Calendar.SECOND);//得到當前秒數 float minuteDegree = minute/60f*360;//得到分針旋轉的角度 canvas.save(); canvas.rotate(minuteDegree, width / 2, height / 2); canvas.drawLine(width / 2, height / 2 - 250, width / 2, height / 2 + 40, mPaintMinute); canvas.restore(); float hourDegree = (hour*60+minute)/12f/60*360;//得到時鐘旋轉的角度 canvas.save(); canvas.rotate(hourDegree, width / 2, height / 2); canvas.drawLine(width / 2, height / 2 - 200, width / 2, height / 2 + 30, mPaintHour); canvas.restore(); float secDegree = sec/60f*360;//得到秒針旋轉的角度 canvas.save(); canvas.rotate(secDegree,width/2,height/2); canvas.drawLine(width/2,height/2-300,width/2,height/2+40,mPaintSec); canvas.restore(); }}
需要在布局中載入自訂View,名字必須是全名(包括包名):
activity_timer
主活動setContentView就行了
public class TimerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_timer); }}
示範結果: