1、前言
最近在做一个能够自选区域进行局部截图的功能,接下来,会给大家讲解,整个截图的实现过程。笔者这边实现的自选区域的形状是矩形,读者如果有需要,可以根据我给大家讲解的思路,修改成适合自己的截图工具。先来看看效果图
2、效果图
这里的图片是来自笔者对webView的截图产生的,读者可以根据自己的需要,替换上面的图片。
通过拖拽四条边框,可以实现屏幕的局部截图:
拖拽之后,只有需要截图的部分才会高亮显示,其余部分用遮罩掩盖。笔者实现的拖拽四条边都可以任意拖拽,并不一定要正方形或者长方形。也可以如下:
3、截图的基本知识
实现截图功能之前,我们需要了解View为我们提供的截图方法的使用。笔者会尽量详细讲述各个方法,让读者可以比较明白的去自由结合各截图方法的使用。
3.1、截图的基本知识点
在对View进行截图的时候,系统需要将当前View的内容缓存下来,因此,在截图之前,我们需要判断当前View是否允许视图缓存。
public boolean isDrawingCacheEnabled()
如果当前View允许进行视图缓存,会返回true否则返回false。如果返回false,我们就需要设置当前的视图,让它允许视图缓存。
public void setDrawingCacheEnabled(boolean enabled)
给当前视图强制生成缓存
public void buildDrawingCache(boolean autoScale)
此方法在视图不可缓存的时候,会强制生成一个视图缓存,autoScale表示是否自适应缩放缓存视图,如果为false的生成的视图大小和View的大小是一致的,true的话,则会根据内容生成自适应大小的图片。但是如果在调用此方法的之前,你没有调用setDrawingEnabled(true)的话,在对生成的视图使用完毕之后,应该对强制生成的视图缓存进行一个清理。清理的方法如下:
public void destroyDrawingCache()
此方法会将缓存的视图内容进行清理,从而对bitmap的资源进行释放回收利用。
获取缓存图像的方法:
public Bitmap getDrawingCache(boolean autoScale)
综合上述知识,使用方法如下:
if(!webView.isDrawingCacheEnabled())
webView.setDrawingCacheEnabled(true);
webView.buildDrawingCache();
Bitmap cropBitmap = webView.getDrawingCache(true);
4、自定义View的知识点要求
由于我们将会对View的视图内容进行部分截图,所以需要我们自定义一个能够监听用户动作从而产生对应响应的View。关于自定义View的基础知识,读者如果不明白的,可以看笔者的这篇文章
自定义View的基本思路,如果读者有这些基础知识,那么可以接着往下看。
先给一个下面讲解会遇到的局部变量和常量的含义说明,这样会比较容易理解下面的操作逻辑:
//移动过程的xy,imageView的canvas的宽高,点击事件的宽高,源图片的宽高。
private float moveX,moveY,startWidth,startHeight,downX,downY,bitmapWidth,bitmapHeight;
private Context context;
//当前手机的像素密度
private float density;
//是否可以开始拖拽选择框
private boolean isStart=true;
//分别表示上线,下线,左线,右线
private Line upLine,downLine,leftLine,rightLine;
//是否是上线移动,是否是垂直移动,是否是左线移动,是否是水平移动
//垂直移动分为上线移动和下线移动,垂直移动同理
private boolean isMovingUpLine=false,isMovingVertical=false,isMovingLeftLine=false,isMovingHorizontal=false;
//用来给点击点的坐标预留一些空间,便于判断移动时间的判断
private float padding;
private Paint paint = new Paint();
笔者将通过实现一个自定义ImageView,来实现一个可拖拽矩形边框,对选中内容高亮显示,对不选的内容进行遮罩掩饰。由于需要监听用户的触摸行为,我们需要实现OnTouchListener接口,如下:
public class ClipImageView extends ImageView implements View.OnTouchListener
当ClipImageView第一次展示的时候,我们需要在它的四周画上矩形边框,如图:
可以看到,在图片和四周的边框之间是有一定的留白的,便于分辨边框和图片的部分。所以我们需要重写OnDraw方法:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(isStart&&moveY==0) {
//初次布局,绘画四周的边线
initCanvas(canvas);
}
else
{
//重画边线和遮罩
reDraw(canvas);
}
}
我们关注iniCanvas方法即可,当前imageView如果是刚刚显示出来并且没有用户触摸动作的产生,就绘制四周的边框。为什么需要判断是否有用户触摸动作呢,因为用户产生触摸动作的时候,View一定是处于可见生命周期,此时就不是第一次绘制视图了,而是需要响应用户触摸行为来绘制视图了。
/**
* 初始化布局
* @param canvas
*/
private void initCanvas(Canvas canvas) {
//获取画布的宽高,用于初始化边线的宽高
startWidth = canvas.getWidth();
startHeight = canvas.getHeight();
upLine=new Line(0,0,startWidth,0);
downLine=new Line(0, startHeight, startWidth, startHeight);
leftLine=new Line(0,0,0,startHeight);
rightLine=new Line(startWidth,0,startWidth,startHeight);
Log.i("上线坐标",upLine.getLeft()+" "+upLine.getTop()+" "+upLine.getRight()+" "+upLine.getBottom() );
Log.i("下线坐标",downLine.getLeft()+" "+downLine.getTop()+" "+downLine.getRight()+" "+downLine.getBottom() );
Log.i("左线坐标",leftLine.getLeft()+" "+leftLine.getTop()+" "+leftLine.getRight()+" "+leftLine.getBottom());
Log.i("右线坐标",rightLine.getLeft()+" "+rightLine.getTop()+" "+rightLine.getRight()+" "+rightLine.getBottom());
//画出上下左右四条线
paint.setStrokeWidth(4);