Android自定义控件-地图之放大镜的实现

最近半年一直在忙公司的GIS SDK,底层是公司的C++大佬来实现,我负责实现framework层,这不,大佬觉得C++实现的放大镜控件扩展性太差,就让我用Android自定义一个放大镜控件,方便后期扩展,要求嘛,和C++实现的要一模一样,手指触摸地图,放大触摸点的图像。

 1.需求分析

需求:手指拖拽节点的时候,放大镜出现,放大触摸点的图像,放大镜可以随着手指移动,放大镜不允许出现移出屏幕外的操作,放大镜需要有拟物效果。

实现方案:去网上找了一下,大致为:加载整个界面的Bitmap,然后手指移动,然后裁剪手指的这个范围的画布,然后再放大绘制出来,问题来了,我的需求是在地图上实现放大镜,地图是可以拖动的,不可能地图随便拖动一点,就更新一个新的Bitmap,那得卡死,最好是只获取手指触摸点的那块的小范围的Bitmap,去看了一下地图控件,是一个SurfaceView,那是不是可以用OPENGL 来获取;

图:

2.开始着手分析绘制过程:

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绘制,如果遇到了需要去放大地图的情况,可以参考,就这样,,,

你可能感兴趣的:(Android自定义控件-地图之放大镜的实现)