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; } }
看一下效果:
基本满足需求
三、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是自己起一个线程更新界面。
以上就是本文的全部内容,希望大家喜欢。