待写uiAndroid

Android自定义View之仿IOS模糊控件 (标题栏滑动过程

2020-06-20  本文已影响0人  请叫我果爸

前言

    Android开发过程中,总是需要与IOS坐比较,每次产品经理都要问,为什么IOS能做到,Android做不到。并不是Android做不到,主要原因是IOS下,有些控件是有现成的组件库的,Android没有,所以Android需要去自己绘制。
    上次产品经理就让我写一个功能,需要在页面滑动时,顶部导航栏需要从透明到模糊渐变,一定程度后不再改变。查阅了网上各种资料,发现关于Android高斯模糊的确有很多文章,但他们都是对于一个静态的页面或者图片做模糊,使用RenderScript高效计算平台进行模糊就好了。但我需要的效果中,页面是动态的,底下View是一直在改变的,怎么让他在需要的区域模糊呢。
    废话不多说,先看实现效果,点击看视频效果,更加清晰流畅。

    上面的效果是大家想要的吗?

实现方式

其实我的思路很简单,就只有四步,获取位置-裁剪-转化-模糊

Step 1 通过onLayout方法,先获取目标区域的位置

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
//        Log.i("BlurView", "onLayoutBlurView changed=" + changed + " left=" + left + " ,top=" + top + " ,right=" + right + " ,bottom=" + bottom);

        lastActionTime = System.currentTimeMillis();
        //获取Bitmap从View
        setBlurBitmapFromView(getLeft(), getTop(), getRight(), getBottom());
    }

Step 2 在整个Windows窗口中,对目标区域的View进行裁剪

     /**
     * 获取需要Bitmap
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    private void setBlurBitmapFromView(int left, int top, int right, int bottom) {
        //获取父View
        ViewGroup parent = (ViewGroup) getParent();
        //将此布局从父View移除,在截图不截进此视图。
        parent.removeView(this);
        //获取对应位置的Bitmap对象
        bitmap = getDownscaledBitmapForView(parent, new Rect(left, top, right, bottom), downScaleFactor);
        //截完图获取到bitmap之后,再把此View加载到父布局上
        parent.addView(this);
        printfActionTime("图片处理");

        //模糊处理
        bitmap = blurBitmap(bitmap, mBlurRadius);
        printfActionTime("模糊处理");
    }

Step 3 然后把裁剪下来的View转化成Bitmap,此时先做一次Bitmap压缩,为了让RenderScript计算的更快

    /**
     * 裁剪View,View转Bitmap,同时进行Bitmap压缩
     * @param view
     * @param crop
     * @param downscaleFactor 压缩比例
     * @return
     */
    private Bitmap getDownscaledBitmapForView(View view, Rect crop, float downscaleFactor) {
        int width = Math.round(crop.width() * downscaleFactor);
        int height = Math.round(crop.height() * downscaleFactor);

        if (view.getWidth() <= 0 || view.getHeight() <= 0 || width <= 0 || height <= 0) {
            throw new IllegalArgumentException("No screen available (width or height = 0)");
        }

        float dx = -crop.left * downscaleFactor;
        float dy = -crop.top * downscaleFactor;

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Matrix matrix = new Matrix();
        matrix.preScale(downscaleFactor, downscaleFactor);
        matrix.postTranslate(dx, dy);
        canvas.setMatrix(matrix);
        view.draw(canvas);

        return bitmap;
    }

Step 4 最后再进行模糊处理就好了

    /**
     * 模糊处理
     * @param image
     * @param blurRadius
     * @return
     */
    public Bitmap blurBitmap(Bitmap image, float blurRadius) {
        // 创建一张渲染后的输出图片
        Bitmap outputBitmap = Bitmap.createBitmap(image);
//        Log.i("BlurView", "onLayoutBlurView bitmap.size=" + inputBitmap.getByteCount());

        // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
        // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
        Allocation tmpIn = Allocation.createFromBitmap(rs, image);
        Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);

        // 设置渲染的模糊程度, 25f是最大模糊度
        blurScript.setRadius(blurRadius);
        // 设置blurScript对象的输入内存
        blurScript.setInput(tmpIn);
        // 将输出数据保存到输出内存中
        blurScript.forEach(tmpOut);

        // 将数据填充到Allocation中
        tmpOut.copyTo(outputBitmap);
        image.recycle();
        rs.destroy();

        return outputBitmap;
    }

    思路其实很简单的,代码也非常简洁,需要在复杂一点的,可以自己去扩展。

自取

源码地址:BlurView
Github地址:Open-View

有帮助就帮忙点个赞吧~

上一篇 下一篇

猜你喜欢

热点阅读