欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中SurfaceView和view画出触摸轨迹

程序员文章站 2024-02-26 17:10:34
一、引言          想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需...

一、引言
         想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需求。一般就来讲就两种实现方式,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; 
 } 
} 

看一下效果:

Android中SurfaceView和view画出触摸轨迹

基本满足需求
三、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; 
 } 
  
  
} 

看看运行效果:

Android中SurfaceView和view画出触摸轨迹

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

大家可以阅读本文《解决android surfaceview绘制触摸轨迹闪烁问题的方法》,或许对大家的学习有所帮助。

五、总结
两种方式都是可以实现的,而且仔细对比发现surfaceview响应的速度比view快很多,view想必与surfaceview更容易实现。
view用于显示被动更新的动画,即需要操作才会更新的动画,而surfaceview则用于主动更新的动画,如在界面上显示一个奔跑的小狗。
view更新界面是在ui主线程。surfaceview是自己起一个线程更新界面。

以上就是本文的全部内容,希望大家喜欢。