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

问题描述 投票:0回答:7

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

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

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

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

如有任何帮助,我们将不胜感激。感谢您的光临!

我的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>

我的代码:

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

结果如下:

android blur android-image android-bitmap blurry
7个回答
12
投票
将这两个类添加到您的应用程序中,

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_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>

不要忘记将其添加到

值 > 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 -->
    

0
投票
这可以通过以下步骤实现:

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

  • 重写OnDraw(Canvas mCanvas)方法。

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

      绘制位图
    1. 绘制颜色。
  • 首先调用DrawBitmap函数,给出从ViewPager获得的到背景图像的偏移量,以便在使用滑动屏幕时图像移动。

  • 最后根据透明度级别绘制颜色

我希望这能解决您的问题。

示例代码

如何模糊视图


0
投票
尝试

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

0
投票
以下代码将使用以下内容模糊图像的左侧和右侧:

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_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" />

使用 ViewPictureActivity 中的代码:

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

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

希望这有帮助。


0
投票
使用

GitHub:wasabeef 的 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() } } } }
    

0
投票
Dmitry Saviuk 的这个库很好地完成了这项工作。配置和使用说明非常简单:

GitHub 上的 BlurView


0
投票
如何实现Android图像模糊效果

我看到在 Android 图像上创建这种背景模糊有 2 个“挑战”。

    是为了创造一个最终作物
  1. 就是对其应用模糊
示例项目

以下是示例项目的结果

完整示例代码

https://github.com/hanscappelle/demo_android_imageview_scaletype源自https://github.com/elye/demo_android_imageview_scaletype/

步骤

创建一个布局,两个图像都位于顶部

你可以忽略

background

clipToOutline
,这些是圆角的。

重要的是,您有 2 个

ImageView

 彼此叠置,顶部的一个与背景的底部对齐。

示例中包含此处使用的图像参考。

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/backdrop" android:layout_width="0dp" android:layout_height="200dp" android:layout_margin="24dp" android:background="@drawable/rounded_corners" android:clipToOutline="true" android:scaleType="centerCrop" android:src="@drawable/athletic" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:id="@+id/blurred" android:layout_width="0dp" android:layout_height="40dp" android:layout_marginStart="24dp" android:layout_marginEnd="24dp" android:background="@drawable/rounded_corners_bottom" android:clipToOutline="true" android:src="@drawable/athletic" app:layout_constraintBottom_toBottomOf="@id/backdrop" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
我们创建一个 Activity 来设置此视图,如下所示。此代码使用 viewBinding,因此请确保在应用程序的 

buildFeatures.viewBinding

 中将 
build.gradle
 设置为 true。

class BlurEndCropView : AppCompatActivity() { private lateinit var binding: ViewBlurBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ViewBlurBinding.inflate(layoutInflater) val view = binding.root setContentView(view) binding.backdrop binding.blurred.apply { scaleType = ImageView.ScaleType.MATRIX addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> updateBaseMatrix() applyBlur(binding.blurred, 16f) } } }
创建末端或底部裁剪

下一步是在顶部图像上创建底部或末端裁剪,其中将包含模糊版本。

这是第一个挑战,因为 Android 默认情况下没有为此提供

scaleType

。所以我们自己这样实现。

/** * source: https://github.com/elye/demo_android_imageview_scaletype/ */ private fun updateBaseMatrix() { val drawable = binding.blurred.drawable ?: return val viewWidth: Float = getImageViewWidth(binding.blurred).toFloat() val viewHeight: Float = getImageViewHeight(binding.blurred).toFloat() val drawableWidth = drawable.intrinsicWidth val drawableHeight = drawable.intrinsicHeight val baseMatrix = Matrix() val widthScale = viewWidth / drawableWidth val heightScale = viewHeight / drawableHeight // end crop part val scale = widthScale.coerceAtLeast(heightScale) baseMatrix.postScale(scale, scale) baseMatrix.postTranslate( (viewWidth - drawableWidth * scale), (viewHeight - drawableHeight * scale) ) binding.blurred.imageMatrix = baseMatrix } private fun getImageViewWidth(imageView: ImageView): Int { return imageView.width - imageView.paddingLeft - imageView.paddingRight } private fun getImageViewHeight(imageView: ImageView): Int { return imageView.height - imageView.paddingTop - imageView.paddingBottom }
对顶部图像应用模糊

最后我们只需要对顶部图像应用模糊。为此,我们可以使用多种技术。

有兼容性更高的

renderkit

选项。

或者,如果您不介意将

minSdk

 设置为 api 级别 31,您也可以使用 Android 12 特定的 
RenderEffect
 
https://developer.android.com/reference/android/graphics/RenderEffect 就像我在示例代码如下:

/** * source: https://developer.android.com/guide/topics/renderscript/migrate#image_blur_on_android_12_into_a_view */ private fun applyBlur(view: View, radius: Float) { val blurRenderEffect = RenderEffect.createBlurEffect( radius, radius, Shader.TileMode.MIRROR ) view.setRenderEffect(blurRenderEffect) }
    
© www.soinside.com 2019 - 2024. All rights reserved.