This commit is contained in:
muqing 2024-07-07 16:15:22 +08:00
parent c422c6dc75
commit 9b6021ce2e
23 changed files with 1423 additions and 28 deletions

View File

@ -40,4 +40,5 @@ dependencies {
testImplementation libs.junit
androidTestImplementation libs.ext.junit
androidTestImplementation libs.espresso.core
implementation(libs.glide)
}

View File

@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<!-- 网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 支持http android:usesCleartextTraffic="true"-->
<application
android:name=".App"
android:allowBackup="true"
@ -11,6 +15,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.AndroidDemo"
tools:targetApi="31" >
<activity
@ -18,6 +23,7 @@
android:exported="false" />
<activity
android:name=".MainActivity"
android:theme="@style/MainActivity"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -27,6 +27,7 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setTheme(R.style.Theme_AndroidDemo);
EdgeToEdge.enable(this);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
@ -51,7 +52,7 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
holder.binding.button.setText(Function.list.get(position).name);
holder.binding.button.setOnClickListener(v -> {
holder.binding.getRoot().setOnClickListener(v -> {
Function function = Function.list.get(holder.getAdapterPosition());
Intent intent = new Intent(MainActivity.this, function.Class);
startActivity(intent);

View File

@ -3,10 +3,19 @@ package com.example.androiddemo.activity;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Dialog;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.activity.EdgeToEdge;
@ -16,6 +25,7 @@ import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.bumptech.glide.Glide;
import com.example.androiddemo.R;
import com.example.androiddemo.databinding.ViewToastBinding;
import com.google.android.material.card.MaterialCardView;
@ -40,13 +50,24 @@ public class Toast extends AppCompatActivity {
// 屏幕的宽度/2
int width = getResources().getDisplayMetrics().widthPixels / 2;
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(0, 50, 0, 50);
// 50dp转px
DisplayMetrics metrics = getResources().getDisplayMetrics();
int v1 = (int) (50 * metrics.density + 0.5f);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, v1);
layoutParams.setMargins(0, 20, 0, 0);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
viewById.setOnClickListener(v -> linearLayout.addView(NewToast("测试"), 0, layoutParams));
Glide.with(this).load("https://pic2.zhimg.com/v2-febc9228d18886a29c68a66536279dfb_r.jpg?source=1940ef5c")
.into((ImageView) findViewById(R.id.imageView));
}
/**
* @noinspection SameParameterValue
*/
private View NewToast(String string) {
// 获取当前主题中的样式
ViewToastBinding binding = ViewToastBinding.inflate(getLayoutInflater());
@ -54,7 +75,7 @@ public class Toast extends AppCompatActivity {
MaterialCardView materialCardView = binding.getRoot();
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(materialCardView, "scaleY", 0.5f, 1f);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(materialCardView, "scaleY", 0.8f, 1f);
scaleYAnimator.setDuration(300); // 动画持续时间1秒
// 创建透明度渐变动画
@ -69,15 +90,17 @@ public class Toast extends AppCompatActivity {
// 设置动画结束后的回调
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(@NonNull Animator animation) {}
public void onAnimationStart(@NonNull Animator animation) {
}
@Override
public void onAnimationEnd(@NonNull Animator animation) {
// 缩小和淡出动画
// 创建透明度淡出动画
ObjectAnimator fadeOutAnimator = ObjectAnimator.ofFloat(materialCardView, "alpha", 1f, 0f);
fadeOutAnimator.setDuration(3000); // 动画持续时间1秒
fadeOutAnimator.start();
ObjectAnimator fadeOutAnimator = ObjectAnimator.ofFloat(materialCardView, "alpha", 1f, 0.1f);
fadeOutAnimator.setDuration(300); // 动画持续时间1秒
new Handler().postDelayed(fadeOutAnimator::start, 2000); // 2000 milliseconds delay
fadeOutAnimator.addListener(new Animator.AnimatorListener() {
@Override
@ -103,10 +126,12 @@ public class Toast extends AppCompatActivity {
}
@Override
public void onAnimationCancel(@NonNull Animator animation) {}
public void onAnimationCancel(@NonNull Animator animation) {
}
@Override
public void onAnimationRepeat(@NonNull Animator animation) {}
public void onAnimationRepeat(@NonNull Animator animation) {
}
});
return materialCardView;
}

View File

@ -21,6 +21,7 @@
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="9dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -6,7 +6,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.Toast">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"/>
<Button
android:id="@+id/TopToast"
android:layout_width="wrap_content"
@ -29,4 +33,5 @@
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/Widget.Material3.CardView.Elevated"
app:cardUseCompatPadding="true">
<Button
<TextView
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton"
android:text="Button"
android:layout_margin="6dp"/>
</LinearLayout>
android:layout_marginHorizontal="6dp"
android:layout_marginVertical="9dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="@style/TextAppearance.AppCompat.Body2"
android:text="@string/app_name" />
</com.google.android.material.card.MaterialCardView>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_height="50dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/Widget.Material3.CardView.Filled"
app:cardUseCompatPadding="true">
@ -9,10 +9,9 @@
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="9dp"
android:gravity="center"
android:text="Hello World!"
android:textStyle="bold"/>
</com.google.android.material.card.MaterialCardView>

View File

@ -6,4 +6,11 @@
</style>
<style name="Theme.AndroidDemo" parent="Base.Theme.AndroidDemo" />
<style name="MainActivity" parent="Theme.Material3.DayNight.NoActionBar">
<!-- 设置沉浸式状态栏-->
<item name="android:windowTranslucentNavigation">true</item>
<!-- Android 5.0+,设置状态栏全透明透明 -->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

1
blurview/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

30
blurview/build.gradle Normal file
View File

@ -0,0 +1,30 @@
plugins {
id 'com.android.library'
}
android {
compileSdk 31
namespace "net.center.blurview"
defaultConfig {
minSdk 19
targetSdk 31
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
compileOnly 'androidx.appcompat:appcompat:1.4.1'
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.center.blurview">
</manifest>

View File

@ -0,0 +1,977 @@
package net.center.blurview;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Build;
import android.util.AttributeSet;
import android.util.StateSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewTreeObserver;
import net.center.blurview.enu.BlurCorner;
import net.center.blurview.enu.BlurMode;
import net.center.blurview.impl.AndroidStockBlurImpl;
import net.center.blurview.impl.AndroidXBlurImpl;
import net.center.blurview.impl.BlurImpl;
import net.center.blurview.impl.EmptyBlurImpl;
import net.center.blurview.impl.SupportLibraryBlurImpl;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DimenRes;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
/**
* A realtime blurring overlay (like iOS UIVisualEffectView). Just put it above
* the view you want to blur and it doesn't have to be in the same ViewGroup
*/
public class ShapeBlurView extends View {
private Context mContext;
/**
* default 4
*/
private float mDownSampleFactor;
/**
* default #000000
*/
private int mOverlayColor;
/**
* default 10dp (0 < r <= 25)
*/
private float mBlurRadius;
public static final int DEFAULT_BORDER_COLOR = Color.WHITE;
private final BlurImpl mBlurImpl;
private boolean mDirty;
private Bitmap mBitmapToBlur, mBlurredBitmap;
private Canvas mBlurringCanvas;
private boolean mIsRendering;
private final Rect mRectSrc = new Rect();
private final RectF mRectFDst = new RectF();
/**
* mDecorView should be the root view of the activity (even if you are on a different window like a dialog)
*/
private View mDecorView;
/**
* If the view is on different root view (usually means we are on a PopupWindow),
* we need to manually call invalidate() in onPreDraw(), otherwise we will not be able to see the changes
*/
private boolean mDifferentRoot;
private static int RENDERING_COUNT;
private static int BLUR_IMPL;
private int blurMode = BlurMode.MODE_RECTANGLE;
private final Paint mBitmapPaint;
//圆形 相关
private float cx = 0, cy = 0, cRadius = 0;
//圆角相关
private static final float DEFAULT_RADIUS = 0f;
private final float[] mCornerRadii = new float[]{DEFAULT_RADIUS, DEFAULT_RADIUS, DEFAULT_RADIUS, DEFAULT_RADIUS};
private final Path cornerPath = new Path();
private float[] cornerRids;
//边框相关
private static final float DEFAULT_BORDER_WIDTH = 0f;
private final RectF mBorderRect = new RectF();
private final Paint mBorderPaint;
private float mBorderWidth = 0;
private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
private Matrix matrix = new Matrix();
private BitmapShader shader;
public ShapeBlurView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// provide your own by override getBlurImpl()
mBlurImpl = getBlurImpl();
try {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShapeBlurView);
mBlurRadius = a.getDimension(R.styleable.ShapeBlurView_blur_radius,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()));
mDownSampleFactor = a.getFloat(R.styleable.ShapeBlurView_blur_down_sample, 4);
mOverlayColor = a.getColor(R.styleable.ShapeBlurView_blur_overlay_color, 0x000000);
float cornerRadiusOverride =
a.getDimensionPixelSize(R.styleable.ShapeBlurView_blur_corner_radius, -1);
mCornerRadii[BlurCorner.TOP_LEFT] =
a.getDimensionPixelSize(R.styleable.ShapeBlurView_blur_corner_radius_top_left, -1);
mCornerRadii[BlurCorner.TOP_RIGHT] =
a.getDimensionPixelSize(R.styleable.ShapeBlurView_blur_corner_radius_top_right, -1);
mCornerRadii[BlurCorner.BOTTOM_RIGHT] =
a.getDimensionPixelSize(R.styleable.ShapeBlurView_blur_corner_radius_bottom_right, -1);
mCornerRadii[BlurCorner.BOTTOM_LEFT] =
a.getDimensionPixelSize(R.styleable.ShapeBlurView_blur_corner_radius_bottom_left, -1);
initCornerData(cornerRadiusOverride);
blurMode = a.getInt(R.styleable.ShapeBlurView_blur_mode, BlurMode.MODE_RECTANGLE);
mBorderWidth = a.getDimensionPixelSize(R.styleable.ShapeBlurView_blur_border_width, -1);
if (mBorderWidth < 0) {
mBorderWidth = DEFAULT_BORDER_WIDTH;
}
mBorderColor = a.getColorStateList(R.styleable.ShapeBlurView_blur_border_color);
if (mBorderColor == null) {
mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
}
a.recycle();
} catch (Exception e) {
e.printStackTrace();
}
mBitmapPaint = new Paint();
// mBitmapPaint.setStyle(Paint.Style.FILL);
mBitmapPaint.setAntiAlias(true);
mBorderPaint = new Paint();
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
mBorderPaint.setStrokeWidth(mBorderWidth);
// matrix = new Matrix();
}
private void initCornerData(float cornerRadiusOverride) {
boolean any = false;
for (int i = 0, len = mCornerRadii.length; i < len; i++) {
if (mCornerRadii[i] < 0) {
mCornerRadii[i] = 0f;
} else {
any = true;
}
}
if (!any) {
if (cornerRadiusOverride < 0) {
cornerRadiusOverride = DEFAULT_RADIUS;
}
for (int i = 0, len = mCornerRadii.length; i < len; i++) {
mCornerRadii[i] = cornerRadiusOverride;
}
}
initCornerRids();
}
private void initCornerRids() {
if (cornerRids == null) {
cornerRids = new float[]{mCornerRadii[BlurCorner.TOP_LEFT], mCornerRadii[BlurCorner.TOP_LEFT],
mCornerRadii[BlurCorner.TOP_RIGHT], mCornerRadii[BlurCorner.TOP_RIGHT],
mCornerRadii[BlurCorner.BOTTOM_RIGHT], mCornerRadii[BlurCorner.BOTTOM_RIGHT],
mCornerRadii[BlurCorner.BOTTOM_LEFT], mCornerRadii[BlurCorner.BOTTOM_LEFT]};
} else {
cornerRids[0] = mCornerRadii[BlurCorner.TOP_LEFT];
cornerRids[1] = mCornerRadii[BlurCorner.TOP_LEFT];
cornerRids[2] = mCornerRadii[BlurCorner.TOP_RIGHT];
cornerRids[3] = mCornerRadii[BlurCorner.TOP_RIGHT];
cornerRids[4] = mCornerRadii[BlurCorner.BOTTOM_RIGHT];
cornerRids[5] = mCornerRadii[BlurCorner.BOTTOM_RIGHT];
cornerRids[6] = mCornerRadii[BlurCorner.BOTTOM_LEFT];
cornerRids[7] = mCornerRadii[BlurCorner.BOTTOM_LEFT];
}
}
protected BlurImpl getBlurImpl() {
if (BLUR_IMPL == 0) {
// try to use stock impl first
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
try {
AndroidStockBlurImpl impl = new AndroidStockBlurImpl();
Bitmap bmp = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888);
impl.prepare(getContext(), bmp, 4);
impl.release();
bmp.recycle();
BLUR_IMPL = 3;
} catch (Throwable e) {
}
}
}
if (BLUR_IMPL == 0) {
try {
getClass().getClassLoader().loadClass("androidx.renderscript.RenderScript");
// initialize RenderScript to load jni impl
// may throw unsatisfied link error
AndroidXBlurImpl impl = new AndroidXBlurImpl();
Bitmap bmp = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888);
impl.prepare(getContext(), bmp, 4);
impl.release();
bmp.recycle();
BLUR_IMPL = 1;
} catch (Throwable e) {
// class not found or unsatisfied link
}
}
if (BLUR_IMPL == 0) {
try {
getClass().getClassLoader().loadClass("android.support.v8.renderscript.RenderScript");
// initialize RenderScript to load jni impl
// may throw unsatisfied link error
SupportLibraryBlurImpl impl = new SupportLibraryBlurImpl();
Bitmap bmp = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888);
impl.prepare(getContext(), bmp, 4);
impl.release();
bmp.recycle();
BLUR_IMPL = 2;
} catch (Throwable e) {
// class not found or unsatisfied link
}
}
if (BLUR_IMPL == 0) {
// fallback to empty impl, which doesn't have blur effect
BLUR_IMPL = -1;
}
switch (BLUR_IMPL) {
case 1:
return new AndroidXBlurImpl();
case 2:
return new SupportLibraryBlurImpl();
case 3:
return new AndroidStockBlurImpl();
default:
return new EmptyBlurImpl();
}
}
// public void setBlurRadius(@FloatRange(from = 0, to = 25) float radius) {
// if (mBlurRadius != radius) {
// mBlurRadius = radius;
// mDirty = true;
// invalidate();
// }
// }
// public void setDownSampleFactor(float factor) {
// if (factor <= 0) {
// throw new IllegalArgumentException("DownSample factor must be greater than 0.");
// }
// if (mDownSampleFactor != factor) {
// mDownSampleFactor = factor;
// // may also change blur radius
// mDirty = true;
// releaseBitmap();
// invalidate();
// }
// }
// public void setOverlayColor(int color) {
// if (mOverlayColor != color) {
// mOverlayColor = color;
// invalidate();
// }
// }
/**
* Set all the corner radii from a dimension resource id.
*
* @param resId dimension resource id of radii.
*/
// public void setCornerRadiusDimen(@DimenRes int resId) {
// float radius = getResources().getDimension(resId);
// setCornerRadius(radius, radius, radius, radius);
// }
/**
* Set the corner radius of a specific corner in px.
*
* @param radius
*/
// public void setCornerRadius(float radius) {
// setCornerRadius(radius, radius, radius, radius);
// }
/**
* Set the corner radius of a specific corner in px.
*/
// public void setCornerRadius(@BlurCorner int corner, float radius) {
// if (mCornerRadii[corner] == radius) {
// return;
// }
// mCornerRadii[corner] = radius;
// initCornerRids();
// invalidate();
// }
/**
* Set the corner radius of a specific corner in px.
*/
// public void setCornerRadius(float topLeft, float topRight, float bottomLeft, float bottomRight) {
// if (mCornerRadii[BlurCorner.TOP_LEFT] == topLeft
// && mCornerRadii[BlurCorner.TOP_RIGHT] == topRight
// && mCornerRadii[BlurCorner.BOTTOM_RIGHT] == bottomRight
// && mCornerRadii[BlurCorner.BOTTOM_LEFT] == bottomLeft) {
// return;
// }
// mCornerRadii[BlurCorner.TOP_LEFT] = topLeft;
// mCornerRadii[BlurCorner.TOP_RIGHT] = topRight;
// mCornerRadii[BlurCorner.BOTTOM_LEFT] = bottomLeft;
// mCornerRadii[BlurCorner.BOTTOM_RIGHT] = bottomRight;
// initCornerRids();
// invalidate();
// }
/**
* @return the largest corner radius.
*/
public float getCornerRadius() {
return getMaxCornerRadius();
}
/**
* @return the largest corner radius.
*/
public float getMaxCornerRadius() {
float maxRadius = 0;
for (float r : mCornerRadii) {
maxRadius = Math.max(r, maxRadius);
}
return maxRadius;
}
public float getBorderWidth() {
return mBorderWidth;
}
// public void setBorderWidth(@DimenRes int resId) {
// setBorderWidth(getResources().getDimension(resId));
// }
// public void setBorderWidth(float width) {
// if (mBorderWidth == width) {
// return;
// }
// mBorderWidth = width;
// invalidate();
// }
@ColorInt
public int getBorderColor() {
return mBorderColor.getDefaultColor();
}
// public void setBorderColor(@ColorInt int color) {
// setBorderColor(ColorStateList.valueOf(color));
// }
// public void setBorderColor(ColorStateList colors) {
// if (mBorderColor.equals(colors)) {
// return;
// }
// mBorderColor = (colors != null) ? colors : ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
// mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
// if (mBorderWidth > 0) {
// invalidate();
// }
// }
@BlurMode
public int getBlurMode() {
return this.blurMode;
}
// public void setBlurMode(@BlurMode int blurMode) {
// if (this.blurMode == blurMode) {
// return;
// }
// this.blurMode = blurMode;
// invalidate();
// }
private void releaseBitmap() {
if (mBitmapToBlur != null) {
mBitmapToBlur.recycle();
mBitmapToBlur = null;
}
if (mBlurredBitmap != null) {
mBlurredBitmap.recycle();
mBlurredBitmap = null;
}
if (matrix != null) {
matrix = null;
}
if (shader != null) {
shader = null;
}
mContext = null;
}
protected void release() {
releaseBitmap();
mBlurImpl.release();
}
protected boolean prepare() {
if (mBlurRadius == 0) {
release();
return false;
}
float downSampleFactor = mDownSampleFactor;
float radius = mBlurRadius / downSampleFactor;
if (radius > 25) {
downSampleFactor = downSampleFactor * radius / 25;
radius = 25;
}
final int width = getWidth();
final int height = getHeight();
int scaledWidth = Math.max(1, (int) (width / downSampleFactor));
int scaledHeight = Math.max(1, (int) (height / downSampleFactor));
boolean dirty = mDirty;
if (mBlurringCanvas == null || mBlurredBitmap == null
|| mBlurredBitmap.getWidth() != scaledWidth
|| mBlurredBitmap.getHeight() != scaledHeight) {
dirty = true;
releaseBitmap();
boolean r = false;
try {
mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (mBitmapToBlur == null) {
return false;
}
mBlurringCanvas = new Canvas(mBitmapToBlur);
mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (mBlurredBitmap == null) {
return false;
}
r = true;
} catch (OutOfMemoryError e) {
// Bitmap.createBitmap() may cause OOM error
// Simply ignore and fallback
} finally {
if (!r) {
release();
return false;
}
}
}
if (dirty) {
if (mBlurImpl.prepare(getContext(), mBitmapToBlur, radius)) {
mDirty = false;
} else {
return false;
}
}
return true;
}
protected void blur(Bitmap bitmapToBlur, Bitmap blurredBitmap) {
shader = new BitmapShader(blurredBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBlurImpl.blur(bitmapToBlur, blurredBitmap);
}
private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final int[] locations = new int[2];
Bitmap oldBmp = mBlurredBitmap;
View decor = mDecorView;
if (decor != null && isShown() && prepare()) {
boolean redrawBitmap = mBlurredBitmap != oldBmp;
oldBmp = null;
decor.getLocationOnScreen(locations);
int x = -locations[0];
int y = -locations[1];
getLocationOnScreen(locations);
x += locations[0];
y += locations[1];
// just erase transparent
mBitmapToBlur.eraseColor(mOverlayColor & 0xffffff);
int rc = mBlurringCanvas.save();
mIsRendering = true;
RENDERING_COUNT++;
try {
mBlurringCanvas.scale(1.f * mBitmapToBlur.getWidth() / getWidth(), 1.f * mBitmapToBlur.getHeight() / getHeight());
mBlurringCanvas.translate(-x, -y);
if (decor.getBackground() != null) {
decor.getBackground().draw(mBlurringCanvas);
}
decor.draw(mBlurringCanvas);
} catch (StopException e) {
} finally {
mIsRendering = false;
RENDERING_COUNT--;
mBlurringCanvas.restoreToCount(rc);
}
blur(mBitmapToBlur, mBlurredBitmap);
if (redrawBitmap || mDifferentRoot) {
invalidate();
}
}
return true;
}
};
protected View getActivityDecorView() {
Context ctx = getContext();
for (int i = 0; i < 4 && !(ctx instanceof Activity) && ctx instanceof ContextWrapper; i++) {
ctx = ((ContextWrapper) ctx).getBaseContext();
}
if (ctx instanceof Activity) {
return ((Activity) ctx).getWindow().getDecorView();
} else {
return null;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mDecorView = getActivityDecorView();
if (mDecorView != null) {
mDecorView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
mDifferentRoot = mDecorView.getRootView() != getRootView();
if (mDifferentRoot) {
mDecorView.postInvalidate();
}
} else {
mDifferentRoot = false;
}
}
@Override
protected void onDetachedFromWindow() {
if (mDecorView != null) {
mDecorView.getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
}
release();
super.onDetachedFromWindow();
}
@Override
public void draw(Canvas canvas) {
if (mIsRendering) {
// Quit here, don't draw views above me
throw STOP_EXCEPTION;
} else if (RENDERING_COUNT > 0) {
// Doesn't support blurview overlap on another blurview
} else {
super.draw(canvas);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBlurredBitmap(canvas, mBlurredBitmap, mOverlayColor);
}
/**
* Custom draw the blurred bitmap and color to define your own shape
*
* @param canvas
* @param blurBitmap
* @param overlayColor
*/
protected void drawBlurredBitmap(Canvas canvas, Bitmap blurBitmap, int overlayColor) {
if (blurBitmap != null) {
if (blurMode == BlurMode.MODE_CIRCLE) {
drawCircleRectBitmap(canvas, blurBitmap, overlayColor);
} else if (blurMode == BlurMode.MODE_OVAL) {
drawOvalRectBitmap(canvas, blurBitmap, overlayColor);
} else {
drawRoundRectBitmap(canvas, blurBitmap, overlayColor);
}
}
}
/**
* 默认或者画矩形可带圆角
*
* @param canvas
* @param blurBitmap
* @param overlayColor
*/
private void drawRoundRectBitmap(Canvas canvas, Bitmap blurBitmap, int overlayColor) {
try {
//圆角的半径依次为左上角xy半径右上角右下角左下角
mRectFDst.right = getWidth();
mRectFDst.bottom = getHeight();
/*向路径中添加圆角矩形。radii数组定义圆角矩形的四个圆角的x,y半径。radii长度必须为8*/
//Path.Direction.CWclockwise 沿顺时针方向绘制,Path.Direction.CCWcounter-clockwise 沿逆时针方向绘制
cornerPath.addRoundRect(mRectFDst, cornerRids, Path.Direction.CW);
cornerPath.close();
canvas.clipPath(cornerPath);
mRectSrc.right = blurBitmap.getWidth();
mRectSrc.bottom = blurBitmap.getHeight();
canvas.drawBitmap(blurBitmap, mRectSrc, mRectFDst, null);
mBitmapPaint.setColor(overlayColor);
canvas.drawRect(mRectFDst, mBitmapPaint);
if (mBorderWidth > 0) {
//目前没找到合适方式
mBorderPaint.setStrokeWidth(mBorderWidth * 2);
canvas.drawPath(cornerPath, mBorderPaint);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 画椭圆如果宽高一样则为圆形
*
* @param canvas
* @param blurBitmap
* @param overlayColor
*/
private void drawOvalRectBitmap(Canvas canvas, Bitmap blurBitmap, int overlayColor) {
try {
mRectFDst.right = getWidth();
mRectFDst.bottom = getHeight();
mBitmapPaint.reset();
mBitmapPaint.setAntiAlias(true);
if (shader == null) {
shader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
if (matrix == null) {
matrix = new Matrix();
matrix.postScale(mRectFDst.width() / blurBitmap.getWidth(), mRectFDst.height() / blurBitmap.getHeight());
}
shader.setLocalMatrix(matrix);
mBitmapPaint.setShader(shader);
canvas.drawOval(mRectFDst, mBitmapPaint);
mBitmapPaint.reset();
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setColor(overlayColor);
canvas.drawOval(mRectFDst, mBitmapPaint);
if (mBorderWidth > 0) {
mBorderRect.set(mRectFDst);
mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
canvas.drawOval(mBorderRect, mBorderPaint);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 画圆形以宽高最小的为半径
*
* @param canvas
* @param blurBitmap
* @param overlayColor
*/
private void drawCircleRectBitmap(Canvas canvas, Bitmap blurBitmap, int overlayColor) {
try {
mRectFDst.right = getMeasuredWidth();
mRectFDst.bottom = getMeasuredHeight();
mRectSrc.right = blurBitmap.getWidth();
mRectSrc.bottom = blurBitmap.getHeight();
mBitmapPaint.reset();
mBitmapPaint.setAntiAlias(true);
if (shader == null) {
shader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
if (matrix == null) {
matrix = new Matrix();
matrix.postScale(mRectFDst.width() / mRectSrc.width(), mRectFDst.height() / mRectSrc.height());
}
shader.setLocalMatrix(matrix);
mBitmapPaint.setShader(shader);
//前面Scale故判断以哪一个来取中心点和半径
if (mRectFDst.width() >= mRectSrc.width()) {
cx = mRectFDst.width() / 2;
cy = mRectFDst.height() / 2;
//取宽高最小的为半径
cRadius = Math.min(mRectFDst.width(), mRectFDst.height()) / 2;
mBorderRect.set(mRectFDst);
} else {
cx = mRectSrc.width() / 2f;
cy = mRectSrc.height() / 2f;
cRadius = Math.min(mRectSrc.width(), mRectSrc.height()) / 2f;
mBorderRect.set(mRectSrc);
}
canvas.drawCircle(cx, cy, cRadius, mBitmapPaint);
mBitmapPaint.reset();
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setColor(overlayColor);
canvas.drawCircle(cx, cy, cRadius, mBitmapPaint);
//使用宽高相等的椭圆为圆形来画边框
if (mBorderWidth > 0) {
if (mBorderRect.width() > mBorderRect.height()) {
//原本宽大于高圆是以中心点为圆心和高的一半为半径椭圆区域是以初始00为开始故整体向右移动差值
float dif = Math.abs(mBorderRect.height() - mBorderRect.width()) / 2;
mBorderRect.left = dif;
mBorderRect.right = Math.min(mBorderRect.width(), mBorderRect.height()) + dif;
mBorderRect.bottom = Math.min(mBorderRect.width(), mBorderRect.height());
} else if (mBorderRect.width() < mBorderRect.height()) {
//原本高大于宽圆是以中心点为圆心和宽的一半为半径椭圆区域是以初始00为开始故整体向下移动差值
float dif = Math.abs(mBorderRect.height() - mBorderRect.width()) / 2;
mBorderRect.top = dif;
mBorderRect.right = Math.min(mBorderRect.width(), mBorderRect.height());
mBorderRect.bottom = Math.min(mBorderRect.width(), mBorderRect.height()) + dif;
} else {
//如果快高相同则不需要偏移椭圆画出来就是圆
mBorderRect.right = Math.min(mBorderRect.width(), mBorderRect.height());
mBorderRect.bottom = Math.min(mBorderRect.width(), mBorderRect.height());
}
mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2);
canvas.drawOval(mBorderRect, mBorderPaint);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* dp转px
*
* @param dpValue dp值
* @return px值
*/
public int dp2px(final float dpValue) {
final float scale = mContext.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public @NonNull
int[] getState() {
return StateSet.WILD_CARD;
}
private static class StopException extends RuntimeException {
}
private static StopException STOP_EXCEPTION = new StopException();
/**
* 传入构造器避免传统的设置一个参数调用一次invalidate()重新绘制
*
* @return
*/
public void refreshView(Builder builder) {
boolean isInvalidate = false;
if (builder == null) {
return;
}
if (builder.blurMode != -1 && this.blurMode != builder.blurMode) {
this.blurMode = builder.blurMode;
isInvalidate = true;
}
if (builder.mBorderColor != null && !mBorderColor.equals(builder.mBorderColor)) {
this.mBorderColor = builder.mBorderColor;
mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
if (mBorderWidth > 0) {
isInvalidate = true;
}
}
if (builder.mBorderWidth > 0) {
mBorderWidth = builder.mBorderWidth;
mBorderPaint.setStrokeWidth(mBorderWidth);
isInvalidate = true;
}
if (mCornerRadii[BlurCorner.TOP_LEFT] != builder.mCornerRadii[BlurCorner.TOP_LEFT]
|| mCornerRadii[BlurCorner.TOP_RIGHT] != builder.mCornerRadii[BlurCorner.TOP_RIGHT]
|| mCornerRadii[BlurCorner.BOTTOM_RIGHT] != builder.mCornerRadii[BlurCorner.BOTTOM_RIGHT]
|| mCornerRadii[BlurCorner.BOTTOM_LEFT] != builder.mCornerRadii[BlurCorner.BOTTOM_LEFT]) {
mCornerRadii[BlurCorner.TOP_LEFT] = builder.mCornerRadii[BlurCorner.TOP_LEFT];
mCornerRadii[BlurCorner.TOP_RIGHT] = builder.mCornerRadii[BlurCorner.TOP_RIGHT];
mCornerRadii[BlurCorner.BOTTOM_LEFT] = builder.mCornerRadii[BlurCorner.BOTTOM_LEFT];
mCornerRadii[BlurCorner.BOTTOM_RIGHT] = builder.mCornerRadii[BlurCorner.BOTTOM_RIGHT];
isInvalidate = true;
initCornerRids();
}
if (builder.mOverlayColor != -1 && mOverlayColor != builder.mOverlayColor) {
mOverlayColor = builder.mOverlayColor;
isInvalidate = true;
}
if (builder.mBlurRadius > 0 && mBlurRadius != builder.mBlurRadius) {
mBlurRadius = builder.mBlurRadius;
mDirty = true;
isInvalidate = true;
}
if (builder.mDownSampleFactor > 0 && mDownSampleFactor != builder.mDownSampleFactor) {
mDownSampleFactor = builder.mDownSampleFactor;
mDirty = true;
isInvalidate = true;
releaseBitmap();
}
if (isInvalidate) {
invalidate();
}
}
public static class Builder {
// default 4
private float mDownSampleFactor = -1;
// default #aaffffff
private int mOverlayColor = -1;
// default 10dp (0 < r <= 25)
private float mBlurRadius = -1;
private float mBorderWidth = -1;
private ColorStateList mBorderColor = null;
private int blurMode = -1;
private final float[] mCornerRadii = new float[]{0f, 0f, 0f, 0f};
private Context mContext;
private Builder(Context context) {
mContext = context.getApplicationContext();
}
/**
* 模糊半径
*
* @param radius 0~25
* @return
*/
public Builder setBlurRadius(@FloatRange(from = 0, to = 25) float radius) {
mBlurRadius = radius;
return this;
}
/**
* 采样率
*
* @param factor
* @return
*/
public Builder setDownSampleFactor(float factor) {
if (factor <= 0) {
throw new IllegalArgumentException("DownSample factor must be greater than 0.");
}
mDownSampleFactor = factor;
return this;
}
/**
* 蒙层颜色
*
* @param color
* @return
*/
public Builder setOverlayColor(int color) {
mOverlayColor = color;
return this;
}
/**
* Set the corner radius of a specific corner in px.
* 设置圆角 圆形椭圆无效
*
* @param corner 枚举类型 对应4个角
* @param radius 角半径幅度
* @return
*/
public Builder setCornerRadius(@BlurCorner int corner, float radius) {
mCornerRadii[corner] = radius;
return this;
}
/**
* Set all the corner radii from a dimension resource id.
* 设置圆角 圆形椭圆无效
*
* @param resId dimension resource id of radii.
*/
public Builder setCornerRadiusDimen(@DimenRes int resId) {
float radius = mContext.getResources().getDimension(resId);
return setCornerRadius(radius, radius, radius, radius);
}
/**
* Set the corner radius of a specific corner in px.
* 设置圆角 圆形椭圆无效
*
* @param radius 4个角同值
*/
public Builder setCornerRadius(float radius) {
return setCornerRadius(radius, radius, radius, radius);
}
/**
* Set the corner radius of a specific corner in px.
* 设置圆角 圆形椭圆无效
*/
public Builder setCornerRadius(float topLeft, float topRight, float bottomLeft, float bottomRight) {
mCornerRadii[BlurCorner.TOP_LEFT] = topLeft;
mCornerRadii[BlurCorner.TOP_RIGHT] = topRight;
mCornerRadii[BlurCorner.BOTTOM_LEFT] = bottomLeft;
mCornerRadii[BlurCorner.BOTTOM_RIGHT] = bottomRight;
return this;
}
/**
* 设置边框的宽度
*
* @param resId
* @return
*/
public Builder setBorderWidth(@DimenRes int resId) {
return setBorderWidth(mContext.getResources().getDimension(resId));
}
/**
* 设置边框的宽度
*
* @param width 转px值
* @return
*/
public Builder setBorderWidth(float width) {
mBorderWidth = width;
return this;
}
/**
* 设置边框颜色
*
* @param color R.color.xxxx
* @return
*/
public Builder setBorderColor(@ColorRes int color) {
return setBorderColor(ColorStateList.valueOf(ContextCompat.getColor(mContext, color)));
}
// public Builder setBorderColor(@ColorInt int color) {
// return setBorderColor(ColorStateList.valueOf(color));
// }
public Builder setBorderColor(ColorStateList colors) {
mBorderColor = (colors != null) ? colors : ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
return this;
}
/**
* 设置高斯模糊的类型
*
* @param blurMode BlurMode枚举值支持圆方形椭圆宽高相等椭圆为圆
* @return
*/
public Builder setBlurMode(@BlurMode int blurMode) {
this.blurMode = blurMode;
return this;
}
}
/**
* 建造者模式避免设置一个参数调用一次重新绘制
*
* @return
*/
public static Builder build(Context context) {
return new Builder(context);
}
}

View File

@ -0,0 +1,21 @@
package net.center.blurview.enu;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author center
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
BlurCorner.TOP_LEFT, BlurCorner.TOP_RIGHT,
BlurCorner.BOTTOM_LEFT, BlurCorner.BOTTOM_RIGHT
})
public @interface BlurCorner {
int TOP_LEFT = 0;
int TOP_RIGHT = 1;
int BOTTOM_RIGHT = 2;
int BOTTOM_LEFT = 3;
}

View File

@ -0,0 +1,20 @@
package net.center.blurview.enu;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author center
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
BlurMode.MODE_RECTANGLE, BlurMode.MODE_CIRCLE,
BlurMode.MODE_OVAL
})
public @interface BlurMode {
int MODE_RECTANGLE = 0;
int MODE_CIRCLE = 1;
int MODE_OVAL = 2;
}

View File

@ -0,0 +1,81 @@
package net.center.blurview.impl;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class AndroidStockBlurImpl implements BlurImpl {
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
@Override
public boolean prepare(Context context, Bitmap buffer, float radius) {
if (mRenderScript == null) {
try {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
} catch (android.renderscript.RSRuntimeException e) {
if (isDebug(context)) {
throw e;
} else {
// In release mode, just ignore
release();
return false;
}
}
}
mBlurScript.setRadius(radius);
mBlurInput = Allocation.createFromBitmap(mRenderScript, buffer,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
return true;
}
@Override
public void release() {
if (mBlurInput != null) {
mBlurInput.destroy();
mBlurInput = null;
}
if (mBlurOutput != null) {
mBlurOutput.destroy();
mBlurOutput = null;
}
if (mBlurScript != null) {
mBlurScript.destroy();
mBlurScript = null;
}
if (mRenderScript != null) {
mRenderScript.destroy();
mRenderScript = null;
}
}
@Override
public void blur(Bitmap input, Bitmap output) {
mBlurInput.copyFrom(input);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(output);
}
// android:debuggable="true" in AndroidManifest.xml (auto set by build tool)
static Boolean DEBUG = null;
static boolean isDebug(Context ctx) {
if (DEBUG == null && ctx != null) {
DEBUG = (ctx.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
return DEBUG.equals(Boolean.TRUE);
}
}

View File

@ -0,0 +1,78 @@
package net.center.blurview.impl;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
public class AndroidXBlurImpl implements BlurImpl {
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
@Override
public boolean prepare(Context context, Bitmap buffer, float radius) {
if (mRenderScript == null) {
try {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
} catch (android.renderscript.RSRuntimeException e) {
if (isDebug(context)) {
throw e;
} else {
// In release mode, just ignore
release();
return false;
}
}
}
mBlurScript.setRadius(radius);
mBlurInput = Allocation.createFromBitmap(mRenderScript, buffer,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
return true;
}
@Override
public void release() {
if (mBlurInput != null) {
mBlurInput.destroy();
mBlurInput = null;
}
if (mBlurOutput != null) {
mBlurOutput.destroy();
mBlurOutput = null;
}
if (mBlurScript != null) {
mBlurScript.destroy();
mBlurScript = null;
}
if (mRenderScript != null) {
mRenderScript.destroy();
mRenderScript = null;
}
}
@Override
public void blur(Bitmap input, Bitmap output) {
mBlurInput.copyFrom(input);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(output);
}
// android:debuggable="true" in AndroidManifest.xml (auto set by build tool)
static Boolean DEBUG = null;
static boolean isDebug(Context ctx) {
if (DEBUG == null && ctx != null) {
DEBUG = (ctx.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
return DEBUG.equals(Boolean.TRUE);
}
}

View File

@ -0,0 +1,14 @@
package net.center.blurview.impl;
import android.content.Context;
import android.graphics.Bitmap;
public interface BlurImpl {
boolean prepare(Context context, Bitmap buffer, float radius);
void release();
void blur(Bitmap input, Bitmap output);
}

View File

@ -0,0 +1,21 @@
package net.center.blurview.impl;
import android.content.Context;
import android.graphics.Bitmap;
public class EmptyBlurImpl implements BlurImpl {
@Override
public boolean prepare(Context context, Bitmap buffer, float radius) {
return false;
}
@Override
public void release() {
}
@Override
public void blur(Bitmap input, Bitmap output) {
}
}

View File

@ -0,0 +1,78 @@
package net.center.blurview.impl;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
public class SupportLibraryBlurImpl implements BlurImpl {
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
@Override
public boolean prepare(Context context, Bitmap buffer, float radius) {
if (mRenderScript == null) {
try {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
} catch (android.renderscript.RSRuntimeException e) {
if (isDebug(context)) {
throw e;
} else {
// In release mode, just ignore
release();
return false;
}
}
}
mBlurScript.setRadius(radius);
mBlurInput = Allocation.createFromBitmap(mRenderScript, buffer,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
return true;
}
@Override
public void release() {
if (mBlurInput != null) {
mBlurInput.destroy();
mBlurInput = null;
}
if (mBlurOutput != null) {
mBlurOutput.destroy();
mBlurOutput = null;
}
if (mBlurScript != null) {
mBlurScript.destroy();
mBlurScript = null;
}
if (mRenderScript != null) {
mRenderScript.destroy();
mRenderScript = null;
}
}
@Override
public void blur(Bitmap input, Bitmap output) {
mBlurInput.copyFrom(input);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(output);
}
// android:debuggable="true" in AndroidManifest.xml (auto set by build tool)
static Boolean DEBUG = null;
static boolean isDebug(Context ctx) {
if (DEBUG == null && ctx != null) {
DEBUG = (ctx.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
return DEBUG.equals(Boolean.TRUE);
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ShapeBlurView">
<attr name="blur_radius" format="dimension" />
<attr name="blur_down_sample" format="float" />
<attr name="blur_overlay_color" format="color" />
<attr name="blur_corner_radius" format="dimension" />
<attr name="blur_corner_radius_top_left" format="dimension" />
<attr name="blur_corner_radius_top_right" format="dimension" />
<attr name="blur_corner_radius_bottom_left" format="dimension" />
<attr name="blur_corner_radius_bottom_right" format="dimension" />
<attr name="blur_border_width" format="dimension" />
<attr name="blur_border_color" format="color" />
<attr name="blur_mode">
<enum name="rectangle" value="0" />
<enum name="circle" value="1" />
<enum name="oval" value="2" />
</attr>
</declare-styleable>
</resources>

View File

@ -1,5 +1,6 @@
[versions]
agp = "8.4.1"
glide = "4.16.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
@ -9,6 +10,7 @@ activity = "1.8.0"
constraintlayout = "2.1.4"
[libraries]
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }

View File

@ -21,3 +21,4 @@ dependencyResolutionManagement {
rootProject.name = "AndroidDemo"
include ':app'
//, ':blurview'