当前位置: 移动技术网 > IT编程>移动开发>Android > Android中SurfaceView和view画出触摸轨迹

Android中SurfaceView和view画出触摸轨迹

2019年07月24日  | 移动技术网IT编程  | 我要评论

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

看看运行效果:

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

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

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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网