Android编程实现画板功能的方法总结【附源码下载】

本文实例讲述了Android编程实现画板功能的方法。分享给大家供大家参考,具体如下:

Android实现画板主要有2种方式,一种是用自定义View实现,另一种是通过Canvas类实现。当然自定义View内部也是用的Canvas。第一种方式的思路是,创建一个自定义View(推荐SurfaceView),在自定义View里通过Path对象记录手指滑动的路径调用lineTo()绘制;第二种方式的思路是,先用Canvas绘制一张空的Bitmap,通过ImageView的setImageBitmap()方法加载这个Bitmap,然后该ImageView实现onTouch()监听事件,跟踪用户手指的移动调用drawLine()绘制线条。

我们先来看第一种的实现的方式吧。这里就用SurfaceView来实现,在这里介绍一下关于SurfaceView的知识。SurfaceView继承自View,两者都可以实现绘图功能,那么他们有什么不同呢。先说下Android绘制视图的原理,View通过刷新来绘制视图,Android系统则通过发出VSYNC信号进行屏幕绘制,玩游戏的朋友都应该知道"垂直同步",VSYNC就是垂直同步,谷歌是在4.1之后引入VSYNC的,VSYNC是为了不让画面掉帧。为了不掉帧,View的绘制需要在16ms之内完成。如果执行耗时太长或者需要频繁刷新,那么View就不合适了,影响用户体验和性能。用 SurfaceView就好办了,它内部是在子线程进行页面刷新,使用了双缓冲机制。现在我们来使用它吧。

通常用法是创建一个View继承自SurfaceView,并实现Callback和Runnable接口。

public class MySurfaceView extends SurfaceView implements
    SurfaceHolder.Callback, Runnable {
  // SurfaceHolder实例
  private SurfaceHolder mSurfaceHolder;
  // Canvas对象
  private Canvas mCanvas;
  // 控制子线程是否运行
  private boolean startDraw;
  // Path实例
  private Path mPath = new Path();
  // Paint实例
  private Paint mpaint = new Paint();
  public MySurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(); // 初始化
  }
  private void initView() {
    mSurfaceHolder = getHolder();
    mSurfaceHolder.addCallback(this);
    // 设置可获得焦点
    setFocusable(true);
    setFocusableInTouchMode(true);
    // 设置常亮
    this.setKeepScreenOn(true);
  }
  @Override
  public void run() {
    // 如果不停止就一直绘制
    while (startDraw) {
      // 绘制
      draw();
    }
  }
  /*
   * 创建
   */
  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    startDraw = true;
    new Thread(this).start();
  }
  /*
   * 改变
   */
  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width,
      int height) {
  }
  /*
   * 销毁
   */
  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
    startDraw = false;
  }
  private void draw() {
    try {
      mCanvas = mSurfaceHolder.lockCanvas();
      mCanvas.drawColor(Color.WHITE);
      mpaint.setStyle(Paint.Style.STROKE);
      mpaint.setStrokeWidth(DensityUtil.px2dip(getContext(), 30));
      mpaint.setColor(Color.BLACK);
      mCanvas.drawPath(mPath, mpaint);
    } catch (Exception e) {
    } finally {
      // 对画布内容进行提交
      if (mCanvas != null) {
        mSurfaceHolder.unlockCanvasAndPost(mCanvas);
      }
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();  //获取手指移动的x坐标
    int y = (int) event.getY();  //获取手指移动的y坐标
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      mPath.moveTo(x, y);
      break;
    case MotionEvent.ACTION_MOVE:
      mPath.lineTo(x, y);
      break;
    case MotionEvent.ACTION_UP:
      break;
    }
    return true;
  }
  // 重置画布
  public void reset() {
    mPath.reset();
  }
}

我们在构造方法里进行初始化,获得SurfaceHolder实例,添加Callback接口实例,及获得焦点等操作。重写了SurfaceView的三个方法surfaceCreated,surfaceChanged,surfaceDestroyed。在surfaceCreated方法里开启子线程,执行draw方法。在surfaceDestroyed方法里关闭线程。在draw方法里,通过mSurfaceHolder.lockCanvas()获取Canvas对象,设置样式,颜色等,然后重写onTouchEvent方法,监听用户手指移动,调用mPath.lineTo(x, y)绘制线条,最后调用mSurfaceHolder.unlockCanvasAndPost(mCanvas)提交画布内容.这样就完成了画板的绘制。

我在代码里添加了reset()方法,可以重置画布,只需要在MainActivity获取SurfaceView对象,调用SurfaceView.reset()就可以了。

private Button reset_btn;
private MySurfaceView mview;
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  context = this;
  mview = (MySurfaceView) findViewById(R.id.MySurfaceView);
  reset_btn = (Button) findViewById(R.id.reset_btn);
  reset_btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      //清除
      mview.reset();
    }
  });

现在我们看下第二种方式吧,其实原理和第一种差不太多,我就不赘述了。直接贴上代码吧。

public class SecondActivity extends Activity {
  private ImageView img;
  private Bitmap mBitmap;
  private Canvas canvas;
  private Paint paint;
  // 重置按钮
  private Button reset_btn;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    img = (ImageView) findViewById(R.id.img);
    reset_btn = (Button) findViewById(R.id.reset_btn);
    reset_btn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        img.setImageBitmap(null);
        showImage();
      }
    });
    // 绘图
    showImage();
  }
  private void showImage() {
    // 创建一张空白图片
    mBitmap = Bitmap.createBitmap(720, 1280, Bitmap.Config.ARGB_8888);
    // 创建一张画布
    canvas = new Canvas(mBitmap);
    // 画布背景为白色
    canvas.drawColor(Color.WHITE);
    // 创建画笔
    paint = new Paint();
    // 画笔颜色为蓝色
    paint.setColor(Color.BLUE);
    // 宽度5个像素
    paint.setStrokeWidth(5);
    // 先将白色背景画上
    canvas.drawBitmap(mBitmap, new Matrix(), paint);
    img.setImageBitmap(mBitmap);
    img.setOnTouchListener(new OnTouchListener() {
      int startX;
      int startY;
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
          // 获取手按下时的坐标
          startX = (int) event.getX();
          startY = (int) event.getY();
          break;
        case MotionEvent.ACTION_MOVE:
          // 获取手移动后的坐标
          int endX = (int) event.getX();
          int endY = (int) event.getY();
          // 在开始和结束坐标间画一条线
          canvas.drawLine(startX, startY, endX, endY, paint);
          // 刷新开始坐标
          startX = (int) event.getX();
          startY = (int) event.getY();
          img.setImageBitmap(mBitmap);
          break;
        }
        return true;
      }
    });
  }
}

有人肯定要问,能不能把绘制的内容保存下来,这当然可以。

加上如下代码就行。

File file = new File(Environment.getExternalStorageDirectory(),
    System.currentTimeMillis() + ".jpg");
OutputStream stream;
try {
   stream = new FileOutputStream(file);
   mBitmap.compress(CompressFormat.JPEG, 200, stream);
   stream.close();
} catch (IOException e) {
  e.printStackTrace();
}

附:完整实例代码点击此处本站下载

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发动画技巧汇总》、《Android开发入门与进阶教程》、《Android视图View技巧总结》、《Android编程之activity操作技巧总结》、《Android文件操作技巧汇总》、《Android资源操作技巧汇总》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

您可能感兴趣的文章:

  • android实现简单的画画板实例代码
  • Android采用双缓冲技术实现画板
  • Android自定义SurfaceView实现画板功能
  • Android实现画板、写字板功能(附源码下载)
  • Android多媒体之画画板开发案例分享
  • 双缓冲技术实现Android 画板应用
  • Android画画板的制作方法
时间: 2018-02-04

双缓冲技术实现Android 画板应用

什么是双缓冲技术?双缓冲技术就是当用户操作界面完成后,会有一个缓冲区保存用户操作的结果. 为什么要使用双缓冲技术?拿Android 游戏开发来说,界面贞每次都是全部重画的,也就说画了新的,旧的就没了,所以需要使用双缓冲技术保存之前的内容. 如何实现双缓冲?使用一个Bitmap对象保留之前的画布即可. package com.example.phonegaptest; import android.content.Context; import android.graphics.Bitmap; i

Android采用双缓冲技术实现画板

本文实例为大家分享了Android实现画板的具体代码,采用的技术是双缓冲技术,供大家参考,具体内容如下 1.双缓冲技术的概念 所谓的双缓冲技术其实很简单,当程序需要在指定的View上进行绘制时,程序并不需要直接绘制到该View组件,而是先绘制到一个内存中的Bitmap图片上(就是缓冲),等内存中的Bitmap绘制好之后,再一次性将Bitmap绘制到View组件上. 2.Android采用双缓冲实现画板  实现的思路: 1).定义一个内存中图片,将他作为缓冲区Bitmap cacheBitmap

Android自定义SurfaceView实现画板功能

接触了这么久的View,总不能一直停留在View里,现在开始呢,就要学习一个新的知识点:SurfaceView,实际上SurfaceView与View的原理都差不多,只是效率和渲染方式上,SurfaceView要优于View,这也是我们写这个的原因.今天就看看这个SurfaceView,好了,下面就是今天要说的效果. 界面很简单,就是一个按钮以及一个画板,先看看界面的代码吧 <LinearLayout xmlns:android="http://schemas.android.com/ap

Android多媒体之画画板开发案例分享

先看看效果: 其实画画板的原理很简单,就是首先记录下按下屏幕的点,然后每移动一下就让这两次移动的点连线,周而复始,图像就由很多条直线构成了. 核心代码 : public class MainActivity extends Activity implements OnClickListener,OnSeekBarChangeListener { private View red_view,green_view,blue_view; //控制画笔颜色的三块区域 private SeekBar se

Android实现画板、写字板功能(附源码下载)

前言 本文给大家分享一个使用Android开发写字板功能Dem.简单操作内存中的图像.对图像进行简单的处理.绘制直线.以达到写字板的效果 效果图如下 XML布局代码 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="

Android画画板的制作方法

本文实例为大家分享了Android画画板展示的具体代码,供大家参考,具体内容如下 main.xml布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_w

android实现简单的画画板实例代码

直接看代码,注释都写清楚了 复制代码 代码如下: public class MainActivity extends Activity { private ImageView iv; private Bitmap baseBitmap; private Canvas canvas; private Paint paint; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedIns

Android 仿今日头条简单的刷新效果实例代码

点击按钮,先自动进行下拉刷新,也可以手动刷新,刷新完后,最后就多一行数据.有四个选项卡. 前两天导师要求做一个给本科学生预定机房座位的app,出发点来自这里.做着做着遇到很多问题,都解决了.这个效果感觉还不错,整理一下. MainActivity package com.example.fragmentmytest; import android.content.DialogInterface; import android.graphics.Color; import android.os.B

Android自定义view实现太极效果实例代码

Android自定义view实现太极效果实例代码 之前一直想要个加载的loading.却不知道用什么好,然后就想到了太极图标,最后效果是有了,不过感觉用来做loading简直丑到爆!!! 实现效果很简单,我们不要用什么贝塞尔曲线啥的,因为太极无非就是圆圆圆,只要画圆就ok了.来上代码: 因为有黑有白,所以定义2个画笔分别为黑和白. private void inital() { whitePaint = new Paint(); whitePaint.setAntiAlias(true); wh

Android自定义水波纹动画Layout实例代码

话不多说,我们先来看看效果: Hi前辈搜索预览 这一张是<Hi前辈>的搜索预览图,你可以在这里下载这个APP查看更多效果: http://www.wandoujia.com/apps/com.superlity.hiqianbei LSearchView 这是一个MD风格的搜索框,集成了ripple动画以及search时的loading,使用很简单,如果你也需要这样的搜索控件不妨来试试:https://github.com/onlynight/LSearchView RippleEverywh

Android连接指定Wifi的方法实例代码

本篇文章主要记录一下Android中打开Wifi.获取Wifi接入点信息及连接指接入点的方法. 自己写的demo主要用于测试接口的基本功能,因此界面及底层逻辑比较粗糙. demo的整体界面如下所示: 上图中的OPEN按键负责开启Wifi: GET按键负责获取扫描到的接入点信息. 当获取到接入点信息后,我选取了其中的名称及信号强度,以列表的形式显示在主界面下方,如下图: 当点击列表中的Item时,就会去连接对应的接入点. 自己的逻辑比较简单,测试时的代码,假定连接的是不许要密码或密码已知的接入点.

Android实现沉浸式导航栏实例代码

废话不多说了,直接给大家贴代码了,具体代码如下所示: private SystemBarTintManager tintManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // getWindow().addFlags(WindowManager.Layo

Android实现客户端语音动弹界面实例代码

今天为大家介绍一下语音动弹界面的实现,新版本的客户端大家应该都看过了,这里我就只简单的介绍一下控件布局了.你可以在这里看到本控件的完整源码:http://git.oschina.net/oschina/android-app/blob/master/osc-android-app/src/net/oschina/app/widget/RecordButton.java 首先,整体界面分三部分,最上层自定义ActionBar相信不需要我讲大家就能看出来了. 中间部分是文字动弹部分,主体就是一个设置

Android 绘制多级树形选择列表实例代码

一.概述 前段时间有个项目的需要在Android端显示一个复选的多层树形控件,主要展示一个公司的组织架构,类似总部下面有各个部门,部门之下是组和员工等.另外需要加上展开与回收部门详情.关闭部分已开展的布局.勾选等功能. 效果图如下: 二.思路分析 毫无疑问,对于这种数据可能达到几千几万行的列表视图,我们需要选择recyclerview等具有回收item功能的控件,因此Item的状态保持放在Model中而不是View中. 由于原始数据是树形结构的,我们需要先将树形结构转换为列表数据,类似根结点 -

Android不规则封闭区域填充色彩的实例代码

一.概述 在上一篇的叙述中,我们通过图层的方式完成了图片颜色的填充(详情请戳:Android不规则图像填充颜色小游戏),不过在着色游戏中更多的还是基于边界的图像的填充.本篇博客将详细描述. 图像的填充有2种经典算法. 一种是种子填充法. 种子填充法理论上能够填充任意区域和图形,但是这种算法存在大量的反复入栈和大规模的递归,降低了填充效率. 另一种是扫描线填充法. 注意:实际上图像填充的算法还是很多的,有兴趣可以去Google学术上去搜一搜. ok,下面先看看今天的效果图: ok,可以看到这样的颜

Android DSelectorBryant 单选滚动选择器的实例代码

单选滚动选择器.diy丰富.有阻尼效果.简单美观.触摸or点击模式 (Rolling Selector, Diy Rich, Damping Effect, Simple and Beautiful, Touch or Click Mode) Github地址 YangsBryant/DSelectorBryant (Github排版比较好,建议进入这里查看详情,如果觉得好,点个star吧!) 引入module allprojects { repositories { google() jcen