美文网首页Android开发那些事移动开发进阶Android
自定义View全解,绘一个动态“飞机大战”

自定义View全解,绘一个动态“飞机大战”

作者: 番茄And鸡蛋 | 来源:发表于2016-12-08 15:22 被阅读697次
    feiji.png
    本文主要介绍自定义View绘图的使用,上面是一个可拖动的飞机,并且不断发射出子弹,完全使用自定义View绘图实现的动态效果,下面是一组动态效果。
    飞机大战.gif
    下面我先介绍一下View中比较重要的几个方法跟参数:
    • onFinishlnflate(): 这是一个回调方法,当应用从XML布局文件加载该组件并利用它来构造界面后,回调该方法。
    • onMeasure(int,int):调用改方法来检测View组件以及所包含的所有子组件的大小。
    • omLayout(boolean,int,int,int,int):当该组件需要分配其子组件的位置,大小时回调。
    • onSizeChanged(int,int,int,int):组件的大小发生改变的时候回调。
    • onDraw(Canvas):组件绘制内容的时候调用该方法进行绘制。
    • onKeyDown(int,KeyEvent):当某个键被按下时触发。
    • onKeyUp(int,KeyEvent):松开某个键时触发。
    • onTrackballEvent(MotionEvent):发生轨迹球事件时触发。
    • onTouchEvent(MotionEvent):发生触摸屏事件时触发。
    • onFocusChanged(boolean gainFocus,int direction,Rect previouslyFocusedRect):该组件焦点发生改变时触发。
    • onWindowFocusChanged(boolean):包含改组件的窗口失去或者得到焦点时触发。
    • onAttachedToWindow():把该组件放入某个窗口时触发。
    • onDetachedFromWindow():把该组件从某个窗口上分离时触发。
    • onWindowVisibilityChanged(int):包含该组件的窗口的可见性发生改变时触发。
    下面是本文的重点,绘制图形所涉及的重要几何图形绘制方法以及实例:
    1.     Paint p = new Paint();  
      
    2.     p.setColor(Color.RED);// 设置红色  
      
    3.     canvas.drawText("画圆:", 10, 20, p);// 画文本  
      
    4.     canvas.drawCircle(60, 20, 10, p);// 小圆  
      
    5.     p.setAntiAlias(true);// 设置画笔的锯齿效果。
      
    6.     canvas.drawCircle(120, 20, 20, p);// 大圆  
      
    7.     canvas.drawText("画线及弧线:", 10, 60, p);  
      
    8.     p.setColor(Color.GREEN);// 设置绿色  
      
    9.     canvas.drawLine(60, 40, 100, 40, p);// 画线  
      
    10.     canvas.drawLine(110, 40, 190, 80, p);// 斜线  
      
    11.     //画笑脸弧线  
      
    12.     p.setStyle(Paint.Style.STROKE);//设置空心  
      
    13.     RectF oval1=new RectF(150,20,180,40);  
      
    14.     canvas.drawArc(oval1, 180, 180, false, p);//小弧形  
      
    15.     oval1.set(190, 20, 220, 40);  
      
    16.     canvas.drawArc(oval1, 180, 180, false, p);//小弧形  
      
    17.     oval1.set(160, 30, 210, 60);  
      
    18.     canvas.drawArc(oval1, 0, 180, false, p);//小弧形   
      
    19.     canvas.drawText("画矩形:", 10, 80, p);  
      
    20.     p.setColor(Color.GRAY);// 设置灰色  
      
    21.     p.setStyle(Paint.Style.FILL);//设置填满  
      
    22.     canvas.drawRect(60, 60, 80, 80, p);// 正方形  
      
    23.     canvas.drawRect(60, 90, 160, 100, p);// 长方形  
      
    24.     canvas.drawText("画扇形和椭圆:", 10, 120, p);  
      
    25.     /* 设置渐变色 这个正方形的颜色是改变的 */  
      
    26.     Shader mShader = new LinearGradient(0, 0, 100, 100,  
      
    27.             new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW,  
      
    28.                     Color.LTGRAY }, null, Shader.TileMode.REPEAT); // 一个材质,打造出一个线性梯度沿著一条线。  
      
    29.     p.setShader(mShader);  
      
    30.     // p.setColor(Color.BLUE);  
      
    31.     RectF oval2 = new RectF(60, 100, 200, 240);// 设置个新的长方形,扫描测量  
      
    32.     canvas.drawArc(oval2, 200, 130, true, p);  
      
    33.     // 画弧,第一个参数是RectF:该类是第二个参数是角度的开始,第三个参数是多少度,第四个参数是真的时候画扇形,是假的时候画弧线  
      
    34.     //画椭圆,把oval改一下  
      
    35.     oval2.set(210,100,250,130);  
      
    36.     canvas.drawOval(oval2, p);  
      
    37.     canvas.drawText("画三角形:", 10, 200, p);  
      
    38.     // 绘制这个三角形,你可以绘制任意多边形  
      
    39.     Path path = new Path();  
      
    40.     path.moveTo(80, 200);// 此点为多边形的起点  
      
    41.     path.lineTo(120, 250);  
      
    42.     path.lineTo(80, 250);  
      
    43.     path.close(); // 使这些点构成封闭的多边形  
      
    44.     canvas.drawPath(path, p);  
      
    45.     // 你可以绘制很多任意多边形,比如下面画六连形  
      
    46.     p.reset();//重置  
      
    47.     p.setColor(Color.LTGRAY);  
      
    48.     p.setStyle(Paint.Style.STROKE);//设置空心  
      
    49.     Path path1=new Path();  //Path类是连接路径
      
    50.     path1.moveTo(180, 200);  
      
    51.     path1.lineTo(200, 200);  
      
    52.     path1.lineTo(210, 210);  
      
    53.     path1.lineTo(200, 220);  
      
    54.     path1.lineTo(180, 220);  
      
    55.     path1.lineTo(170, 210);  
      
    56.     path1.close();//封闭  
      
    57.     canvas.drawPath(path1, p);  
      
    58.     //画圆角矩形  
      
    59.     p.setStyle(Paint.Style.FILL);//充满  
      
    60.     p.setColor(Color.LTGRAY);  
      
    61.     p.setAntiAlias(true);// 设置画笔的锯齿效果  
      
    62.     canvas.drawText("画圆角矩形:", 10, 260, p);  
      
    63.     RectF oval3 = new RectF(80, 260, 200, 300);// 设置个新的长方形  
      
    64.     canvas.drawRoundRect(oval3, 20, 15, p);//第二个参数是x半径,第三个参数是y半径    
      
    65.     //画贝塞尔曲线  
      
    66.     canvas.drawText("画贝塞尔曲线:", 10, 310, p);  
      
    67.     p.reset();  
      
    68.     p.setStyle(Paint.Style.STROKE);  
      
    69.     p.setColor(Color.GREEN);  
      
    70.     Path path2=new Path();  
      
    71.     path2.moveTo(100, 320);//设置Path的起点  
      
    72.     path2.quadTo(150, 310, 170, 400); //设置贝塞尔曲线的控制点坐标和终点坐标  
      
    73.     canvas.drawPath(path2, p);//画出贝塞尔曲线    
      
    74.     //画点  
      
    75.     p.setStyle(Paint.Style.FILL);  
      
    76.     canvas.drawText("画点:", 10, 390, p);  
      
    77.     canvas.drawPoint(60, 390, p);//画一个点  
      
    78.     canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//画多个点   
      
    79.     //画图片
      
    80.     Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);  
      
    81.     canvas.drawBitmap(bitmap, 250,360, p);  
      
    除了以上绘制的方法,Canvas还提供了如下方法进行坐标变换:
    • rotate(float degrees,float px,float py):对Canvas执行旋转变换。
    • scale(float sx,float sy,float px,float py):对Canvas执行缩放变换。
    • skew(float sx,float sy):对Canvas执行倾斜变换。
    • trnslate(float dx,float dy):移动Canvas.向右移动dx距离(为负数相反方向移动),向下移动dy距离(为负数相反方向移动)。
    Paint 代表了Canvas上的画笔、画刷、颜料等等,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) // 设置下划线
    • setStyle(Paint.Style style) //设置填充的风格
    • setStrokeLayer(float radius,float dx,float dy,int color) //设置阴影。
    • setStrokeWidth(float width) //设置画笔的笔触宽度
    学习了上面的API知识,我们可以开始绘制自己需要的View跟效果了,下面是我模拟飞机射击,绘出来的一个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
            // 如果触碰事件不是按下、移动事件
            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("补充弹药", 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(); 
       }
    }
    

    代码中我添加的监听事件是onTouchEvent,并且判断了手势的滑动,排除了非按下跟移动事件。为了实现拖动效果,在拖动的范围必须在飞机的正负100PX之内。当X1 Y1发生改变时,界面也开始重绘。因为我在绘制方法内写入一个死循环,不停地刷新界面绘制调用invalidate()方法。而子弹的射出,是循环绘制的50发子弹,并写入算法,不停的改变x y 的坐标,成规律型增长。就实现了移动效果。在iniData()方法中,获取资源图片,就是飞机的图片格式转换为bitmap位图。另外就是我直接手机测试完成,分辨率为1080*1920的,在代码中并没有进行分辨率的适配,直接用的PX像素单位,不同的手机运行起来肯定有差异了,注意更改适配。
    完全使用刷新重绘完成的View,所以动画的效果更改不是那么完善,后面我会用ObjectAnimator来介绍自定义View跟自定义的属性动画所结合,更灵活的实现子弹独立化的移动。本文就介绍到这里,不懂的地方和不足之处请留言,谢谢支持。

    相关文章

      网友评论

      本文标题:自定义View全解,绘一个动态“飞机大战”

      本文链接:https://www.haomeiwen.com/subject/ivajmttx.html