在上一章的学习中我们已经知道如何处理游戏中的触摸事件,这一章将向同学们介绍绘制游戏触摸轨迹的曲线图,在onTouchEvent方法中我们可以拿到手指在屏幕中触摸点 X Y时时的坐标,这章我们研究的课题就是如何把这些点变成一种无规则轨迹并且将这条无规则曲线显示在屏幕中。
Android提供了一个Path类 , 顾名思义这个类可以设置曲线路径轨迹。任何无规则的曲线实际上都是由若干条线段组成,而线段的定义为两点之间最短的一条线。path类就 可以记录这两点之间的轨迹,那么若干个Path 就是我们须要绘制的无规则曲线。
下面介绍一下API 中path类设置轨迹路径的方法
public class
Path
extends Object
java.lang.Object
android.graphics.Path
quadTo(float x1, float y1, float x2, float y2)
Add a quadratic bezier from the last point, approaching control point (x1,y1), and ending at (x2,y2).
解释:
参数1 轨迹起始点X坐标
参数2 轨迹起始点Y坐标
参数3 轨迹结束点X坐标
参数4 轨迹结束点Y坐标
所以根据这个参数就可以设置一条线段轨迹。
同学们,我们先看一张效果图。 为了设置一条比较圆滑好看的曲线我们需要对游戏画笔进行一些设置。注释已经在代码中写的很清楚了,在这里我详细说一下 设置画笔风格 mPaint.setStyle(Paint.Style.STROKE); 意思是设置画笔的风格 android 画笔一共提供了三种风格Paint.Style.STROKE 、Paint.Style.FILL、Paint.Style.FILL_AND_STROKE 意思分别为 空心 、实心、实心与空心 。如果不设置的话默认为 Paint.Style.FILL,在这里必需设置成空心 因为如果一旦设置成实心或者实心与空心那么画笔会把path路径中间包住这样就不是曲线线段了,所以同学们注意一下这里。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** 创建曲线画笔 **/ mPaint = new Paint(); mPaint.setColor(Color.BLACK); /**设置画笔抗锯齿**/ mPaint.setAntiAlias(true); /**画笔的类型**/ mPaint.setStyle(Paint.Style.STROKE); /**设置画笔变为圆滑状**/ mPaint.setStrokeCap(Paint.Cap.ROUND); /**设置线的宽度**/ mPaint.setStrokeWidth(5); |
在触摸按下事件中 通过moveTo() 方法设置触摸屏幕点为轨迹的起始点,这样在触摸移动事件中设置曲线的轨迹 起始点为上次触摸点 结束点为本次触摸点。使用quadTo方法记录每次移动产生的一个曲线线段 然后将所有的曲线线段绘制在屏幕中,如果触摸抬起将调用reset()方法重置曲线轨迹。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
@Override public boolean onTouchEvent(MotionEvent event) { /** 拿到触摸的状态 **/ int action = event.getAction(); float x = event.getX(); float y = event.getY(); switch (action) { // 触摸按下的事件 case MotionEvent.ACTION_DOWN: /**设置曲线轨迹起点 X Y坐标**/ mPath.moveTo(x, y); break; // 触摸移动的事件 case MotionEvent.ACTION_MOVE: /**设置曲线轨迹**/ //参数1 起始点X坐标 //参数2 起始点Y坐标 //参数3 结束点X坐标 //参数4 结束点Y坐标 mPath.quadTo(mposX, mposY, x, y); break; // 触摸抬起的事件 case MotionEvent.ACTION_UP: /**按键抬起后清空路径轨迹**/ mPath.reset(); break; } //记录当前触摸X Y坐标 mposX = x; mposY = y; return true; } |
游戏绘制中调用drawPath方法将onTouchEvent中记录的路径曲线绘制在屏幕当中。
1 2 3 4 5 6 7 8 9 10 11 12 |
private void Draw() { /**清空画布**/ mCanvas.drawColor(Color.WHITE); /**绘制曲线**/ mCanvas.drawPath(mPath, mPaint); /**记录当前触点位置**/ mCanvas.drawText("当前触笔 X:" + mposX, 0, 20,mTextPaint); mCanvas.drawText("当前触笔 Y:" + mposY, 0, 40,mTextPaint); } |
给出整体代码的实现
详细的注释已经在代码中写出 欢迎大家阅读喔 哇咔咔~~~~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.os.Bundle; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.Window; import android.view.WindowManager; import android.view.SurfaceHolder.Callback; public class SurfaceViewAcitvity extends Activity { MyView mAnimView = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 全屏显示窗口 requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 显示自定义的游戏View mAnimView = new MyView(this); setContentView(mAnimView); } public class MyView extends SurfaceView implements Callback,Runnable { /**每50帧刷新一次屏幕**/ public static final int TIME_IN_FRAME = 50; /** 游戏画笔 **/ Paint mPaint = null; Paint mTextPaint = null; SurfaceHolder mSurfaceHolder = null; /** 控制游戏更新循环 **/ boolean mRunning = false; /** 游戏画布 **/ Canvas mCanvas = null; /**控制游戏循环**/ boolean mIsRunning = false; /**曲线方向**/ private Path mPath; private float mposX, mposY; public MyView(Context context) { super(context); /** 设置当前View拥有控制焦点 **/ this.setFocusable(true); /** 设置当前View拥有触摸事件 **/ this.setFocusableInTouchMode(true); /** 拿到SurfaceHolder对象 **/ mSurfaceHolder = this.getHolder(); /** 将mSurfaceHolder添加到Callback回调函数中 **/ mSurfaceHolder.addCallback(this); /** 创建画布 **/ mCanvas = new Canvas(); /** 创建曲线画笔 **/ mPaint = new Paint(); mPaint.setColor(Color.BLACK); /**设置画笔抗锯齿**/ mPaint.setAntiAlias(true); /**画笔的类型**/ mPaint.setStyle(Paint.Style.STROKE); /**设置画笔变为圆滑状**/ mPaint.setStrokeCap(Paint.Cap.ROUND); /**设置线的宽度**/ mPaint.setStrokeWidth(5); /**创建路径对象**/ mPath = new Path(); /** 创建文字画笔 **/ mTextPaint = new Paint(); /**设置颜色**/ mTextPaint.setColor(Color.BLACK); /**设置文字大小**/ mTextPaint.setTextSize(15); } @Override public boolean onTouchEvent(MotionEvent event) { /** 拿到触摸的状态 **/ int action = event.getAction(); float x = event.getX(); float y = event.getY(); switch (action) { // 触摸按下的事件 case MotionEvent.ACTION_DOWN: /**设置曲线轨迹起点 X Y坐标**/ mPath.moveTo(x, y); break; // 触摸移动的事件 case MotionEvent.ACTION_MOVE: /**设置曲线轨迹**/ //参数1 起始点X坐标 //参数2 起始点Y坐标 //参数3 结束点X坐标 //参数4 结束点Y坐标 mPath.quadTo(mposX, mposY, x, y); break; // 触摸抬起的事件 case MotionEvent.ACTION_UP: /**按键抬起后清空路径轨迹**/ mPath.reset(); break; } //记录当前触摸X Y坐标 mposX = x; mposY = y; return true; } private void Draw() { /**清空画布**/ mCanvas.drawColor(Color.WHITE); /**绘制曲线**/ mCanvas.drawPath(mPath, mPaint); /**记录当前触点位置**/ mCanvas.drawText("当前触笔 X:" + mposX, 0, 20,mTextPaint); mCanvas.drawText("当前触笔 Y:" + mposY, 0, 40,mTextPaint); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { /**开始游戏主循环线程**/ mIsRunning = true; new Thread(this).start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { mIsRunning = false; } @Override public void run() { while (mIsRunning) { /** 取得更新游戏之前的时间 **/ long startTime = System.currentTimeMillis(); /** 在这里加上线程安全锁 **/ synchronized (mSurfaceHolder) { /** 拿到当前画布 然后锁定 **/ mCanvas = mSurfaceHolder.lockCanvas(); Draw(); /** 绘制结束后解锁显示在屏幕上 **/ mSurfaceHolder.unlockCanvasAndPost(mCanvas); } /** 取得更新游戏结束的时间 **/ long endTime = System.currentTimeMillis(); /** 计算出游戏一次更新的毫秒数 **/ int diffTime = (int) (endTime - startTime); /** 确保每次更新时间为50帧 **/ while (diffTime <= TIME_IN_FRAME) { diffTime = (int) (System.currentTimeMillis() - startTime); /** 线程等待 **/ Thread.yield(); } } } } } |
总体来说这章内容还是比较简单的,老规矩每篇文章都会附带源代码,最后如果你还是觉得我写的不够详细 看的不够爽 不要紧我把源代码的下载地址贴出来 欢迎大家一起讨论学习雨松MOMO希望可以和大家一起进步。
下载地址:http://vdisk.weibo.com/s/aajeJ
- 本文固定链接: https://www.xuanyusong.com/archives/329
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
我想知道 case MotionEvent.ACTION_MOVE:里的功能如何用重力感应实现根据屏幕方向自动绘制曲线
“我们爱代码、爱加班、爱Hello World!” 刚刚注意到,雨松还有这种嗜好啊。
是啊 蛤蛤
SurfaceViewAcitvity 写错了,应该是 SurfaceViewActivity
细心。。。。
好的,谢谢
加油~
弱弱的问一下,如果两只手同时触摸屏幕,屏幕是否会同时识别?