目录
Android PorterDuffXfermode模式理解
前言
文字选择放大镜,类似于微信阅读器移动选择文字圆形放大镜效果.
效果图
长按移动选择文字,圆形放大镜的作用是为了方便选择文字,效果如下图gif.
实现思路
当前手指所选中的文字,以这个字为中心,画一个圆圈,上面的圆就实现了.里面的内容,获取的是当前界面的截图,往绘制的圆圈里填充即可,简单的理解你手指移动到哪个位置,就以这个位置为中心截取该圆形大小的内容即可.(当然实现方法较多,这个只是其中一个思路).
理解Canvas与Bitmap
老生常谈,canvas大家都知道,canvas是画布,bitmap就是字面的意思bit(位)map(图) = 位图.这两者有什么关联吗?
在Android关于Canvas的API描述中,一开始就如下描述:
To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap),
a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
谷歌翻译如下:
想画一些东西的时候,你需要4个基本的组件,一个Bitmap来存储像素,一个Canvas来接收draw的调用(draw的结果是将像素给画到前面所讲的Bitmap中,
一个源,即是你想画的东西(比如矩形,路径,文本,抑或另一个位图),最后就是一个画笔(描述想画的颜色和风格等)
简单这样理解下:canvas就仅仅是画布,类似于你画画所需要的外边框,承载内容的画板,bitmap相当于一个空白的画纸,承载你画画的内容,即你在canvas画的内容,都存储在bitmap上,最后你画完后,需要返回的是这个bitmap,而不是canvas,因为用户直观的看到的是位图bitmap,而不是画布canvas.
因此,Canvas canvas = new Canvas();这种写法没有意义,这个没有bitmap来承载画的内容,严格意义上来说,不可以这样写.
一般写法
//创建一个width,height的位图
Bitmap bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
//创建一个画布,传入创建好的位图
Canvas canvas = new Canvas(bitmap);
那么此时这个画布的大小就是这个bitmap的大小,此时可以调用canvas.drawCircle,drawRect等方法在bitmap上画所需要的内容了,最后画完后,返回此bitmap.
Android PorterDuffXfermode模式理解
上面对于canvas和bitmap理解后,下面的就好处理了,这个圆形的效果我使用的Android 系统api porterDuffXfermode这个类来实现的.
对于这个类的理解 大家可以看下我这边博客 https://blog.youkuaiyun.com/shenshibaoma/article/details/52291620,这篇博客写的比较早,就是使用xfermode来制作特定形状的头像,建议大家码起来.
放大镜效果使用的xfermode src_in模式,即两个图层图片交集后,显示src源图.
1.获取屏幕截图方法
private Bitmap getScreenBitmap(View view, int x, int y) {
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
if (x + floatViewWidth > bmp.getWidth()) {
x = bmp.getWidth() - floatViewWidth;
}
if (y + floatViewHeight > bmp.getHeight()) {
y = bmp.getHeight() - floatViewHeight;
}
if (x <= 0) {
x = 0;
}
if (y <= 0) {
y = 0;
}
Bitmap bitmap = Bitmap.createBitmap(bmp, x, y, floatViewWidth, floatViewHeight);
view.setDrawingCacheEnabled(false);
return bitmap;
}
2. 获取屏幕截图后,利用xfermode,最终获取放大镜bitmap,代码如下:
private Bitmap getMagnifierBitmap(Bitmap source, View view) {
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap target = Bitmap.createBitmap(source.getWidth() + 2, source.getHeight() + 2, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);
canvas.drawCircle((source.getWidth() + 2) / 2, (source.getHeight() + 2) / 2, source.getHeight() / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(source, 1, 1, paint);
paint.setXfermode(null);
paint.setColor(view.getContext().getResources().getColor(R.color.lib_reader_page_background_night));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawCircle((source.getWidth() + 2) / 2, (source.getWidth() + 2) / 2, source.getHeight() / 2 + 1, paint);
return target;
}
上述两个方法就是核心方法,不超过50行代码,主要是实现的思路.
3.最后在onTouchMove的时候不断刷新圆心位置,不断的刷新放大镜里面的bitmap即可实现该效果.
建议大家码起来!
文末彩蛋
厉害的是,谷歌在Android P增加了文字的放大镜效果,提升了用户体验.
https://mp.weixin.qq.com/s/UDXOw7ArKzvESSt65Ln4fw
或许实现的思路和我的类似吧!