您的位置:首页 > 其它

SurfaceView和view分别实现触摸轨迹

2015-09-26 17:10 477 查看

一、引言

想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需求。一般就来讲就两种实现方式,view或者surfaceview。下面看看两种是如何实现的。

二、实现原理

先简单说一下实现原理:

(1)用一张白色的Bitmap作为画板

(2)用canvas在bitmap上画线

(3)为了画出平滑的曲线,要用canvas的drawPath(Path,Paint)方法。

(4)同时使用贝塞尔曲线来使曲线更加平滑

三、View实现

直接贴代码了:

package picturegame.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.winton.picturegame.R;

/** 
* @ClassName: GameView 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author Winton winton_by@126.com 
* @date 2015年9月26日 上午8:54:37 
*  
*/
public class GameView extends View{
	
	private Paint paint = null; //
	
	private Bitmap originalBitmap = null;//原始图
	
	private Bitmap new1Bitmap = null;
	
	private Bitmap new2Bitmap = null;
	
	private float clickX =0;
	
	private float clickY=0;
	
	private float startX=0;
	
	private float startY=0;
	
	private boolean isMove = true;
	
	private boolean isClear = false;
	
	private int color =Color.RED;//默认画笔颜色
	
	private float strokeWidth =20f;//默认画笔宽度
	
	Path mPath;
	
	

	public GameView(Context context) {
		this(context,null);
		// TODO Auto-generated constructor stub
	}
	public GameView(Context context,AttributeSet atts) {
		this(context,atts,0);
		// TODO Auto-generated constructor stub
	}
	@SuppressWarnings("static-access")
	public GameView(Context context,AttributeSet atts,int defStyle) {
		super(context,atts,defStyle);
		// TODO Auto-generated constructor stub
		
		originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_pic).copy(Bitmap.Config.ARGB_8888, true);//白色的画板
		new1Bitmap=originalBitmap.createBitmap(originalBitmap);
		mPath=new Path();
	}

	//清楚
	@SuppressWarnings("static-access")
	public void clear(){
		isClear =true;
		new2Bitmap=originalBitmap.createBitmap(originalBitmap);
		invalidate();//重置
	}
	
	public void setStrokeWidth(float width){
		this.strokeWidth=width;
		initPaint();
	}
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		canvas.drawBitmap(writer(new1Bitmap),0,0, null);
		
	}
	
	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		
		
		clickX =event.getX();
		
		clickY=event.getY();
		
		if(event.getAction()==MotionEvent.ACTION_DOWN){
			//手指点下屏幕时触发
			
			startX=clickX;
			startY=clickY;
			mPath.reset();
			mPath.moveTo(clickX, clickY);
			
//			isMove =false;
//			invalidate();
//			return true;
		}
		else if(event.getAction()==MotionEvent.ACTION_MOVE){
			//手指移动时触发
			float dx=Math.abs(clickX-startX);
			float dy=Math.abs(clickY-startY);
//			if(dx>=3||dy>=3){
				//设置贝塞尔曲线的操作点为起点和终点的一半
	            float cX = (clickX + startX) / 2;
	            float cY = (clickY + startY) / 2;
	            mPath.quadTo(startX,startY, cX, cY);
	            
	            startX=clickX;
	            startY=clickY;
				
//			}
//			isMove =true;
//			invalidate();
//			return true;
		}
		
		
		invalidate();
		return true;
	}
	

	/** 
	* @Title: writer 
	* @Description: TODO(这里用一句话描述这个方法的作用) 
	* @param @param pic
	* @param @return    设定文件 
	* @return Bitmap    返回类型 
	* @throws 
	*/
	public Bitmap writer(Bitmap pic){
		initPaint();
		
		Canvas canvas =null;
		if(isClear){
			canvas=new Canvas(new2Bitmap);
		}else{
			canvas=new Canvas(pic);
		}
		
			//canvas.drawLine(startX, startY, clickX, clickY, paint);//画线
			canvas.drawPath(mPath, paint);
		if(isClear){
			return new2Bitmap;
		}
		return pic;
	}
	
	private void initPaint(){
		
		paint = new Paint();//初始化画笔
		
		paint.setStyle(Style.STROKE);//设置为画线
		
		paint.setAntiAlias(true);//设置画笔抗锯齿
		
		paint.setColor(color);//设置画笔颜色
		
		paint.setStrokeWidth(strokeWidth);//设置画笔宽度
	}
	
	
	/** 
	* @Title: setColor 
	* @Description: TODO(设置画笔颜色) 
	* @param @param color    设定文件 
	* @return void    返回类型 
	* @throws 
	*/
	public void setColor(int color){
		
		this.color=color;
		initPaint();
	}
	
	public Bitmap getPaint(){
		return new1Bitmap;
	}
}
看一下效果:



基本满足需求

三、surfaceView实现

package picturegame.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

import com.winton.picturegame.R;

public class GameViewSurface extends SurfaceView implements Callback,Runnable{
	
	
	/** 控制游戏更新循环 **/  
    boolean mRunning = false; 
    
    /**控制游戏循环**/  
    boolean mIsRunning = false; 
    
    /**每50帧刷新一次屏幕**/    
    public static final int TIME_IN_FRAME = 50; 

	private int paintColor=android.graphics.Color.WHITE;//默认画笔颜色为黑色
	
	private float paintWidth=2f;//默认画笔宽度
	
	private Style paintStyle=Style.STROKE;//默认画笔风格
	
	private int paintAlph=255;//默认不透明
	
	private Path mPath;//轨迹
	
	private Paint mPaint;//画笔
	
	private float startX=0.0f;//初始x
	
	private float startY=0.0f;//初始Y
	
	private SurfaceHolder surfaceHolder;
	
	public Canvas mCanvas;
	
	public boolean first=true;
	
	Bitmap bg;
	
	public GameViewSurface(Context context){
		this(context,null);
	}
	public GameViewSurface(Context context,AttributeSet attrs){
		this(context,attrs,0);
	}
	public GameViewSurface(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		this.setFocusable(true);//设置当前view拥有触摸事件
		
		surfaceHolder=getHolder();
		surfaceHolder.addCallback(this);
		
		mPath=new Path();
		initPaint();
		
		bg = BitmapFactory.decodeResource(getResources(), R.drawable.default_pic).copy(Bitmap.Config.ARGB_8888, true);//白色的画板
	}
	/** 
	* @Title: initPaint 
	* @Description: TODO(初始化画笔) 
	* @param     设定文件 
	* @return void    返回类型 
	* @throws 
	*/
	private void initPaint(){
		mPaint=new Paint();
		mPaint.setAntiAlias(true);//消除锯齿
		mPaint.setColor(paintColor);//画笔颜色
		mPaint.setAlpha(paintAlph);//画笔透明度
		mPaint.setStyle(paintStyle);//设置画笔风格
		mPaint.setStrokeWidth(paintWidth);//设置画笔宽度
	}
	
	public void doDraw(){
		mCanvas=surfaceHolder.lockCanvas();
		mCanvas.drawPath(mPath, mPaint);//绘制 
		surfaceHolder.unlockCanvasAndPost(mCanvas);
	}	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			//手接触屏幕时触发
			doTouchDown(event);
			break;
		case MotionEvent.ACTION_MOVE:
			//手滑动时触发
			doTouchMove(event);
			break;
			
		case MotionEvent.ACTION_UP:
			//手抬起时触发
			
			break;
			

		default:
			break;
		}
		return true;
	}
	
	/** 
	* @Title: doTouchDown 
	* @Description: TODO(手触摸到屏幕时需要做的事情) 
	* @param @param event    设定文件 
	* @return void    返回类型 
	* @throws 
	*/
	private void doTouchDown(MotionEvent event){
		
		float touchX=event.getX();
		float touchY=event.getY();
		startX=touchX;
		startY=touchY;
		mPath.reset();
		mPath.moveTo(touchX, touchY);
	}
	
	/** 
	* @Title: doTouchMove 
	* @Description: TODO(手在屏幕上滑动时要做的事情) 
	* @param @param event    设定文件 
	* @return void    返回类型 
	* @throws 
	*/
	private void doTouchMove(MotionEvent event){
		
		float touchX=event.getX();
		float touchY=event.getY();
		
		float dx=Math.abs(touchX-startX);//移动的距离
		
		float dy =Math.abs(touchY-startX);//移动的距离
		
		if(dx>3||dy>3){
			float cX=(touchX+startX)/2;
			float cY=(touchY+startY)/2;
			mPath.quadTo(startX, startY, cX, cY);
			
			startX=touchX;
			startY=touchY;
			
		}
		
	}
	
	public void setPaintColor(int paintColor) {
		this.paintColor = paintColor;
		initPaint();
	}
	public void setPaintWidth(float paintWidth) {
		this.paintWidth = paintWidth;
		initPaint();
	}
	public void setPaintStyle(Style paintStyle) {
		this.paintStyle = paintStyle;
		initPaint();
	}
	public void setPaintAlph(int paintAlph) {
		this.paintAlph = paintAlph;
		initPaint();
	}
	
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (mIsRunning) {  
			 
	        /** 取得更新游戏之前的时间 **/  
	        long startTime = System.currentTimeMillis();  
	        /** 在这里加上线程安全锁 **/  
	        synchronized(surfaceHolder){
	        	doDraw();  
	        }
	        
	        /** 取得更新游戏结束的时间 **/  
	        long endTime = System.currentTimeMillis();  
	 
	        /** 计算出游戏一次更新的毫秒数 **/  
	        int diffTime = (int) (endTime - startTime);  
	 
	        /** 确保每次更新时间为50帧 **/  
	        while (diffTime <= TIME_IN_FRAME) {  
	            diffTime = (int) (System.currentTimeMillis() - startTime);  
	            /** 线程等待 **/  
	            Thread.yield();  
	        }  
	 
	       }  
		
	}
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// TODO Auto-generated method stub
		mCanvas =surfaceHolder.lockCanvas();
		mCanvas.drawBitmap(bg, 0,0, null);
		surfaceHolder.unlockCanvasAndPost(mCanvas);
		
		mIsRunning=true;
        new Thread(this).start(); 
	}
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO Auto-generated method stub
		 mIsRunning = false;
	}
	
	
}
看看运行效果:



当我不设置背景时是没问题的,但使用了背景就不停的闪烁了,不知道有没同学知道的,可以说一下。

五、总结

两种方式都是可以实现的,而且仔细对比发现surfaceview响应的速度比view快很多,view想必与surfaceview更容易实现。

view用于显示被动更新的动画,即需要操作才会更新的动画,而surfaceview则用于主动更新的动画,如在界面上显示一个奔跑的小狗。

view更新界面是在UI主线程。surfaceview是自己起一个线程更新界面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: