Android Blur View(模糊视图背后的背景)

Posted

技术标签:

【中文标题】Android Blur View(模糊视图背后的背景)【英文标题】:Android Blur View (Blur background behind the view) 【发布时间】:2015-10-26 20:07:58 【问题描述】:

我正在尝试使图像的底部部分模糊,使其顶部的视图像图像中一样。

我尝试使用 Rendenscript 对其进行模糊处理,但我无法仅模糊视图后面的部分。 :(

我见过很多库,但几乎所有的库都模糊了整个图像,而不是其中的一部分。

另外,一个重要的部分是我在 ViewPager 中使用它,因此需要像this in ios 这样的快速和动态的东西,它会在它背后的图像发生变化时重新绘制自己。

感谢任何帮助。感谢您的光临!

我的 xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:gravity="center"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/image"
        android:src="@drawable/broadstairs"
        android:layout_
        android:layout_
        android:scaleType="centerCrop"
        android:layout_centerInParent="true"/>

    <TextView
        android:id="@+id/text"
        android:layout_
        android:layout_
        android:text="Hello World"
        android:gravity="center"
        android:layout_alignParentBottom="true"
        android:textColor="@android:color/white"
        android:textStyle="bold"
        android:textSize="36sp"/>
</RelativeLayout>

我的代码:

BlurBuilder.java

public class BlurBuilder 
    private static final float BITMAP_SCALE = 0.1f;
    private static final float BLUR_RADIUS = 7.5f;

    public static Bitmap blur(Context context, Bitmap image) 
        int width = Math.round(image.getWidth() * BITMAP_SCALE);
        int height = Math.round(image.getHeight() * BITMAP_SCALE);

        Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
        Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);

        RenderScript rs = RenderScript.create(context);
        ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
        Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
        theIntrinsic.setRadius(BLUR_RADIUS);
        theIntrinsic.setInput(tmpIn);
        theIntrinsic.forEach(tmpOut);
        tmpOut.copyTo(outputBitmap);

        return outputBitmap;
    

    @SuppressLint("NewApi")
    public static void blur(final Context context, final Bitmap bitmap, final View view) 

        new AsyncTask<Void, Void, Bitmap>() 

            @Override
            protected Bitmap doInBackground(Void... params) 
                Paint paint = new Paint();
                paint.setFilterBitmap(true);
                Bitmap cropImage = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() - view.getHeight(), bitmap.getWidth(), view.getHeight());

                return BlurBuilder.blur(context, cropImage);
            


            @Override
            protected void onPostExecute(Bitmap bitmap) 
                super.onPostExecute(bitmap);
                if (bitmap != null) 
                    int sdk = android.os.Build.VERSION.SDK_INT;
                    if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) 
                        view.setBackgroundDrawable(new BitmapDrawable(context.getResources(), bitmap));
                     else 
                        view.setBackground(new BitmapDrawable(context.getResources(), bitmap));
                    

                

            
        .execute();


    


在我的 MainActivity 的 onCreate 中我这样做:

BlurBuilder.blur(BitmapActivity.this, ((BitmapDrawable) mView.getDrawable()).getBitmap(), mDummyView);

结果如下:

【问题讨论】:

也许你的底视图上的透明模糊可以解决问题。 @DerGol...lum 我不知道为什么,我试过 FastBlur 他们的示例 apk 工作得很好,但是当我实施它们时,我会模糊整个图像:( 是的。只需将模糊应用于background 图像。不是srcandroid:src 在“第一平面”图像上工作。 android:background 仅在后台工作。有 Java 等价物:setDrawable()setBackgroundDrawable() - 分别是,因为你必须在你的代码中做到这一点。不过,您可能必须禁用硬件加速。 if (sdk 只需用底部视图的高度剪切图像的底部,然后模糊该位图 【参考方案1】:

将这两个类添加到您的应用中,

1> BlurKit.Java

public class BlurKit 

    private static BlurKit instance;

    private RenderScript rs;

    public static void init(Context context) 
        if (instance != null) 
            return;
        

        instance = new BlurKit();
        instance.rs = RenderScript.create(context);
    

    public Bitmap blur(Bitmap src, int radius) 
        final Allocation input = Allocation.createFromBitmap(rs, src);
        final Allocation output = Allocation.createTyped(rs, input.getType());
        final ScriptIntrinsicBlur script;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) 
            script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
            script.setRadius(radius);
            script.setInput(input);
            script.forEach(output);
        
        output.copyTo(src);
        return src;
    

    public Bitmap blur(View src, int radius) 
        Bitmap bitmap = getBitmapForView(src, 1f);
        return blur(bitmap, radius);
    

    public Bitmap fastBlur(View src, int radius, float downscaleFactor) 
        Bitmap bitmap = getBitmapForView(src, downscaleFactor);
        return blur(bitmap, radius);
    

    private Bitmap getBitmapForView(View src, float downscaleFactor) 
        Bitmap bitmap = Bitmap.createBitmap(
                (int) (src.getWidth() * downscaleFactor),
                (int) (src.getHeight() * downscaleFactor),
                Bitmap.Config.ARGB_4444
        );

        Canvas canvas = new Canvas(bitmap);
        Matrix matrix = new Matrix();
        matrix.preScale(downscaleFactor, downscaleFactor);
        canvas.setMatrix(matrix);
        src.draw(canvas);

        return bitmap;
    

    public static BlurKit getInstance() 
        if (instance == null) 
            throw new RuntimeException("BlurKit not initialized!");
        

        return instance;
    


2> BlurLayout.Java

public class BlurLayout extends FrameLayout 

    public static final float DEFAULT_DOWNSCALE_FACTOR = 0.12f;
    public static final int DEFAULT_BLUR_RADIUS = 12;
    public static final int DEFAULT_FPS = 60;

    // Customizable attributes

    /** Factor to scale the view bitmap with before blurring. */
    private float mDownscaleFactor;

    /** Blur radius passed directly to stackblur library. */
    private int mBlurRadius;

    /** Number of blur invalidations to do per second.  */
    private int mFPS;

    // Calculated class dependencies

    /** Reference to View for top-parent. For retrieval see @link #getActivityView() getActivityView. */
    private WeakReference<View> mActivityView;

    public BlurLayout(Context context) 
        super(context, null);
    

    public BlurLayout(Context context, AttributeSet attrs) 
        super(context, attrs);
        BlurKit.init(context);

        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.BlurLayout,
                0, 0);

        try 
            mDownscaleFactor = a.getFloat(R.styleable.BlurLayout_downscaleFactor, DEFAULT_DOWNSCALE_FACTOR);
            mBlurRadius = a.getInteger(R.styleable.BlurLayout_blurRadius, DEFAULT_BLUR_RADIUS);
            mFPS = a.getInteger(R.styleable.BlurLayout_fps, DEFAULT_FPS);
         finally 
            a.recycle();
        

        if (mFPS > 0) 
            Choreographer.getInstance().postFrameCallback(invalidationLoop);
        
    

    /** Choreographer callback that re-draws the blur and schedules another callback. */
    private Choreographer.FrameCallback invalidationLoop = new Choreographer.FrameCallback() 
        @Override
        public void doFrame(long frameTimeNanos) 
            invalidate();
            Choreographer.getInstance().postFrameCallbackDelayed(this, 1000 / mFPS);
        
    ;

    /**
     * @inheritDoc
     */
    @Override
    public void invalidate() 
        super.invalidate();
        Bitmap bitmap = blur();
        if (bitmap != null) 
            setBackground(new BitmapDrawable(bitmap));
        
    

    /**
     * Recreates blur for content and sets it as the background.
     */
    private Bitmap blur() 
        if (getContext() == null) 
            return null;
        

        // Check the reference to the parent view.
        // If not available, attempt to make it.
        if (mActivityView == null || mActivityView.get() == null) 
            mActivityView = new WeakReference<>(getActivityView());
            if (mActivityView.get() == null) 
                return null;
            
        

        // Calculate the relative point to the parent view.
        Point pointRelativeToActivityView = getPositionInScreen();

        // Set alpha to 0 before creating the parent view bitmap.
        // The blur view shouldn't be visible in the created bitmap.
        setAlpha(0);

        // Screen sizes for bound checks
        int screenWidth = mActivityView.get().getWidth();
        int screenHeight = mActivityView.get().getHeight();

        // The final dimensions of the blurred bitmap.
        int width = (int) (getWidth() * mDownscaleFactor);
        int height = (int) (getHeight() * mDownscaleFactor);

        // The X/Y position of where to crop the bitmap.
        int x = (int) (pointRelativeToActivityView.x * mDownscaleFactor);
        int y = (int) (pointRelativeToActivityView.y * mDownscaleFactor);

        // Padding to add to crop pre-blur.
        // Blurring straight to edges has side-effects so padding is added.
        int xPadding = getWidth() / 8;
        int yPadding = getHeight() / 8;

        // Calculate padding independently for each side, checking edges.
        int leftOffset = -xPadding;
        leftOffset = x + leftOffset >= 0 ? leftOffset : 0;

        int rightOffset = xPadding;
        rightOffset = x + getWidth() + rightOffset <= screenWidth ? rightOffset : screenWidth - getWidth() - x;

        int topOffset = -yPadding;
        topOffset = y + topOffset >= 0 ? topOffset : 0;

        int bottomOffset = yPadding;
        bottomOffset = y + height + bottomOffset <= screenHeight ? bottomOffset : 0;

        // Create parent view bitmap, cropped to the BlurLayout area with above padding.
        Bitmap bitmap;
        try 
            bitmap = getDownscaledBitmapForView(
                    mActivityView.get(),
                    new Rect(
                            pointRelativeToActivityView.x + leftOffset,
                            pointRelativeToActivityView.y + topOffset,
                            pointRelativeToActivityView.x + getWidth() + Math.abs(leftOffset) + rightOffset,
                            pointRelativeToActivityView.y + getHeight() + Math.abs(topOffset) + bottomOffset
                    ),
                    mDownscaleFactor
            );
         catch (NullPointerException e) 
            return null;
        

        // Blur the bitmap.
        bitmap = BlurKit.getInstance().blur(bitmap, mBlurRadius);

        //Crop the bitmap again to remove the padding.
        bitmap = Bitmap.createBitmap(
                bitmap,
                (int) (Math.abs(leftOffset) * mDownscaleFactor),
                (int) (Math.abs(topOffset) * mDownscaleFactor),
                width,
                height
        );

        // Make self visible again.
        setAlpha(1);

        // Set background as blurred bitmap.
        return bitmap;
    

    /**
     * Casts context to Activity and attempts to create a view reference using the window decor view.
     * @return View reference for whole activity.
     */
    private View getActivityView() 
        Activity activity;
        try 
            activity = (Activity) getContext();
         catch (ClassCastException e) 
            return null;
        

        return activity.getWindow().getDecorView().findViewById(android.R.id.content);
    

    /**
     * Returns the position in screen. Left abstract to allow for specific implementations such as
     * caching behavior.
     */
    private Point getPositionInScreen() 
        return getPositionInScreen(this);
    

    /**
     * Finds the Point of the parent view, and offsets result by self getX() and getY().
     * @return Point determining position of the passed in view inside all of its ViewParents.
     */
    private Point getPositionInScreen(View view) 
        if (getParent() == null) 
            return new Point();
        


        ViewGroup parent;
        try 
            parent = (ViewGroup) view.getParent();
         catch (Exception e) 
            return new Point();
        

        if (parent == null) 
            return new Point();
        

        Point point = getPositionInScreen(parent);
        point.offset((int) view.getX(), (int) view.getY());
        return point;
    

    /**
     * Users a View reference to create a bitmap, and downscales it using the passed in factor.
     * Uses a Rect to crop the view into the bitmap.
     * @return Bitmap made from view, downscaled by downscaleFactor.
     * @throws NullPointerException
     */
    private Bitmap getDownscaledBitmapForView(View view, Rect crop, float downscaleFactor) throws NullPointerException 
        View screenView = view.getRootView();

        int width = (int) (crop.width() * downscaleFactor);
        int height = (int) (crop.height() * downscaleFactor);

        if (screenView.getWidth() <= 0 || screenView.getHeight() <= 0 || width <= 0 || height <= 0) 
            throw new NullPointerException();
        

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

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

        return bitmap;
    

    /**
     * Sets downscale factor to use pre-blur.
     * See @link #mDownscaleFactor.
     */
    public void setDownscaleFactor(float downscaleFactor) 
        this.mDownscaleFactor = downscaleFactor;
        invalidate();
    

    /**
     * Sets blur radius to use on downscaled bitmap.
     * See @link #mBlurRadius.
     */
    public void setBlurRadius(int blurRadius) 
        this.mBlurRadius = blurRadius;
        invalidate();
    

    /**
     * Sets FPS to invalidate blur with.
     * See @link #mFPS.
     */
    public void setFPS(int fps) 
        this.mFPS = fps;
    


在 XML 文件中:

               <FrameLayout
                        android:id="@+id/fl_uploadedView"
                        android:layout_
                        android:layout_
                        android:layout_margin="@dimen/_10sdp">

                        <ImageView
                            android:id="@+id/dv_uploadedPic"
                            android:layout_
                            android:layout_
                            android:contentDescription="@string/app_name"
                            android:scaleType="centerCrop"
                            android:src="@color/gray" />

                        <BlurLayout
                            android:id="@+id/ll_blurView"
                            android:layout_
                            android:layout_
                            android:layout_gravity="bottom">

                        <TextView
                            android:id="@+id/tv_fileName"
                            android:layout_
                            android:layout_
                            android:layout_gravity="left"
                            android:padding="@dimen/_5sdp"
                            android:textSize="@dimen/_10sdp"
                           />
                </BlurLayout>
            </FrameLayout>

不要忘记将其添加到 值 > attr.xml

<!--
         Blur Layout start
      -->
    <declare-styleable name="BlurLayout">
        <attr name="downscaleFactor" format="float" />
        <attr name="blurRadius" format="integer" />
        <attr name="fps" format="integer" />
    </declare-styleable>

    <!--
       Blur Layout end
    -->

【讨论】:

如果不是不断地创建模糊位图、无效化、重绘、重新创建......这对应用程序性能非常不利。【参考方案2】:

这可以通过以下步骤实现:

通过裁剪背景图像提取LinearLayout的背景图像。 现在扩展 LinearLayout 类。

覆盖 OnDraw(Canvas mCanvas) 方法。

在您的自定义 LinearLayout 类中创建两个方法:

    绘制位图 绘制颜色。

首先调用 DrawBitmap 函数,方法是将您从 ViewPager 获得的偏移量赋予背景图像,以便在用户滑动屏幕时图像移动。

最后用你的透明度绘制颜色

希望这能解决你的问题。

此示例代码

How to Blur a View

【讨论】:

【参考方案3】:

试试applyBlur方法:

private void applyBlur(ImageView image) 
    image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() 
        @Override
        public boolean onPreDraw() 
            image.getViewTreeObserver().removeOnPreDrawListener(this);
            image.buildDrawingCache();

            Bitmap bmp = image.getDrawingCache();
            blur(bmp, text);
            return true;
        
    );


@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void blur(Bitmap bkg, View view) 
    long startMs = System.currentTimeMillis();

    float radius = 20;

    Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()),
            (int) (view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(overlay);

    canvas.translate(-view.getLeft(), -view.getTop());
    canvas.drawBitmap(bkg, 0, 0, null);

    RenderScript rs = RenderScript.create(getActivity());

    Allocation overlayAlloc = Allocation.createFromBitmap(
            rs, overlay);

    ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(
            rs, overlayAlloc.getElement());

    blur.setInput(overlayAlloc);

    blur.setRadius(radius);

    blur.forEach(overlayAlloc);

    overlayAlloc.copyTo(overlay);

    view.setBackground(new BitmapDrawable(
            getResources(), overlay));

    rs.destroy();

【讨论】:

【参考方案4】:

以下代码将使用以下代码模糊图像的左右两侧:

ViewPictureActivity 内部:

private void applyBlur(final ImageView image) 
    image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() 
        @Override
        public boolean onPreDraw() 
            image.getViewTreeObserver().removeOnPreDrawListener(this);
            image.buildDrawingCache();

            Bitmap bmp = image.getDrawingCache();
            blur(bmp, image);
            return true;
        
    );


@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void blur(Bitmap bkg, View view) 

    float radius = 20;

    Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()),
            (int) (view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(overlay);
    canvas.translate(-view.getLeft(), -view.getTop());
    canvas.drawBitmap(bkg, 100, 0,null);
    canvas.drawBitmap(bkg, -95,0,null);
    RenderScript rs = RenderScript.create(getApplicationContext());

    Allocation overlayAlloc = Allocation.createFromBitmap(
            rs, overlay);

    ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(
            rs, overlayAlloc.getElement());

    blur.setInput(overlayAlloc);

    blur.setRadius(radius);

    blur.forEach(overlayAlloc);

    overlayAlloc.copyTo(overlay);

    view.setBackground(new BitmapDrawable(
            getResources(), overlay));

    rs.destroy();
//end blur

将此类添加到您的项目中:

public class ZoomableImageView extends android.support.v7.widget.AppCompatImageView

    Matrix matrix = new Matrix();

static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
static final int CLICK = 3;
int mode = NONE;

PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 4f;
float[] m;

float redundantXSpace, redundantYSpace;
float width, height;
float saveScale = 1f;
float right, bottom, origWidth, origHeight, bmWidth, bmHeight;

ScaleGestureDetector mScaleDetector;
Context context;

public ZoomableImageView(Context context, AttributeSet attr)

    super(context, attr);
    super.setClickable(true);
    this.context = context;
    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    matrix.setTranslate(1f, 1f);
    m = new float[9];
    setImageMatrix(matrix);
    setScaleType(ScaleType.MATRIX);

    setOnTouchListener(new OnTouchListener()
    

        @Override
        public boolean onTouch(View v, MotionEvent event)
        
            mScaleDetector.onTouchEvent(event);

            matrix.getValues(m);
            float x = m[Matrix.MTRANS_X];
            float y = m[Matrix.MTRANS_Y];
            PointF curr = new PointF(event.getX(), event.getY());

            switch (event.getAction())
            
                //when one finger is touching
                //set the mode to DRAG
                case MotionEvent.ACTION_DOWN:
                    last.set(event.getX(), event.getY());
                    start.set(last);
                    mode = DRAG;
                    break;
                //when two fingers are touching
                //set the mode to ZOOM
                case MotionEvent.ACTION_POINTER_DOWN:
                    last.set(event.getX(), event.getY());
                    start.set(last);
                    mode = ZOOM;
                    break;
                //when a finger moves
                //If mode is applicable move image
                case MotionEvent.ACTION_MOVE:
                    //if the mode is ZOOM or
                    //if the mode is DRAG and already zoomed
                    if (mode == ZOOM || (mode == DRAG && saveScale > minScale))
                    
                        float deltaX = curr.x - last.x;// x difference
                        float deltaY = curr.y - last.y;// y difference
                        float scaleWidth = Math.round(origWidth * saveScale);// width after applying current scale
                        float scaleHeight = Math.round(origHeight * saveScale);// height after applying current scale
                        //if scaleWidth is smaller than the views width
                        //in other words if the image width fits in the view
                        //limit left and right movement
                        if (scaleWidth < width)
                        
                            deltaX = 0;
                            if (y + deltaY > 0)
                                deltaY = -y;
                            else if (y + deltaY < -bottom)
                                deltaY = -(y + bottom);
                        
                        //if scaleHeight is smaller than the views height
                        //in other words if the image height fits in the view
                        //limit up and down movement
                        else if (scaleHeight < height)
                        
                            deltaY = 0;
                            if (x + deltaX > 0)
                                deltaX = -x;
                            else if (x + deltaX < -right)
                                deltaX = -(x + right);
                        
                        //if the image doesnt fit in the width or height
                        //limit both up and down and left and right
                        else
                        
                            if (x + deltaX > 0)
                                deltaX = -x;
                            else if (x + deltaX < -right)
                                deltaX = -(x + right);

                            if (y + deltaY > 0)
                                deltaY = -y;
                            else if (y + deltaY < -bottom)
                                deltaY = -(y + bottom);
                        
                        //move the image with the matrix
                        matrix.postTranslate(deltaX, deltaY);
                        //set the last touch location to the current
                        last.set(curr.x, curr.y);
                    
                    break;
                //first finger is lifted
                case MotionEvent.ACTION_UP:
                    mode = NONE;
                    int xDiff = (int) Math.abs(curr.x - start.x);
                    int yDiff = (int) Math.abs(curr.y - start.y);
                    if (xDiff < CLICK && yDiff < CLICK)
                        performClick();
                    break;
                // second finger is lifted
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    break;
            
            setImageMatrix(matrix);
            invalidate();
            return true;
        

    );


@Override
public void setImageBitmap(Bitmap bm)

    super.setImageBitmap(bm);
    bmWidth = bm.getWidth();
    bmHeight = bm.getHeight();


public void setMaxZoom(float x)

    maxScale = x;


private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener


    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector)
    
        mode = ZOOM;
        return true;
    

    @Override
    public boolean onScale(ScaleGestureDetector detector)
    
        float mScaleFactor = detector.getScaleFactor();
        float origScale = saveScale;
        saveScale *= mScaleFactor;
        if (saveScale > maxScale)
        
            saveScale = maxScale;
            mScaleFactor = maxScale / origScale;
        
        else if (saveScale < minScale)
        
            saveScale = minScale;
            mScaleFactor = minScale / origScale;
        
        right = width * saveScale - width - (2 * redundantXSpace * saveScale);
        bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
        if (origWidth * saveScale <= width || origHeight * saveScale <= height)
        
            matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
            if (mScaleFactor < 1)
            
                matrix.getValues(m);
                float x = m[Matrix.MTRANS_X];
                float y = m[Matrix.MTRANS_Y];
                if (mScaleFactor < 1)
                
                    if (Math.round(origWidth * saveScale) < width)
                    
                        if (y < -bottom)
                            matrix.postTranslate(0, -(y + bottom));
                        else if (y > 0)
                            matrix.postTranslate(0, -y);
                    
                    else
                    
                        if (x < -right)
                            matrix.postTranslate(-(x + right), 0);
                        else if (x > 0)
                            matrix.postTranslate(-x, 0);
                    
                
            
        
        else
        
            matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
            matrix.getValues(m);
            float x = m[Matrix.MTRANS_X];
            float y = m[Matrix.MTRANS_Y];
            if (mScaleFactor < 1) 
                if (x < -right)
                    matrix.postTranslate(-(x + right), 0);
                else if (x > 0)
                    matrix.postTranslate(-x, 0);
                if (y < -bottom)
                    matrix.postTranslate(0, -(y + bottom));
                else if (y > 0)
                    matrix.postTranslate(0, -y);
            
        
        return true;
    


@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    width = MeasureSpec.getSize(widthMeasureSpec);
    height = MeasureSpec.getSize(heightMeasureSpec);
    //Fit to screen.
    float scale;
    float scaleX =  width / bmWidth;
    float scaleY = height / bmHeight;
    scale = Math.min(scaleX, scaleY);
    matrix.setScale(scale, scale);
    setImageMatrix(matrix);
    saveScale = 1f;

    // Center the image
    redundantYSpace = height - (scale * bmHeight) ;
    redundantXSpace = width - (scale * bmWidth);
    redundantYSpace /= 2;
    redundantXSpace /= 2;

    matrix.postTranslate(redundantXSpace, redundantYSpace);

    origWidth = width - 2 * redundantXSpace;
    origHeight = height - 2 * redundantYSpace;
    right = width * saveScale - width - (2 * redundantXSpace * saveScale);
    bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
    setImageMatrix(matrix);

我使用以下布局来显示图像:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:context="com.example.yourpackagename">

<com.example.yourpackagename.ZoomableImageView
    android:id="@+id/zivImage"
    android:layout_
    android:layout_ />

使用 ViewPictureActivity 中的代码:

ZoomableImageView zifImage = findViewById(R.id.zivImage);
 zifImage.setImageBitmap(HelperClass.clientImageBitmap);
        applyBlur(zifImage);

此答案中使用的代码取自上述答案并进行了修改。

希望这会有所帮助。

【讨论】:

【参考方案5】:

使用GitHub: wasabeef's Blurry。它的更新日期为 2021 年,适用于 API >= 21。

Blurry.with(context)
    .capture(viewToBlur)
    .into(imageViewWithTheBlurredImage)

或者这个以位图为源

Blurry.with(context).from(bitmap).into(imageView)

此外,如果有一个来来去去的键盘,并且我们复制的屏幕布局在键盘上方,我们可以使用此 Kotlin 代码更新模糊视图(并在 imageViewWithTheBlurredImage 上使用 layout_height=wrap_content)。

private fun registerBlurryListenerForFutureLayoutChanges() 
    rootViewContainerToBlur.doOnNextLayout 
        if (!isDestroyed) rootViewContainerToBlur.doOnPreDraw 
            if (!isDestroyed) 
                captureAndBlur()
                registerBlurryListenerForFutureLayoutChanges()
            
        
    


【讨论】:

【参考方案6】:

Dmitry Saviuk 的这个库做得很好。配置和使用说明一目了然:

BlurView on GitHub

【讨论】:

这个 500px 已经很老了,它依赖的 RenderScript 现在显然已经消失了,developer.android.com/guide/topics/renderscript/migrate,该库没有构建,maven 库也消失了。 @arberg 感谢您的评论。是的,您是对的-我最初在答案中提到的 500px 库已经很旧并且没有维护。我已经搬到另一个图书馆(来自 Dmitry Saviuk),我现在正在更新答案。另外,在框架提供对创建模糊的原生支持之前,您是否会说这些渲染脚本库是用于创建模糊效果的后备(并且唯一?)选项? 我对渲染脚本一无所知,我只是看到它很久以前就被弃用了。我也确实找到了这份工作的包,并在这里创建了一个答案:***.com/a/69417068/197141 我确实遇到了 Blurry 库,但是答案中提到的库确实更好地解决了这个问题中提出的问题.. 那是 - 模糊特定视图背后的背景.. 而不是视图本身。就这个问题而言,这是关键点。

以上是关于Android Blur View(模糊视图背后的背景)的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 图像模糊原理

iOS View 模糊效果(毛玻璃)

移动端UI设计越来越流行的高斯模糊(Gaussian blur)和毛玻璃效果(磨砂效果),如何使用Android RenderScript简单实现?

iOS 高斯模糊 毛玻璃效果

SwiftUI - 如何模糊视图的默认背景颜色?

opencv之模糊处理