- 繼承View必須要重寫構(gòu)造方法
- 構(gòu)造方法中必須有繼承一個(gè)和第二個(gè)
第二個(gè)構(gòu)造方法中有AttributeSet參數(shù)瘩燥,如果沒有這個(gè)參數(shù)則會(huì)報(bào)RutimeException異常锨天;
AttributeSet是用來解析android自帶的layout_width、layout_height、id辫塌、padding原押、margin等屬性的 - 筆 Paint
需要new Paint paint = new Patin(); - 紙 Canvas(畫布)
紙?jiān)趏nDraw(Canvas canvas)方法中傳遞進(jìn)來 - 不要在draw和layout的過程中去實(shí)例化對(duì)象
故不要在onDraw()方法中去實(shí)例化對(duì)象分冈,一般在構(gòu)造函數(shù)去實(shí)例化圾另;
draw和layout很可能是一個(gè)頻繁重復(fù)執(zhí)行的過程,new是需要分配內(nèi)存的雕沉,這樣操作浪費(fèi)內(nèi)存集乔,甚至爆掉 - Paint 各種屬性
setAntiAlias(true) 抗鋸齒
setColor() 設(shè)置畫筆顏色
setStrokeWidth() 設(shè)置描邊線條
setStyle()設(shè)置畫筆樣式
...
paint方法.png
- Canvas 各種屬性
drawCircle() 繪制圓
drawArc() 繪制圓弧
drawBitmap() 繪制位圖
...
canvas方法.png
繪制一個(gè)圓環(huán)
測(cè)量工具類
public class MeasureUtil {
/**
* 獲取屏幕尺寸
* @param activity
* @return 屏幕尺寸像素值,下標(biāo)為0的值為寬度坡椒,下標(biāo)為1的值為高
*/
public static int[] getScreenSize(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return new int[]{metrics.widthPixels, metrics.heightPixels};
}
}
- 初始化畫筆
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true); // 抗鋸齒
/**
* 設(shè)置畫筆樣式
* 1. Paint.Style.STROKE: 描邊
* 2. Paint.Style.FILL_AND_STROKE: 描邊并填充
* 3. Paint.Style.FILL: 填充
*/
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.GREEN); // 設(shè)置畫筆顏色
/**
* 設(shè)置畫筆寬度扰路,單位像素
* setStrokeWidth(0)的時(shí)候?qū)挾炔⒉粸?,為一個(gè)像素
*/
mPaint.setStrokeWidth(10);
}
- 繪制圓
// 繪制圓
canvas.drawCircle(MeasureUtil.getScreenSize(((Activity) mContext))[0] / 2,
MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, 200, mPaint);
circle.png
圓已經(jīng)出來了倔叼,現(xiàn)在我們來整個(gè)動(dòng)態(tài)變換半徑大小的怎么樣汗唱,說干就干,不能慫丈攒。
其實(shí)要?jiǎng)討B(tài)的圓很簡單哩罪,我們只要更改其半徑的大小,然后重繪就行巡验,Android中給我們提供了invalidate()和postInvalidate()方法可以重繪我們的View际插,它們兩者的區(qū)別我等下在下面敘述,我們先來看代碼深碱。
public class SimpleView extends View implements Runnable {
private Paint mPaint;
private Context mContext;
private int radius; // 半徑
public SimpleView(Context context) {
this(context, null);
}
public SimpleView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initPaint();
}
/**
* 初始化畫筆
*/
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true); // 抗鋸齒
/**
* 設(shè)置畫筆樣式
* 1. Paint.Style.STROKE: 描邊
* 2. Paint.Style.FILL_AND_STROKE: 描邊并填充
* 3. Paint.Style.FILL: 填充
*/
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.GREEN); // 設(shè)置畫筆顏色
/**
* 設(shè)置畫筆寬度腹鹉,單位像素
* setStrokeWidth(0)的時(shí)候?qū)挾炔⒉粸?,為一個(gè)像素
*/
mPaint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 繪制圓
canvas.drawCircle(MeasureUtil.getScreenSize(((Activity) mContext))[0] / 2,
MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, radius, mPaint);
}
@Override
public void run() {
/**
* 不斷刷新界面
*/
while (true) {
try {
/**
* 半徑小于等于300則自動(dòng)放大
*/
if(radius <= 300) {
radius += 20;
// 刷新view
postInvalidate();
} else {
radius = 0;
}
// 每100毫秒刷新一次
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
MainActivity中很簡單敷硅,開個(gè)線程就行
simpleView = (SimpleView) findViewById(R.id.main_simpleview);
new Thread(simpleView).start();
dynamic.gif
如果我們把上面的postInvalidate改成invalidate就會(huì)報(bào)下面這個(gè)錯(cuò),
Error1.png
這是為什么呢愉阎,因?yàn)槲覀冊(cè)诜荱I線程中更新UI绞蹦,而我們都知道在Android中非UI線程是不能更新UI的,故我們用postInvalidate榜旦。
postInvalidate()和invalidate()的區(qū)別:
invalidate()必須在主線程中調(diào)用幽七,而postInvalidate()內(nèi)部是由Handler的消息機(jī)制實(shí)現(xiàn)的,所以在任何線程中都可以調(diào)用溅呢,但實(shí)時(shí)性沒有invalidate()強(qiáng)澡屡,一般為了保險(xiǎn)起見,均使用postInvalidate()來刷新界面咐旧。