最近半年一直在忙公司的GIS SDK,底层是公司的C++大佬来实现,我负责实现framework层,这不,大佬觉得C++实现的放大镜控件扩展性太差,就让我用Android自定义一个放大镜控件,方便后期扩展,要求嘛,和C++实现的要一模一样,手指触摸地图,放大触摸点的图像。
需求:手指拖拽节点的时候,放大镜出现,放大触摸点的图像,放大镜可以随着手指移动,放大镜不允许出现移出屏幕外的操作,放大镜需要有拟物效果。
实现方案:去网上找了一下,大致为:加载整个界面的Bitmap,然后手指移动,然后裁剪手指的这个范围的画布,然后再放大绘制出来,问题来了,我的需求是在地图上实现放大镜,地图是可以拖动的,不可能地图随便拖动一点,就更新一个新的Bitmap,那得卡死,最好是只获取手指触摸点的那块的小范围的Bitmap,去看了一下地图控件,是一个SurfaceView,那是不是可以用OPENGL 来获取;
图:
1.获取手指触摸范围的Bitmap对象,看是否可行,不就就白搭:
代码如下:
/**
* @param x 手指的位置
* @param y 手指的位置
* @param width 放大镜的大小
* @param height 放大镜的大小
* @return
*/
private android.graphics.Bitmap readBufferPixelToBitmap(int x, int y, int width, int height) {
ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
buf.order(ByteOrder.LITTLE_ENDIAN);
GLES20.glReadPixels(x, y, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
buf.rewind();
android.graphics.Bitmap bmp = android.graphics.Bitmap.createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888);
bmp.copyPixelsFromBuffer(buf);
Matrix m = new Matrix();
// 水平翻转
m.setScale(1, -1);
int w = bmp.getWidth();
int h = bmp.getHeight();
// 生成的翻转后的bitmap
bmp = android.graphics.Bitmap.createBitmap(bmp, 0, 0, w, h, m, true);
return bmp;
}
2.测试了一下,Bitmap拿到了,那就很香了,剩下的一步就是把放大镜绘制出来,先不考虑放大镜随手指移动的情况,先实现放大镜效果:
@Override
protected void onDraw(Canvas canvas) {
if (bm != null) {
//背景防止加载自带透明的图片时,放大图片后面能看到原来的图片
Paint paintBg = new Paint();
//抗锯齿
paintBg.setAntiAlias(true);
paintBg.setColor(Color.parseColor("#ffffff"));
canvas.drawCircle(magnifierLen / 2, magnifierLen / 2, magnifierLen / 2, paintBg);
Paint paint = new Paint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
//抗锯齿
paint.setAntiAlias(true);
//bitmapShader画圆形图片,也就是获取到的bitmap
paint.setShader(bitmapShader);
//创建矩阵,缩放平移图片
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY);
//将获取到的图片平移到图片的正中心
matrix.postTranslate(-magnifierLen/2, -magnifierLen/2);
//利用bitmapShader画圆形图片
bitmapShader.setLocalMatrix(matrix);
canvas.drawCircle(magnifierLen / 2, magnifierLen / 2, magnifierLen / 2, paint);
//设置一个蒙层效果,让效果看起来更好
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.magnifierdest);
// 获得图片的宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 设置想要的大小
int newWidth = (int) magnifierLen;
int newHeight = (int) magnifierLen;
// 计算缩放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 取得想要缩放的matrix参数
Matrix matrix1 = new Matrix();
matrix1.postScale(scaleWidth, scaleHeight);
// 得到新的图片
Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix1,
true);
//创建一个画笔
Paint paintShade = new Paint(Paint.ANTI_ALIAS_FLAG);
paintShade.setAlpha(magnifierAlpha);
//重置画笔
paintShade.reset();
//调用截图图层的方法
paintShade.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
//画图片
canvas.drawBitmap(newbm, 0, 0, paintShade);
}
}
3.因为不是需要放大镜跟随手指,是需要放大镜跟随我的控件的拖拽节点,所以我去拖拽的监听方法里面去实时获取x,y,并拿到Bitmap(可以根据需求的不同,去对应的地方获取,比如OnTouch)
Bitmap bm = readBufferPixelToBitmap(x, y, viewW, viewH);
//利用BitmapShader画圆,模式可以查询用法
BitmapShader bitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
activityWeakReference.get().runOnUiThread(new Runnable() {
@Override
public void run() {
//去从新绘制放大镜
invalidate();
}
});
4.功能基本就实现了,也没什么,最后处理一下,手指在上下左右四个边界和几个角度的特殊情况,避免出现手指挡出放大镜之类的影响用户看的情况:
x = (int) (dragInfo.getScreenPos().getX() - magnifierLen / 2);//dragInfo为拖拽点信息
y = (int) (height - dragInfo.getScreenPos().getY() - magnifierLen / 2);
float x = dragInfo.getScreenPos().getX() - viewW;
float y = dragInfo.getScreenPos().getY() - viewH;
if (x>0&&y>0){
//正常情况放在手指头的左上角
setX(x);
setY(y);
}else if (x>0&&y<0){
//放在最顶上
setX(x);
setY(0);
}else if (x<0&&y>0){
//放在最左边
setX(0);
setY(y);
}else if (x<0&&y<0){
//在最顶上,向右平移一个单位
setY(0);
setX(viewW);
}
总结:因为这个放大镜不是固定位置,也不是写在xml中,我也就不考虑onMeasure了,直接只使用了onDraw绘制,如果遇到了需要去放大地图的情况,可以参考,就这样,,,