本文主要介紹自定義View繪圖的使用把还,上面是一個(gè)可拖動(dòng)的飛機(jī)实蓬,并且不斷發(fā)射出子彈,完全使用自定義View繪圖實(shí)現(xiàn)的動(dòng)態(tài)效果吊履,下面是一組動(dòng)態(tài)效果安皱。
下面我先介紹一下View中比較重要的幾個(gè)方法跟參數(shù):
- onFinishlnflate(): 這是一個(gè)回調(diào)方法,當(dāng)應(yīng)用從XML布局文件加載該組件并利用它來(lái)構(gòu)造界面后艇炎,回調(diào)該方法酌伊。
- onMeasure(int,int):調(diào)用改方法來(lái)檢測(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):松開(kāi)某個(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):包含該組件的窗口的可見(jiàn)性發(fā)生改變時(shí)觸發(fā)。
下面是本文的重點(diǎn)狠轻,繪制圖形所涉及的重要幾何圖形繪制方法以及實(shí)例:
Paint p = new Paint();
p.setColor(Color.RED);// 設(shè)置紅色
canvas.drawText("畫(huà)圓:", 10, 20, p);// 畫(huà)文本
canvas.drawCircle(60, 20, 10, p);// 小圓
p.setAntiAlias(true);// 設(shè)置畫(huà)筆的鋸齒效果奸例。
canvas.drawCircle(120, 20, 20, p);// 大圓
canvas.drawText("畫(huà)線及弧線:", 10, 60, p);
p.setColor(Color.GREEN);// 設(shè)置綠色
canvas.drawLine(60, 40, 100, 40, p);// 畫(huà)線
canvas.drawLine(110, 40, 190, 80, p);// 斜線
//畫(huà)笑臉弧線
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("畫(huà)矩形:", 10, 80, p);
p.setColor(Color.GRAY);// 設(shè)置灰色
p.setStyle(Paint.Style.FILL);//設(shè)置填滿(mǎn)
canvas.drawRect(60, 60, 80, 80, p);// 正方形
canvas.drawRect(60, 90, 160, 100, p);// 長(zhǎng)方形
canvas.drawText("畫(huà)扇形和橢圓:", 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);
// 畫(huà)弧查吊,第一個(gè)參數(shù)是RectF:該類(lèi)是第二個(gè)參數(shù)是角度的開(kāi)始,第三個(gè)參數(shù)是多少度湖蜕,第四個(gè)參數(shù)是真的時(shí)候畫(huà)扇形逻卖,是假的時(shí)候畫(huà)弧線
//畫(huà)橢圓,把oval改一下
oval2.set(210,100,250,130);
canvas.drawOval(oval2, p);
canvas.drawText("畫(huà)三角形:", 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);
// 你可以繪制很多任意多邊形昭抒,比如下面畫(huà)六連形
p.reset();//重置
p.setColor(Color.LTGRAY);
p.setStyle(Paint.Style.STROKE);//設(shè)置空心
Path path1=new Path(); //Path類(lèi)是連接路徑
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);
//畫(huà)圓角矩形
p.setStyle(Paint.Style.FILL);//充滿(mǎn)
p.setColor(Color.LTGRAY);
p.setAntiAlias(true);// 設(shè)置畫(huà)筆的鋸齒效果
canvas.drawText("畫(huà)圓角矩形:", 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半徑
//畫(huà)貝塞爾曲線
canvas.drawText("畫(huà)貝塞爾曲線:", 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);//畫(huà)出貝塞爾曲線
//畫(huà)點(diǎn)
p.setStyle(Paint.Style.FILL);
canvas.drawText("畫(huà)點(diǎn):", 10, 390, p);
canvas.drawPoint(60, 390, p);//畫(huà)一個(gè)點(diǎn)
canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//畫(huà)多個(gè)點(diǎn)
//畫(huà)圖片
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上的畫(huà)筆、畫(huà)刷怎静、顏料等等邮弹,Paint類(lèi)常用方法:
- 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類(lèi)包含了一些常見(jiàn)顏色定義
- 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è)置畫(huà)筆的筆觸寬度
學(xué)習(xí)了上面的API知識(shí),我們可以開(kāi)始繪制自己需要的View跟效果了捞高,下面是我模擬飛機(jī)射擊氯材,繪出來(lái)的一個(gè)View。主要還是注意算法的寫(xiě)入硝岗。
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)聽(tīng)事件是onTouchEvent,并且判斷了手勢(shì)的滑動(dòng)型檀,排除了非按下跟移動(dòng)事件冗尤。為了實(shí)現(xiàn)拖動(dòng)效果,在拖動(dòng)的范圍必須在飛機(jī)的正負(fù)100PX之內(nèi)。當(dāng)X1 Y1發(fā)生改變時(shí)裂七,界面也開(kāi)始重繪皆看。因?yàn)槲以诶L制方法內(nèi)寫(xiě)入一個(gè)死循環(huán),不停地刷新界面繪制調(diào)用invalidate()方法背零。而子彈的射出腰吟,是循環(huán)繪制的50發(fā)子彈,并寫(xiě)入算法徙瓶,不停的改變x y 的坐標(biāo)毛雇,成規(guī)律型增長(zhǎng)。就實(shí)現(xiàn)了移動(dòng)效果侦镇。在iniData()方法中灵疮,獲取資源圖片,就是飛機(jī)的圖片格式轉(zhuǎn)換為bitmap位圖壳繁。另外就是我直接手機(jī)測(cè)試完成震捣,分辨率為1080*1920的,在代碼中并沒(méi)有進(jìn)行分辨率的適配氮趋,直接用的PX像素單位伍派,不同的手機(jī)運(yùn)行起來(lái)肯定有差異了,注意更改適配剩胁。
完全使用刷新重繪完成的View,所以動(dòng)畫(huà)的效果更改不是那么完善诉植,后面我會(huì)用ObjectAnimator來(lái)介紹自定義View跟自定義的屬性動(dòng)畫(huà)所結(jié)合,更靈活的實(shí)現(xiàn)子彈獨(dú)立化的移動(dòng)昵观。本文就介紹到這里晾腔,不懂的地方和不足之處請(qǐng)留言,謝謝支持啊犬。