下面我分享Android的一个抠图技巧,这篇文章只适合有Android基础和向量基础的小伙伴,如果朋友们刚学Android不久,建议先去了解Android自定义View、Touch机制、Canvas/Path/Paint、向量等相关知识。
先来看看效果图:这张是原图
抠出脸部图片,下面是结果:
下面我给小伙伴们讲述具体流程。
1,获取本地相册,很简单,直接代码,不解释
public void scanAlbum(Context context, AlbumScanListener listener){
try {
if(listener == null){
return;
}
List<LDAPhoto> photos = new ArrayList<>();
String[] projection = {MediaStore.Images.ImageColumns.DATA};
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null,null);
while(cursor.moveToNext()) {
LDAPhoto photo = new LDAPhoto();
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
File file = new File(path);
if(FileUtil.isFileExisted(path)) {
photo.setPath(path);
photo.setCreateTime(file.lastModified());
photos.add(photo);
}
}
listener.onCompleted(photos);
}catch (Exception e){
listener.onScannerFailed();
}
}
2,全屏预览图片,手势层与图片层分离
整个view分图片层、带透明度的手势层(Mask),mask层覆盖图片层,手势相关逻辑都在mask层展开
简而言之,就是预览图片,上面铺一层蒙层,然后在蒙层里进行相关的抠图操作,预览的时候建议不是直接加载原图,而是根据屏幕宽高对图片进行自适应缩放,比如原图可能10000x10000,而屏幕只有100x100,那直接加载原图直接oom了,可以参考我的另一篇博客,如何巧妙的读取本地图片——bitmap的常用小技巧
3手势层监听手指轨迹,生成对应闭合区域(Path)
核心思想:定义mCanvas,操作bitmap,通过onDraw,中回调的canvas绘制到屏幕。
(1)初始化画笔:
mMaskPaint是蒙层画笔,mPathPaint是手指轨迹画笔,mCirclePaint是圆滑起点和终点的画笔(手指轨迹可能不是闭合,此时应该对两端点进行圆滑处理,mPath即用户手指轨迹)
令注:mStartX,mStartY是图片的左上角坐标,mBmWidth,mBmHeight是图片宽高
private void initPaint() {
mMaskPaint = new Paint();//mask画笔
mMaskPaint.setAntiAlias(true);
mMaskPaint.setStyle(Paint.Style.FILL);
mMaskPaint.setStrokeCap(Paint.Cap.ROUND);
mMaskPaint.setStrokeJoin(Paint.Join.ROUND);
mMaskPaint.setColor(Color.parseColor("#d9000000"));
mPathPaint = new Paint();//path画笔
mPathPaint.setAlpha(0);
mPathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPathPaint.setPathEffect(new CornerPathEffect(mPathWidth / 2));
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeWidth(mPathWidth);
mCirclePaint = new Paint();//圆滑两端的圆形画笔
mCirclePaint.setAlpha(0);
mCirclePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCirclePaint.setPathEffect(new CornerPathEffect(mPathWidth / 2));
mCirclePaint.setAntiAlias(true);
mCirclePaint.setDither(true);
mCirclePaint.setStyle(Paint.Style.FILL);
mPath = new Path();//轨迹
mPathRect = new RectF();//轨迹所在的矩形区域
}
(2)初始化onDraw,第一次调用onDraw操作
if(mBitmap == null){
mBitma