自定義View
自定義View分類
- ①自繪控件:顧名思義,這一類自定義控件注重控件本身的特性,如形狀、動畫效果等,一般繼承View(但也不絕對,看具體需求),重寫onDraw()方法完成繪製。
- ②組合控件:組合控件的特點是使用原生控件組合成新的控件。
- ③繼承控件:繼承控件的特點是利用了父控件本身的一些特性,在此基礎上添加新的功能。充分利用已有資源,避免了重複的開發。
時鐘案例(思路+代碼)
1.首先創建一個JAVA類繼承於View,裏面有四個構造器,一般情況下調用前2個構造器,
2.進行繪製圖形操作(詳細見代碼部分)
3.繪製完成後爲了實現秒針、分針和時針的走動,需要不斷的向UI線程發送消息從而進行重新繪製,這就需要用到Handler向UI線程不斷髮送消息,同時不斷得到當前系統時間刷新繪圖的位置。必須要調用invalidate();方法提醒UI線程進行重新繪製
4.在佈局文件中要調用自定義View需要寫上包名+路徑(com.my.mywidget.widget.MyView)這樣纔可以被真正的調用
5.代碼實現
public class MyView extends View{
private int width;
private int heigth;
private Paint mPaintLine;
private Paint mPaintCircle;
private Paint mPaintText;
private Paint mPaintPoint;
private Calendar mCalendar;
public static final int NEED_REFRESH=0x23;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case NEED_REFRESH:
mCalendar=Calendar.getInstance();
invalidate();//提醒UI線程重新繪製
handler.sendEmptyMessageDelayed(NEED_REFRESH,1000);//相當於迭代,每隔1s就發送一個空消息,告訴UI線程進行重繪操作
break;
}
}
};
// View有4個構造器
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
mCalendar=Calendar.getInstance();//初始化Calendar,得到當前時間
mPaintLine=new Paint();
mPaintLine.setColor(Color.BLACK);//設置顏色
mPaintLine.setAntiAlias(true);//設置抗鋸齒
mPaintLine.setStrokeWidth(10);//設置線條寬度
mPaintCircle=new Paint();
mPaintCircle.setStrokeWidth(10);//設置線條寬度
mPaintCircle.setColor(Color.BLACK);//設置顏色
mPaintCircle.setAntiAlias(true);//設置抗鋸齒
mPaintCircle.setStyle(Paint.Style.STROKE);//設置爲空心
mPaintText=new Paint();
mPaintText.setColor(Color.BLUE);//設置顏色
mPaintText.setTextAlign(Paint.Align.CENTER);//設置對齊方式
mPaintText.setAntiAlias(true);//設置抗鋸齒
mPaintText.setTextSize(30);//設置字體大小
mPaintPoint=new Paint();
mPaintPoint.setAntiAlias(true);//設置抗鋸齒
mPaintPoint.setStyle(Paint.Style.FILL);//設置爲FILL
mPaintPoint.setStrokeWidth(10);//設置寬度
mPaintPoint.setColor(Color.BLACK);//設置顏色
handler.sendEmptyMessage(NEED_REFRESH);//向Handler發送一個空消息
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width=getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);//得到系統默認的寬度
heigth=getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);//得到系統默認的高度
setMeasuredDimension(width,heigth);//將默認寬高設置上去
}
//onDraw是有UI線程調用,不需要做其他處理
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawLine(0,600,600,600,mPaintLine);
// canvas.drawCircle(300,300,100,mPaintCircle);
canvas.drawCircle(width/2,heigth/2,300,mPaintCircle);
canvas.drawCircle(width/2,heigth/2,10,mPaintPoint);
//利用for循環繪製時鐘的時刻信息
for (int i=1;i<=12;i++){
// canvas.save();和canvas.restore();需要配合使用
//先保存,然後進行旋轉,然後畫線,最後在轉到初始位置,
canvas.save();//保存當前畫布狀態
canvas.rotate(360/12*i,width/2,heigth/2);//第一參數代表旋轉角度,第二和第三個參數代表旋轉中心
canvas.drawLine(width/2,heigth/2-300,width/2,heigth/2-280,mPaintLine);//前兩個表示起始位置,第三和第四個表示末位置,最後一個表示畫筆
canvas.drawText(""+i,width/2,heigth/2-250,mPaintText);//第一個是文本內容,第二和第三個表示顯示文本位置
canvas.restore();//恢復到保存的畫布狀態
}
int minutes = mCalendar.get(Calendar.MINUTE);//得到當前時間的分鐘數
int hours = mCalendar.get(Calendar.HOUR);//得到當前時間的小時數
int second=mCalendar.get(Calendar.SECOND);//得到當前時間的秒數
canvas.save();
Float minutesDegree = minutes / 60f * 360;//得到當前分鐘數所佔的角度
canvas.rotate(minutesDegree, width / 2, heigth / 2);
canvas.drawLine(width / 2, heigth / 2 - 200, width / 2, heigth / 2 + 20, mPaintLine);
canvas.restore();
canvas.save();
Float hoursDegree = (hours * 60 + minutes) / 12f / 60 * 360;//得到當前小時數所佔的角度
canvas.rotate(hoursDegree, width / 2, heigth / 2);
canvas.drawLine(width / 2, heigth / 2 - 100, width / 2, heigth / 2 + 30, mPaintLine);
canvas.restore();
canvas.save();
Float secondDegree = second/60f*360;//得到當前小時數所佔的角度
canvas.rotate(secondDegree, width / 2, heigth / 2);
canvas.drawLine(width / 2, heigth / 2 - 240, width / 2, heigth / 2 + 40, mPaintLine);
canvas.restore();
}
}
圓形ImageView(代碼)
package com.lucasey.demo0808;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* 20行代碼寫出一個圓形的imageview
* @author Administrator
*/
public class CircleImage extends ImageView{
public CircleImage(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
//獲得圖片的寬度
int width=getWidth();
//獲得圖片的高度
int height=getHeight();
//短的二分之一作爲半徑
int radius=height>width?width/2:height/2;
//重新定義的一個畫布,這一步很關鍵
Paint mPaint = new Paint();
//抗鋸齒
mPaint.setAntiAlias(true);
Bitmap bitmap = Bitmap.createBitmap(width,height,
Bitmap.Config.ARGB_8888);
Canvas bitmapCanvas = new Canvas(bitmap);
super.onDraw(bitmapCanvas);
//圓形的框
Bitmap cB = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas cCanv = new Canvas(cB);
//在控件中間畫一個
cCanv.drawCircle(width/ 2, height/ 2, radius,
mPaint);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
//dst是後畫的圖形
mPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.DST_IN));
//一定要用之前的畫布,不然會出現邊角是黑色
bitmapCanvas.drawBitmap(cB, 0.0f, 0.0f, mPaint);
//給圖形加邊框
Paint paint =new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.BLACK);
canvas.drawCircle(width/ 2, height/ 2, radius,
paint);
}
}