一、背景

如果有这样一个需求,需要擦除的是素材而保留背景图片,并且保存的时候能将背景图片和素材重新绘制到一起生成一张新的图片,这张图片大小比例就和原图一样,只不过上面多了素材。

之前在网上找过橡皮檫的实现,大多数是以整张view作为画板,但是这个需求情况下就不能使用整张view作为画板了,不然保存的时候还要裁剪出素材,一不小心还把素材大小什么的搞乱了,这时候就需要另一种办法了

二、实现

先重写view的手势监听事件


@Override
public boolean onTouchEvent(MotionEvent event) {
if (materialEntities.size() < 1) {return;}float x =event.getX();float y =event.getY();MaterialEntity materialEntity = materialEntities.get(0);x -= materialEntity.mBorderDstPoint[4];y -= materialEntity.mBorderDstPoint[5];float[]disPoint = new float[2];mMatrix.mapPoints(disPoint,new float[]{x, y});x = disPoint[0];y = disPoint[1];switch (event.getAction()& MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:mPaths.add(new Path());mPath = mPaths.getLast();mLastX = x;mLastY = y;mPath.moveTo(mLastX, mLastY);break;case MotionEvent.ACTION_MOVE:mPath.quadTo(mLastX, mLastY, x, y);//quardTo适合画曲线,lineTo适合直线mLastX = x;mLastY = y;drawPath();break;}
}

可以看到监听手势滑动时启动橡皮檫功能,上面我们说到橡皮檫要针对素材而不是整个view,这边就体现在mMatrix这个变量里面了,我们先来看一下这个变量的定义:


public void setMode(int mode) {mMode = mode;if (mMode != MODE_NORMAL) {setEraserListener(this);mMatrix.reset();int size = materialEntities.size();MaterialEntity materialEntity = size > 0 ? materialEntities.get(size - 1) : null;if (materialEntity!= null) {float scale = 1.0f / materialEntity.mImageScale;mMatrix.postScale(scale, scale);mMatrix.postRotate(360 - materialEntity.mImageDegree);}invalidate();} else {setEraserListener(null);}
}

可以看到这个矩阵的作用其实就是素材矩阵的逆变换,简单来说,比如素材被放大了两倍,然后你的手指指向了素材的中间,这时候我们怎么计算点击的位置相对素材的坐标呢,以下两个步骤:

1) 先把onTouchEvent获取的坐标点减去素材左上角坐标,求出触点相对素材左上角的向量

2) 把这个向量实行素材矩阵的逆变换,转换成以素材左上角为原点的坐标

有了坐标后就简单了,这时候就和以整张view作为画板的操作一样了,继续执行以下几个步骤:

1) 创建一个新的Canvas,并且以素材大小作为画板

2) 开始在这个新的Canvas上面画轨迹:


private void init() {mPaths = new LinkedBlockingDeque<>();mMatrix = new Matrix();mPaint = new Paint();mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));mEraserPaint = new Paint();mEraserPaint.setAntiAlias(true);mEraserPaint.setDither(true);mEraserPaint.setStyle(Paint.Style.STROKE);mEraserPaint.setStrokeJoin(Paint.Join.ROUND);mEraserPaint.setStrokeWidth(30);mRecoverPaint = new Paint();mRecoverPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));    mRecoverPaint.setAntiAlias(true);mRecoverPaint.setDither(true);mRecoverPaint.setStyle(Paint.Style.STROKE);mRecoverPaint.setStrokeJoin(Paint.Join.ROUND);mRecoverPaint.setStrokeWidth(30);}private void drawPath() {if (mMaskBitmap == null) {Bitmap dragImage = materailEntities.get(materailEntities.size() - 1).mImage;mMaskBitmap = Bitmap.createBitmap(dragImage.getWidth(), dragImage.getHeight(), dragImage.getConfig());mMaskCanvas = new Canvas(mMaskBitmap);}if (mMode == MODE_ERASER) {mMaskCanvas.drawPath(mPath, mEraserPaint);} else if (mMode == MODE_RECOVER) {mMaskCanvas.drawPath(mPath, mRecoverPaint);}if (mPathListener != null) {mPathListener.onDrawPath();}invalidate();
}

最终橡皮檫的实现就是用的图像混合的理念了,这边用的是PorterDuff.Mode.DST_OUT,即所画到的地方目标图片透明度变为0

这边你可能看到奇怪的地方,那个mMaskBitmap是干嘛用的呢,这个作用可大了,因为我们不仅要能擦除素材,还要能恢复擦除的部分,但是像drawPath这种是画下去就直接改变图片了,想要恢复是不可能了,那怎么办呢,就要用到mMaskBitmap了,原理如下:

1) 生成一张素材的蒙层,所有橡皮檫的轨迹用画笔画在上面,想要恢复的话用另外一个画笔在蒙层里面擦除橡皮檫画笔的轨迹

2) 应用效果时候,先画上素材原图,然后画上蒙层,蒙层使用的画笔还是PorterDuff.Mode.DST_OUT,这样的话蒙层上面橡皮檫的轨迹就会把素材原图上面对应的部位的透明度变为0,即达到了擦除素材的目的:

private void initCanvas() {mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);mCanvas = new Canvas(mBitmap);mClearPaint = new Paint();mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
} @Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Canvas tempCanvas;tempCanvas = mCanvas;tempCanvas.drawPaint(mClearPaint);tempCanvas.save();if (drawFilter == null) {drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);}tempCanvas.setDrawFilter(drawFilter);int i = 0;int totalDragImageCount = materailEntities.size();for (MaterailEntity materialEntity : materailEntities) {i++;final int saveCount = tempCanvas.save();tempCanvas.rotate(materialEntity.mImageShowDegree, materialEntity.centerX,materialEntity.centerY);tempCanvas.drawBitmap(materialEntity.mImage, materialEntity.mImageMatrix, mImagePaint);if (mMaskBitmap != null) {tempCanvas.drawBitmap(mMaskBitmap, materialEntity.mImageMatrix, mPaint);}if (mCanvas != null) {canvas.drawBitmap(mBitmap, 0, 0, null);tempCanvas.restore();}}

可以看到onDraw的时候先创建一张新的画布,然后在这张画布上面绘制好素材原图后再绘制素材的蒙层,最后把这张新的画布生成的图片绘制到屏幕上就实现了我们擦除素材的目的。

注意的是mImageMatrix里面只有缩放和平移,不包括旋转,所以绘制的时候要将画布先旋转,然后再画上素材

最后我们怎么保存呢:

public Bitmap saveDragImage(int position, float scale, float degree) {MaterialEntity materailEntity =materialEntities.size() > 0 && position < materialEntities.size() ? materialEntities.get(position) : null;if (materailEntity == null || (materailEntity.mImage == null)) {return null;}Bitmap dragBitmap;Matrix matrix = new Matrix();matrix.postScale(scale, scale);matrix.postRotate(degree);dragBitmap = Bitmap.createBitmap(dragImageEntity.mImage.getWidth(), dragImageEntity.mImage.getHeight(), dragImageEntity.mImage.getConfig());dragBitmap = Bitmap.createBitmap(dragBitmap, 0, 0, materailEntity.mImage.getWidth(),materailEntity.mImage.getHeight(), matrix, true);if (!dragBitmap.isMutable()) {dragBitmap = dragBitmap.copy(dragBitmap.getConfig(), true);}Canvas canvas = new Canvas(dragBitmap);matrix.reset();matrix.postTranslate(dragBitmap.getWidth() / 2 - dragImageEntity.mImage.getWidth() / 2.0f,dragBitmap.getHeight() / 2 - dragImageEntity.mImage.getHeight() / 2.0f);matrix.postScale(scale, scale, dragBitmap.getWidth() / 2, dragBitmap.getHeight() / 2);matrix.postRotate(degree, dragBitmap.getWidth() / 2, dragBitmap.getHeight() / 2);canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));canvas.drawBitmap(dragImageEntity.mDragImage, matrix, mDragImagePaint);if (mMaskBitmap != null) {canvas.drawBitmap(mMaskBitmap, matrix, mPaint);}return dragBitmap;
}

其实就是和onDraw类似,重新绘制一遍素材生成一张素材效果图,然后把这张图画到原图上面,最后就是处理好的图片了

Android实现素材擦除功能相关推荐

  1. Android 自定义写字板控件实现用图片做橡皮擦实现擦除功能

    在最近的开发项目中,要实现自定义写字板实现签名和用图片做橡皮擦实现擦除功能,这就需要动态添加图片,然后拖动图片的同时,实现擦除的效果,具体步骤如下: 1.自定义写字板: import android. ...

  2. android实现忘记密码功能,手机忘记密码如可解决 安卓手机重置密码教程【详解】...

    许多安卓手机用户都会为自己的手机设置一个密码,方便保护自己的手机隐私,但是密码变化多次之后,自己有时候也会忘记密码,尤其是图形密码,更是容易记混了.那么,安卓手机忘记密码该怎么办呢?一起来看看今天小编 ...

  3. Android 程序自动更新功能模块实现

    2019独角兽企业重金招聘Python工程师标准>>> Android 程序自动更新功能模块实现 在程序启动的时候检测服务器上有没有对应版本更新,如果有更新,提示用户是否更新. 在程 ...

  4. Android应用自动更新功能实现使用AsyncTask!

    为什么80%的码农都做不了架构师?>>>    我所开发应用不是面向大众的应用,所以无法放到应用市场去让大家下载,然后通过应用市场更新.所以我必要做一个应用自动更新功能.但是不难,T ...

  5. Android中实现Launcher功能之四---滑屏初探 scrollTo 以及 scrollBy方法使用说明

     本文原创 ,转载必须注明出处 :http://blog.csdn.net/qinjuning 今天给大家介绍下Android中滑屏功能的一个基本实现过程以及原理初探,最后给大家重点讲解View视图中 ...

  6. [转]Android 代码自动提示功能

    源地址http://blog.sina.com.cn/s/blog_7dbac12501019mbh.html 或者http://blog.csdn.net/longvslove/article/de ...

  7. android倒计时功能,Android 实现列表倒计时功能

    Android 实现列表倒计时功能 发布时间:2020-08-21 21:47:11 来源:脚本之家 阅读:147 作者:Choi晨 单个计时器,然后遍历数据 刷新条目: 两种实现方式:1.Handl ...

  8. android 上线apk,码云 Android apk 在线构建功能上线啦!

    原标题:码云 Android apk 在线构建功能上线啦! #点击上图,立即参与OSC珠海源创会# duang duang duang -- 各位看官,开源中国码云 Android 项目构建新功能上线 ...

  9. [转]5分钟实现Android中更换头像功能

    5分钟实现Android中更换头像功能 写在前面: 更换头像这个功能在用户界面几乎是100%出现的.通过拍摄照片或者调用图库中的图片,并且进行剪裁,来进行头像的设置. 功能相关截图如下: 下面我们直接 ...

最新文章

  1. cefsharp 发送请求服务器_WEB服务器之HTTP协议
  2. 关于单CPU,多CPU上的原子操作
  3. load、loads、dump、dumps的区别
  4. HTC 败诉对 Android 意味着什么?
  5. mybatis连接oracle_Mybatis 系列 0:初恋Mybatis
  6. mapbox 将坐标转换成米
  7. 【转】Linux 的启动流程
  8. mongodb 集群shard_MongoDB 分片集群环境搭建
  9. js生成xlsx/xls格式文件并下载到本地
  10. 搭建C语言开发环境,编写hello,world
  11. 【整理】PYTHON代码审查工具
  12. 游戏开发之地图编辑器
  13. led点阵c语言程序,51单片机驱动LED点阵扫描显示C语言程序
  14. SQLite在指定列后面插入字段_如何用SQL语句添加和修改字段?
  15. 来阿里三年,他从宠妻狂魔到正义战士
  16. sending data 慢SQL原因排查命令
  17. cocos creator麻将教程系列(九)—— 幼麟棋牌代码讲解
  18. ESP8266开发板+mysql数据库+DHT11
  19. 【Java程序设计】Java上机实验(二)
  20. TensorFlow Object Detection API Custom Object Hangs On

热门文章

  1. 读入一个数,计算其各位数字之和,用汉语拼音写出和的每一位数字
  2. minHash(最小哈希)和LSH(局部敏感哈希)
  3. cuda, cudnn的升级,各种深度学习库的安装
  4. pytorch+tensorflow+sklearn安装教程(conda版)
  5. FISCO BCOS中交易池及其优化策略
  6. Android EventLog简介
  7. flask---》Flask-WTF
  8. Java Stream API(一)
  9. js中双感叹号_JavaScript中双叹号!!作用示例介绍
  10. ECharts-Java 开源项目寻求合作维护