Android编程实现画板功能的方法总结【附源码下载】
作者:Dennis-Android 发布时间:2023-08-05 10:45:07
本文实例讲述了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程序设计有所帮助。
来源:http://blog.csdn.net/qq_31530015/article/details/51115680


猜你喜欢
- java 泛型方法:泛型是什么意思在这就不多说了,而Java中泛型类的定义也比较简单,例如:public class Test
- 内容:1、滑动优化(滑动时不加载图片,停止才加载)2、第一次进入时手动加载代码如下:1、界面布局<?xml version="
- 智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制
- 目录1、IP地址2、端口3、通信协议3.1 TCP/IP协议簇:实际上是一组协议3.2 TCP UDP对比3.3 TCP实现聊天3.4 TC
- 这是进行Java Web开发必备的一个过程,仅供新手参考,高手可以忽略!JDK 和 JRE 的区别JRE(Java Runtime Envi
- Android Studio生成的APP默认图标是经典的机器人图标。可以通过Android Studio实现APP图标和名称的修改。1 修改
- Scrollview标题栏滑动渐变仿京东样式(上滑显示下滑渐变消失)/** * @ClassName MyScrollView * @Aut
- 实现过滤器和 * 首先,我们先来看一下二者在 Spring Boot 项目中的具体实现,这对后续理解二者的区别有很大的帮助。a) 实现过滤器
- 在使用AndroidNDK开发的时候有个事情是很烦人的,那就是创建本地代码文件夹,生成本地代码文件和创建本地代码的编译文件。特别是实现本地方
- 本文实例为大家分享了Android Studio实现弹窗设置的具体代码,供大家参考,具体内容如下弹窗能很好的显示当前处理事情的状态,那么这里
- Java连接SQL Server数据库的详细操作流程一.明确JDK版本和下载驱动1.1 JDK版本查看win + r输入cmd,命令窗口输入
- 本篇文章是直接下载最新的APK安装的方法,并不是增量下载该APk。想要实现一个android应用,自动更新下载APK软件的方法,我采取的是以
- 目录一、基础配置1、<parent> 标签1)使用 spring-boot-starter-parent2)使用自定义 pare
- 在mybatis中sql是写在xml映射文件中的,如果sql中有一些特殊字符的话,在解析xml文件的时候就会被转义,如若不希望被转义,那该怎
- 本文实例为大家分享了Retrofit2 RxJava2实现Android App自动更新,具体内容如下功能解析自动更新可以说已经是App的标
- 思路:先获得当前季度的开始和结束日期,在当前日期的基础上往前推3个月即上个季度的开始和结束日期/** * @param fla
- poi解析Excel文件版本问题解决办法poi解析Excel文件时有两种格式: HSSFWorkbook格式用来解析Excel2003(xl
- Android移动开发实现简单计算器功能,供大家参考,具体内容如下前言android 开发小实验android 移动开发实现 简易计算器功能
- 用微信提供的SDK来实现分享:从http://open.weixin.qq.com下载Android相关的jar包,将libammsdk.j
- 主要有四个:public——成员可以由任何代码访问。private——成员只能由类中的代码访问(如果没有使用任何关键字,就默认使用这个关键字