Android中SurfaceView和view画出触摸轨迹

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

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

(0)

相关推荐

  • Android实现手势滑动多点触摸放大缩小图片效果

    网上文章虽多,但是这种效果少之又少,我真诚的献上以供大家参考 实现原理:自定义ImageView对此控件进行相应的layout(动态布局). 这里你要明白几个方法执行的流程: 首先ImageView是继承自View的子类. onLayout方法:是一个回调方法.该方法会在在View中的layout方法中执行,在执行layout方法前面会首先执行setFrame方法. setFrame方法:判断我们的View是否发生变化,如果发生变化,那么将最新的l,t,r,b传递给View,然后刷新进行动态更新

  • android 多点触摸图片缩放的具体实现方法

    布局: 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/relativeLayout1"    android:layout_width="fill_parent

  • android中处理各种触摸事件的方法浅谈

    Android里有两个类android.view.GestureDetectorandroid.view.GestureDetector.SimpleOnGestureListener(另外android.widget.Gallery好像是更牛x的OnGestureListener )1)新建一个类继承SimpleOnGestureListener,HahaGestureDetectorListener可以实现以下event事件.boolean onDoubleTap(MotionEvent e

  • 解析Android开发中多点触摸的实现方法

    多点触摸技术在实际开发过程中,用的最多的就是放大缩小功能.比如有一些图片浏览器,就可以用多个手指在屏幕上操作,对图片进行放大或者缩小.再比如一些浏览器,也可以通过多点触摸放大或者缩小字体.其实放大缩小也只是多点触摸的实际应用样例之一,有了多点触摸技术,在一定程度上就可以创新出更多的操作方式来,实现更酷的人机交互. 理论上,Android系统本身可以处理多达256个手指的触摸,这主要取决于手机硬件的支持.当然,支持多点触摸的手机,也不会支持这么多点,一般是支持2个点或者4个点.对于开发者来说,编写

  • Android实现手势滑动多点触摸缩放平移图片效果

    现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位. 一.概述 想要做到图片支持多点触控,自由的进行缩放.平移,需要了解几个知识点:Matrix , GestureDetector , ScaleGestureDetector 以及事件分发机制,ps:不会咋办,不会你懂的. 1.Matrix 矩阵,看深入了都是3维矩阵的乘啊什么的,怪麻烦的~~ 其实这么了解下就行了: Matrix 数据结构:3维矩阵

  • Android触摸事件传递机制初识

    前言 今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套ListView,都是上下滑动,这该如何解决呢,它解决的依据就是View事件的传递机制,所以开发者需要对View的事件传递机制有较深入的理解. 目录 Activity.View.ViewGroup三者关系 触摸事件类型 事件传递三个阶段 View事件传递机制 ViewGroup事件传递机制 小结 Act

  • 解决Android SurfaceView绘制触摸轨迹闪烁问题的方法

    本文分享了解决SurfaceView触摸轨迹闪烁问题的方法,供大家参考,具体内容如下 第一种解决SurfaceView触摸轨迹闪烁问题的方法: 由于SurfaceView使用双缓存机制,两张画布轮流显示到屏幕上.那么,要存储触摸轨迹并避免两张画布内容不一致造成的闪烁问题,完全可以利用保存绘制过程并不断重新绘制的方法解决闪烁,而且这样还顺带解决了多次试验中偶尔出现的因为moveTo()函数不能读取到参数执行默认设置(参数设为上次的触摸点)而出现的断线连接闪烁问题,详细代码如下: package c

  • 简单讲解Android开发中触摸和点击事件的相关编程方法

    在Android上,不止一个途径来侦听用户和应用程序之间交互的事件.对于用户界面里的事件,侦听方法就是从与用户交互的特定视图对象截获这些事件.视图类提供了相应的手段. 在各种用来组建布局的视图类里面,你可能会注意到一些公共的回调方法看起来对用户界面事件有用.这些方法在该对象的相关动作发生时被Android框架调用.比如,当一个视图(如一个按钮)被触摸时,该对象上的onTouchEvent()方法会被调用.不过,为了侦听这个事件,你必须扩展这个类并重写该方法.很明显,扩展每个你想使用的视图对象(只

  • Android应用开发中触摸屏手势识别的实现方法解析

    很多时候,利用触摸屏的Fling.Scroll等Gesture(手势)操作来操作会使得应用程序的用户体验大大提升,比如用Scroll手势在 浏览器中滚屏,用Fling在阅读器中翻页等.在Android系统中,手势的识别是通过 GestureDetector.OnGestureListener接口来实现的,不过William翻遍了Android的官方文档也没有找到一个相 关的例子,API Demo中的TouchPaint也仅仅是提到了onTouch事件的处理,没有涉及到手势. 我们先来明确一些概念

  • Android修改源码解决Alertdialog触摸对话框边缘消失的问题

    研究其父类时候发现,可以设置这么一条属性,在AlertDialog.Builder.create()之后才能调用这两个方法 方法一: setCanceledOnTouchOutside(false);调用这个方法时,按对话框以外的地方不起作用.按返回键还起作用 方法二: setCanceleable(false);调用这个方法时,按对话框以外的地方不起作用.按返回键也不起作用 这两个方法都属于Dialog方法,可查阅源码 修改后的源码如下: 复制代码 代码如下: case 1:         

随机推荐