20

I am trying to make the bottom part of an Image blur for the view on top it like in the image.

I tried blurring it using Rendenscript but I am not able to blur only the part behind the view. :(

I have seen many libraries but almost all of them blur the entire image, but not a part of it.

Also, an important part is that I am using this inside a ViewPager and hence needs to be fast and dynamic something like this in IOS which redraws itself the moment image behind it changes.

Any help is appreciated. Thanks for stopping by!

My xml:

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

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

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

My code:

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();


    }

}

In my MainActivity's onCreate I do:

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

Below is the result:

enter image description here

Atul O Holic
  • 6,692
  • 4
  • 39
  • 74
  • 1
    Maybe transparent blur on your bottom view will do the trick. – NullByte Aug 04 '15 at 12:44
  • @DerGol...lum I dont know why, I tried FastBlur their sample apk works perfectly, but when I am implementing them I get blurred the entire image :( – Atul O Holic Aug 04 '15 at 14:15
  • Yes. Just apply the blur to the `background` image. Not to the `src`. `android:src` works on the "first plane" image. `android:background` works on the background only. There are the Java equivalents: `setDrawable()` and `setBackgroundDrawable()` - respectively, since you have to do the trick in your code. You might have to disable the hardware acceleration, though. – Phantômaxx Aug 04 '15 at 14:29
  • if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) { view.setBackgroundDrawable(new BitmapDrawable(context.getResources(), bitmap)); } else { view.setBackground(new BitmapDrawable(context.getResources(), bitmap)); } I am already using those? – Atul O Holic Aug 04 '15 at 14:33
  • Just cut the bottom of your image with height of your bottom view, and blur that bitmap – NullByte Aug 05 '15 at 04:58
  • I think you can split the image in an external software and shove both parts in seperate layouts on top of each other so they appear seamless as one image and then apply blur to the bottom view with whatever tool/method you choose. Cheating at its best! FastBlur is great +1! – Ilan Kutsman Apr 19 '16 at 11:39
  • I found this [link](http://stackoverflow.com/questions/14780006/transparent-blurry-view-which-blurs-layout-underneath?rq=1) help, hope this helps. – Abhi Dec 31 '16 at 06:25

6 Answers6

12

Add these two classes to your app,

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;
    }

}

in XML file:

               <FrameLayout
                        android:id="@+id/fl_uploadedView"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_margin="@dimen/_10sdp">

                        <ImageView
                            android:id="@+id/dv_uploadedPic"
                            android:layout_width="match_parent"
                            android:layout_height="@dimen/_150sdp"
                            android:contentDescription="@string/app_name"
                            android:scaleType="centerCrop"
                            android:src="@color/gray" />

                        <BlurLayout
                            android:id="@+id/ll_blurView"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="bottom">

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

Do not forget to add this to values > 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
    -->
Deep Patel
  • 2,584
  • 2
  • 15
  • 28
  • 3
    This could be usable if it wasn't constantly creating the blur bitmap, invalidating, redrawing, recreating... very bad for app performance. – Sergio Dec 22 '19 at 16:47
0

Try applyBlur method:

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();
}
PEHLAJ
  • 9,980
  • 9
  • 41
  • 53
0

This can be achieved in following steps:

  • Extract the background image of LinearLayout by cropping background Image. Now extend LinearLayout Class.

  • Override OnDraw(Canvas mCanvas) method.

  • Create two methods in your Custom LinearLayout Class:

    1. DrawBitmap
    2. DrawColor.
  • First call the DrawBitmap function by giving the offset you got from ViewPager to background image so that image moves when the use swipe the screen.

  • Finally draw the color with your transparency level

I hope this will solve your problem.

Sample Code for this

How to Blur a View

Satan Pandeya
  • 3,747
  • 4
  • 27
  • 53
M.ArslanKhan
  • 3,640
  • 8
  • 34
  • 56
0

The following code will blur out left and right sides of image using the following:

Inside 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

Add this class to your project:

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);
}

}

I used the following layout to display the image:

<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_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.yourpackagename">

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

Using the code in your ViewPictureActivity:

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

Code used in this answer is taken from above answer and modified.

Hope this helps.

0

This library from Dmitry Saviuk does the job pretty well. Configuration and usage instructions are straight-forward:

BlurView on GitHub

Gagan
  • 1,497
  • 1
  • 12
  • 27
  • This 500px is very old, it relies on RenderScript which is now apparently gone, https://developer.android.com/guide/topics/renderscript/migrate, the library doesn't build and the maven library is gone. – arberg Oct 02 '21 at 12:04
  • @arberg Thanks for the comment. Yes you are right - The 500px library I initially mentioned in the answer is quite old and not maintained. I have moved over to another library (from Dmitry Saviuk) which I am updating in the answer now. Also, would you say these renderscript-libraries are fallback (and only?) options for creating blur-effect until the native support for creating Blur is provided by the framework? – Gagan Oct 05 '21 at 07:41
  • I don't really know anything about render-script, I just saw it was deprecated long ago. I did also find package for the job and I created an answer here: https://stackoverflow.com/a/69417068/197141 – arberg Oct 10 '21 at 19:28
  • I did come across the Blurry library, however the one mentioned in the answer does exactly what is asked in this question better.. that is - blurring the background behind a particular view.. and not the view itself. That's the key point as far as this question is concerned. – Gagan Oct 15 '21 at 18:28
0

Use GitHub: wasabeef's Blurry. Its up2date as of 2021, and works in API >= 21.

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

or this with a bitmap as the source

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

Also if there's a keyboard that comes and goes and the screen we copy is laid out above the keyboard, we can update the blurred view with this Kotlin code (and use layout_height=wrap_content on the imageViewWithTheBlurredImage).

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

arberg
  • 4,148
  • 4
  • 31
  • 39