
feiji.png
本文主要介紹自定義View繪圖的使用,上面是一個(gè)可拖動(dòng)的飛機(jī),并且不斷發(fā)射出子彈,完全使用自定義View繪圖實(shí)現(xiàn)的動(dòng)態(tài)效果,下面是一組動(dòng)態(tài)效果。

飛機(jī)大戰(zhàn).gif
下面我先介紹一下View中比較重要的幾個(gè)方法跟參數(shù):
- onFinishlnflate(): 這是一個(gè)回調(diào)方法,當(dāng)應(yīng)用從XML布局文件加載該組件并利用它來構(gòu)造界面后,回調(diào)該方法。
- onMeasure(int,int):調(diào)用改方法來檢測(cè)View組件以及所包含的所有子組件的大小。
- omLayout(boolean,int,int,int,int):當(dāng)該組件需要分配其子組件的位置,大小時(shí)回調(diào)。
- onSizeChanged(int,int,int,int):組件的大小發(fā)生改變的時(shí)候回調(diào)。
- onDraw(Canvas):組件繪制內(nèi)容的時(shí)候調(diào)用該方法進(jìn)行繪制。
- onKeyDown(int,KeyEvent):當(dāng)某個(gè)鍵被按下時(shí)觸發(fā)。
- onKeyUp(int,KeyEvent):松開某個(gè)鍵時(shí)觸發(fā)。
- onTrackballEvent(MotionEvent):發(fā)生軌跡球事件時(shí)觸發(fā)。
- onTouchEvent(MotionEvent):發(fā)生觸摸屏事件時(shí)觸發(fā)。
- onFocusChanged(boolean gainFocus,int direction,Rect previouslyFocusedRect):該組件焦點(diǎn)發(fā)生改變時(shí)觸發(fā)。
- onWindowFocusChanged(boolean):包含改組件的窗口失去或者得到焦點(diǎn)時(shí)觸發(fā)。
- onAttachedToWindow():把該組件放入某個(gè)窗口時(shí)觸發(fā)。
- onDetachedFromWindow():把該組件從某個(gè)窗口上分離時(shí)觸發(fā)。
- onWindowVisibilityChanged(int):包含該組件的窗口的可見性發(fā)生改變時(shí)觸發(fā)。
下面是本文的重點(diǎn),繪制圖形所涉及的重要幾何圖形繪制方法以及實(shí)例:
Paint p = new Paint();p.setColor(Color.RED);// 設(shè)置紅色canvas.drawText("畫圓:", 10, 20, p);// 畫文本canvas.drawCircle(60, 20, 10, p);// 小圓p.setAntiAlias(true);// 設(shè)置畫筆的鋸齒效果。canvas.drawCircle(120, 20, 20, p);// 大圓canvas.drawText("畫線及弧線:", 10, 60, p);p.setColor(Color.GREEN);// 設(shè)置綠色canvas.drawLine(60, 40, 100, 40, p);// 畫線canvas.drawLine(110, 40, 190, 80, p);// 斜線//畫笑臉弧線p.setStyle(Paint.Style.STROKE);//設(shè)置空心RectF oval1=new RectF(150,20,180,40);canvas.drawArc(oval1, 180, 180, false, p);//小弧形oval1.set(190, 20, 220, 40);canvas.drawArc(oval1, 180, 180, false, p);//小弧形oval1.set(160, 30, 210, 60);canvas.drawArc(oval1, 0, 180, false, p);//小弧形canvas.drawText("畫矩形:", 10, 80, p);p.setColor(Color.GRAY);// 設(shè)置灰色p.setStyle(Paint.Style.FILL);//設(shè)置填滿canvas.drawRect(60, 60, 80, 80, p);// 正方形canvas.drawRect(60, 90, 160, 100, p);// 長(zhǎng)方形canvas.drawText("畫扇形和橢圓:", 10, 120, p);/* 設(shè)置漸變色 這個(gè)正方形的顏色是改變的 */Shader mShader = new LinearGradient(0, 0, 100, 100,new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,Color.LTGRAY }, null, Shader.TileMode.REPEAT); // 一個(gè)材質(zhì),打造出一個(gè)線性梯度沿著一條線。p.setShader(mShader);// p.setColor(Color.BLUE);RectF oval2 = new RectF(60, 100, 200, 240);// 設(shè)置個(gè)新的長(zhǎng)方形,掃描測(cè)量canvas.drawArc(oval2, 200, 130, true, p);// 畫弧,第一個(gè)參數(shù)是RectF:該類是第二個(gè)參數(shù)是角度的開始,第三個(gè)參數(shù)是多少度,第四個(gè)參數(shù)是真的時(shí)候畫扇形,是假的時(shí)候畫弧線//畫橢圓,把oval改一下oval2.set(210,100,250,130);canvas.drawOval(oval2, p);canvas.drawText("畫三角形:", 10, 200, p);// 繪制這個(gè)三角形,你可以繪制任意多邊形Path path = new Path();path.moveTo(80, 200);// 此點(diǎn)為多邊形的起點(diǎn)path.lineTo(120, 250);path.lineTo(80, 250);path.close(); // 使這些點(diǎn)構(gòu)成封閉的多邊形canvas.drawPath(path, p);// 你可以繪制很多任意多邊形,比如下面畫六連形p.reset();//重置p.setColor(Color.LTGRAY);p.setStyle(Paint.Style.STROKE);//設(shè)置空心Path path1=new Path(); //Path類是連接路徑path1.moveTo(180, 200);path1.lineTo(200, 200);path1.lineTo(210, 210);path1.lineTo(200, 220);path1.lineTo(180, 220);path1.lineTo(170, 210);path1.close();//封閉canvas.drawPath(path1, p);//畫圓角矩形p.setStyle(Paint.Style.FILL);//充滿p.setColor(Color.LTGRAY);p.setAntiAlias(true);// 設(shè)置畫筆的鋸齒效果canvas.drawText("畫圓角矩形:", 10, 260, p);RectF oval3 = new RectF(80, 260, 200, 300);// 設(shè)置個(gè)新的長(zhǎng)方形canvas.drawRoundRect(oval3, 20, 15, p);//第二個(gè)參數(shù)是x半徑,第三個(gè)參數(shù)是y半徑//畫貝塞爾曲線canvas.drawText("畫貝塞爾曲線:", 10, 310, p);p.reset();p.setStyle(Paint.Style.STROKE);p.setColor(Color.GREEN);Path path2=new Path();path2.moveTo(100, 320);//設(shè)置Path的起點(diǎn)path2.quadTo(150, 310, 170, 400); //設(shè)置貝塞爾曲線的控制點(diǎn)坐標(biāo)和終點(diǎn)坐標(biāo)canvas.drawPath(path2, p);//畫出貝塞爾曲線//畫點(diǎn)p.setStyle(Paint.Style.FILL);canvas.drawText("畫點(diǎn):", 10, 390, p);canvas.drawPoint(60, 390, p);//畫一個(gè)點(diǎn)canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//畫多個(gè)點(diǎn)//畫圖片Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);canvas.drawBitmap(bitmap, 250,360, p);
除了以上繪制的方法,Canvas還提供了如下方法進(jìn)行坐標(biāo)變換:
- rotate(float degrees,float px,float py):對(duì)Canvas執(zhí)行旋轉(zhuǎn)變換。
- scale(float sx,float sy,float px,float py):對(duì)Canvas執(zhí)行縮放變換。
- skew(float sx,float sy):對(duì)Canvas執(zhí)行傾斜變換。
- trnslate(float dx,float dy):移動(dòng)Canvas.向右移動(dòng)dx距離(為負(fù)數(shù)相反方向移動(dòng)),向下移動(dòng)dy距離(為負(fù)數(shù)相反方向移動(dòng))。
Paint 代表了Canvas上的畫筆、畫刷、顏料等等,Paint類常用方法:
- setARGB(int a, int r, int g, int b) // 設(shè)置 Paint對(duì)象顏色,參數(shù)一為alpha透明值
- setAlpha(int a) // 設(shè)置alpha不透明度,范圍為0~255
- setAntiAlias(boolean aa) // 是否抗鋸齒
- setColor(int color) // 設(shè)置顏色,這里Android內(nèi)部定義的有Color類包含了一些常見顏色定義
- setTextScaleX(float scaleX) // 設(shè)置文本縮放倍數(shù),1.0f為原始
- setTextSize(float textSize) // 設(shè)置字體大小
- setUnderlineText(booleanunderlineText) // 設(shè)置下劃線
- setStyle(Paint.Style style) //設(shè)置填充的風(fēng)格
- setStrokeLayer(float radius,float dx,float dy,int color) //設(shè)置陰影。
- setStrokeWidth(float width) //設(shè)置畫筆的筆觸寬度
學(xué)習(xí)了上面的API知識(shí),我們可以開始繪制自己需要的View跟效果了,下面是我模擬飛機(jī)射擊,繪出來的一個(gè)View。主要還是注意算法的寫入。
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
/**
* Created by Amin on 2016/12/1.
*/
public class ImageDragView extends View {
SheXian sheXian;
private float x1 = 500;
private float y1 = 1000;
private float ZHIDAN = 50;
private Bitmap bitmap;
public final ArrayList<SheXian> balls = new ArrayList<SheXian>(); float time2 = 0;
public ImageDragView(Context context) {
super(context);
iniData();
}
public ImageDragView(Context context, AttributeSet attrs) {
super(context, attrs);
iniData();
}
public ImageDragView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
iniData();
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
// 如果觸碰事件不是按下、移動(dòng)事件
if (event.getAction() != MotionEvent.ACTION_DOWN && event.getAction() != MotionEvent.ACTION_MOVE) {
return false;
}
if (Math.abs(x1 - event.getX()) < 100 && Math.abs(y1 - event.getY()) < 100) {
x1 = event.getX();
y1 = event.getY();
// if (balls.size() > 100) {
// balls.clear();
// }
// sheXian.setY1(y1);
// sheXian.setX1(x1);
// balls.add(sheXian);
}
if (Math.abs(getHeight() / 2 - x1) < 150 && Math.abs(getWidth() / 2 - y1) < 150) {
time2 = 0;
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawBitmap(bitmap, x1 - 72, y1 - 72, paint);
DrawLine(canvas, paint, time2, x1, y1);
paint.setColor(Color.BLUE);
canvas.drawCircle(getHeight() / 2, getWidth() / 2, 100, paint);
paint.setColor(Color.RED);
paint.setTextSize(40);
canvas.drawText("補(bǔ)充彈藥", getHeight() / 2 - 70, getWidth() / 2, paint); drawMyLine(canvas, paint);
if (true) {
invalidate();
}
time2 = time2 + 20;
if (time2 >= 7500) {
time2 = 3750;
drawMyLine(canvas, paint);
}
}
private void drawMyLine(Canvas canvas, Paint paint) {
Log.i("mytag", "balls.size()=" + balls.size());
for (int i = 0; i < 50; i++) {
DrawLine(canvas, paint, time2 - 150 * i, x1, y1);
}
}
//
// private float X(int i) {
// Log.i("mytag", "i=" + i);
// if (balls.size() != 0) {
// Log.i("mytag", "balls.get(0).getX1()=" + balls.get(0).getX1());
// return balls.get(balls.size() - 1).getX1();
//
// } else {
// Log.i("mytag", "x1=" + x1);
// return x1;
// }
//
// }
//
// private float Y(int i) {
//
// if (balls.size() != 0) {
// Log.i("mytag", "balls.get(0).getX1()=" + balls.get(0).getY1());
// return balls.get(balls.size() - 1).getY1();
// } else {
// return y1;
// }
// }
private void DrawLine(Canvas canvas, Paint paint, float time, float x2, float y2) {
if (time > 0) {
canvas.drawLine(x2 + X3(time * (float) Math.sqrt(2) / 2), y2 + Y3(time * (float) Math.sqrt(2) / 2),
x2 + X3(time * (float) Math.sqrt(2) / 2 + ZHIDAN), y2 + Y3(time * (float) Math.sqrt(2) / 2 + ZHIDAN), paint);
canvas.drawLine(x2 - time * (float) Math.sqrt(2) / 2, y2 - time * (float) Math.sqrt(2) / 2,
x2 - time * (float) Math.sqrt(2) / 2 - ZHIDAN, y2 - time * (float) Math.sqrt(2) / 2 - ZHIDAN, paint);
canvas.drawLine(x2 + time * (float) Math.sqrt(2) / 2, y2 - time * (float) Math.sqrt(2) / 2,
x2 + time * (float) Math.sqrt(2) / 2 + ZHIDAN, y2 - time * (float) Math.sqrt(2) / 2 - ZHIDAN, paint);
canvas.drawLine(x2 - X3(time * (float) Math.sqrt(2) / 2), y2 + Y3(time * (float) Math.sqrt(2) / 2),
x2 - X3(time * (float) Math.sqrt(2) / 2 - ZHIDAN), y2 + Y3(time * (float) Math.sqrt(2) / 2 - ZHIDAN), paint);
canvas.drawLine(x2 - time, y2, x2 - time - ZHIDAN, y2, paint);
canvas.drawLine(x2, y2 - time, x2, y2 - time - ZHIDAN, paint);
canvas.drawLine(x2 + time, y2, x2 + time + ZHIDAN, y2, paint);
// canvas.drawLine(x1, y1 + time, x1, y1 + time + 70, paint);
}
}
private float X3(float x) {
if (x < 300) {
return x;
} else {
return 300;
}
}
private float Y3(float y) {
if (y < 300) {
return y;
} else {
return -y + 620;
}
}
private void iniData() {
Resources res = getResources();
bitmap = BitmapFactory.decodeResource(res, R.drawable.feiji2);
sheXian = new SheXian();
}
}
代碼中我添加的監(jiān)聽事件是onTouchEvent,并且判斷了手勢(shì)的滑動(dòng),排除了非按下跟移動(dòng)事件。為了實(shí)現(xiàn)拖動(dòng)效果,在拖動(dòng)的范圍必須在飛機(jī)的正負(fù)100PX之內(nèi)。當(dāng)X1 Y1發(fā)生改變時(shí),界面也開始重繪。因?yàn)槲以诶L制方法內(nèi)寫入一個(gè)死循環(huán),不停地刷新界面繪制調(diào)用invalidate()方法。而子彈的射出,是循環(huán)繪制的50發(fā)子彈,并寫入算法,不停的改變x y 的坐標(biāo),成規(guī)律型增長(zhǎng)。就實(shí)現(xiàn)了移動(dòng)效果。在iniData()方法中,獲取資源圖片,就是飛機(jī)的圖片格式轉(zhuǎn)換為bitmap位圖。另外就是我直接手機(jī)測(cè)試完成,分辨率為1080*1920的,在代碼中并沒有進(jìn)行分辨率的適配,直接用的PX像素單位,不同的手機(jī)運(yùn)行起來肯定有差異了,注意更改適配。
完全使用刷新重繪完成的View,所以動(dòng)畫的效果更改不是那么完善,后面我會(huì)用ObjectAnimator來介紹自定義View跟自定義的屬性動(dòng)畫所結(jié)合,更靈活的實(shí)現(xiàn)子彈獨(dú)立化的移動(dòng)。本文就介紹到這里,不懂的地方和不足之處請(qǐng)留言,謝謝支持。