前言:近些天项目中需要一个放大镜的效果,但是网上大部分都是一张图片进行放大,但是现在我要对View进行放大所以特意研究了下Shader着色器
具体细节如下:
首先绘制一个放大镜的控件,需要ShapeDrawable来生成放大镜进行处理
mShapeDrawable = new ShapeDrawable(new OvalShape());生成一个椭圆形的shape,在当前控件中,首先一个初始化,用来初始化控件着色器的
public void setInitCurBitmap(Bitmap bitmap) { mBitmap = bitmap; mBitmapScale = mBitmap; mBitmapScale = Bitmap.createScaledBitmap(mBitmapScale, mBitmapScale.getWidth() * FACTOR, mBitmapScale.getHeight() * FACTOR, true); BitmapShader bitmapShader = new BitmapShader(mBitmapScale , Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mShapeDrawable.getPaint().setShader(bitmapShader); mShapeDrawable.setBounds(0, 0, 0, 0); invalidate(); }
通过外部传入的图片来绘制放大后的图片,通过传入响应的手指点击的坐标绘制响应的放大镜
public void setCurShowPos(int x, int y) { mMatrix.setTranslate(RADIUS - x * FACTOR, RADIUS - y * FACTOR); mShapeDrawable.getPaint().getShader().setLocalMatrix(mMatrix); mShapeDrawable.setBounds(x - RADIUS / 2, y - 3 * RADIUS / 2 , x + 3 * RADIUS / 2, y + RADIUS / 2); invalidate(); }
上面是用来绘制放大镜自定义控件的主类
接下来我们来自定义我们需要添加放大镜效果的父控件,控件中包含子控件,子控件中可能还有点击事件,我这里也做了响应的处理
private void init() { contentParent = (FrameLayout) ((Activity) getContext()).getWindow() .getDecorView().findViewById(android.R.id.content); zoomView = new ZoomView(getContext()); }
首先初始化跟布局DecorView,然后通过decorview拿到最顶层的FrameLayout,最后我们通过把放大镜绘制到顶层布局中便于随之我们手指移动,首先我们要在onInterceptTouchEvent事件中进行事件的拦击以便于我们拦截控件的点击事件,其中的isIntercept是用来处理当子控件没有点击事件以及在父控件空白处点击的时候能弹出响应的放大镜。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: startTime = System.currentTimeMillis(); isLong = false; break; case MotionEvent.ACTION_MOVE: if (System.currentTimeMillis() - startTime > 150 && !isLong) { isLong = true; isIntercept = true; } break; } if (isLong) { return true; } else { return false; } }
通过事件拦截后,我们在onTouchEvent中进行事件的处理如下
@Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); Log.d("点击的坐标", "x:" + x + "y:" + y); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: if (!isIntercept && System.currentTimeMillis() - startTime > 150 && !isLong) { isLong = true; isIntercept = true; } if (isLong && !isInit) { isInit = true; Bitmap bitmapFromView = getBitmapFromView(this); zoomView.setInitCurBitmap(bitmapFromView); contentParent.addView(zoomView); } if (isLong && isInit) { zoomView.setCurShowPos(x, y); } break; case MotionEvent.ACTION_UP: if (isLong && isInit) { contentParent.removeView(zoomView); isLong = false; isInit = false; } isIntercept = false;//恢复默认 break; } if (isLong) { return true; } else if (!isIntercept) {//此时没有点击事件需要拦截处理事件, // 有点击事件的时候,只用拦截才会进去onTouch,此时isLong为true return true; } else { return false; } }
通过事件的拦截处理,我们就可以获得在当前控件点击的时候获取响应的放大镜的效果
最后还有一个获取当前控件的图片如下
public static Bitmap getBitmapFromView(View v) { Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565); Canvas c = new Canvas(b); v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); // Draw background Drawable bgDrawable = v.getBackground(); if (bgDrawable != null) bgDrawable.draw(c); else c.drawColor(Color.WHITE); // Draw view to canvas v.draw(c); return b; }
通过以上我们就可以做到view的放大镜效果,还有就是为什么我会每次长按的时候都会获取一下view相对应的图片,是由于项目中图片加载时网络上的,为了防止缓存,造成当前图片给view真实样式不一样,才每次都获取。
效果图如下
最后希望大家多多关注,共同研究,探讨。
项目代码已经上传github,希望大家多多加星
你们的支持,就是我的动力
https://github.com/Zmingxu/ViewZoom