项目的新launcher设计带有高斯模糊效果,百度到的方法大多数是对一张固定的图片实现毛玻璃效果。而launcher是fragment+viewpager的结构,无法满足滑动时,对背景动态高斯模糊的效果。于是想到在viewpager的滑动监听onPageScrolled方法中对一块区域进行截图,再对此图片进行高斯模糊效果处理,最后设置成一个view的背景。代码实现方法如下:
/**
* viewpager滑动监听
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
initView();
int positionOffsetPixels1 = positionOffsetPixels;
int count = positionOffsetPixels - OffsetPixel;
System.err.println("onPageScrolled______" + OffsetPixel);
if (count > 10) {
OffsetPixel = count;
new Thread() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = UPDATE_BACKGROUND;
try {
msg.obj = getBitmap(naviView);
} catch (Exception e) {
e.printStackTrace();
}
mHandler.sendMessage(msg);
}
}.start();
}
}
/**
* 高斯模糊处理
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case UPDATE_BACKGROUND:
try {
naviView.setBackground(new BitmapDrawable((Bitmap) msg.obj));
fmView.setBackground(new BitmapDrawable(getBitmap(fmView)));
weChat.setBackground(new BitmapDrawable(getBitmap(weChat)));
Bitmap source = BitmapFactory.decodeResource(getResources(), R.id.navi);
//对Bitmap进行模糊化处理,第一个参数代表原始Bitmap,第二个参数代表模糊半径
//半径越大,处理后的图片就越模糊
Bitmap bm = NativeStackBlur.process(source, 80);
mImageView.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
};
然而,这样做效果太差,滑动时不仅卡顿,部分区域还白屏,于是只能放弃这一种做法。这时,我看到了一博客介绍了一个第三方库http://www.oddjack.com/?certs=mmin18/RealtimeBlurView,效果也是正是我想要的,写了一个demo试了试,完美。具体做法:
1.在app中的build.gradle中依赖:
compile 'com.github.mmin18:realtimeblurview:1.0.3'
2.添加渲染支持:
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 17
targetSdkVersion 26
versionCode 1
versionName "1.0"
renderscriptTargetApi 26
renderscriptSupportModeEnabled true //开启渲染模式支持 一定要加
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
3.在布局中,将自定义控件放在最下层,这样的效果只对背景进行高斯模糊效果处理
<com.github.mmin18.widget.RealtimeBlurView
android:layout_width="160dp"
android:layout_height="match_parent"
app:realtimeBlurRadius="20dp" //这个自定义属性代表虚化程度 50 最高,0 最低
app:realtimeOverlayColor="#1000" /> //改变透明度 1-9
也可以不依赖第三方库,自己写一个自定义控件,代码如下:
public class RealtimeBlurView extends View {
private float mDownsampleFactor; // default 4
private int mOverlayColor; // default #aaffffff
private float mBlurRadius; // default 10dp (0 < r <= 25)
private boolean mDirty;
private Bitmap mBitmapToBlur, mBlurredBitmap;
private Canvas mBlurringCanvas;
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
private boolean mIsRendering;
private Paint mPaint;
private final Rect mRectSrc = new Rect(), mRectDst = new Rect();
// 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;
public RealtimeBlurView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RealtimeBlurView);
mBlurRadius = a.getDimension(R.styleable.RealtimeBlurView_realtimeBlurRadius,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()));
mDownsampleFactor = a.getFloat(R.styleable.RealtimeBlurView_realtimeDownsampleFactor, 4);
mOverlayColor = a.getColor(R.styleable.RealtimeBlurView_realtimeOverlayColor, 0xAAFFFFFF);
a.recycle();
mPaint = new Paint();
}
public void setBlurRadius(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;
mDirty = true; // may also change blur radius
releaseBitmap();
invalidate();
}
}
public void setOverlayColor(int color) {
if (mOverlayColor != color) {
mOverlayColor = color;
invalidate();
}
}
private void releaseBitmap() {
if (mBlurInput != null) {
mBlurInput.destroy();
mBlurInput = null;
}
if (mBlurOutput != null) {
mBlurOutput.destroy();
mBlurOutput = null;
}
if (mBitmapToBlur != null) {
mBitmapToBlur.recycle();
mBitmapToBlur = null;
}
if (mBlurredBitmap != null) {
mBlurredBitmap.recycle();
mBlurredBitmap = null;
}
}
private void releaseScript() {
if (mRenderScript != null) {
mRenderScript.destroy();
mRenderScript = null;
}
if (mBlurScript != null) {
mBlurScript.destroy();
mBlurScript = null;
}
}
protected void release() {
releaseBitmap();
releaseScript();
}
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;
}
if (mDirty || mRenderScript == null) {
if (mRenderScript == null) {
try {
mRenderScript = RenderScript.create(getContext());
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
} catch (android.support.v8.renderscript.RSRuntimeException e) {
if (isDebug(getContext())) {
if (e.getMessage() != null && e.getMessage().startsWith("Error loading RS jni library: java.lang.UnsatisfiedLinkError:")) {
throw new RuntimeException("Error loading RS jni library, Upgrade buildToolsVersion=\"24.0.2\" or higher may solve this issue");
} else {
throw e;
}
} else {
// In release mode, just ignore
releaseScript();
return false;
}
}
}
mBlurScript.setRadius(radius);
mDirty = false;
}
final int width = getWidth();
final int height = getHeight();
int scaledWidth = Math.max(1, (int) (width / downsampleFactor));
int scaledHeight = Math.max(1, (int) (height / downsampleFactor));
if (mBlurringCanvas == null || mBlurredBitmap == null
|| mBlurredBitmap.getWidth() != scaledWidth
|| mBlurredBitmap.getHeight() != scaledHeight) {
releaseBitmap();
boolean r = false;
try {
mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (mBitmapToBlur == null) {
return false;
}
mBlurringCanvas = new Canvas(mBitmapToBlur);
mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
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) {
releaseBitmap();
return false;
}
}
}
return true;
}
protected void blur(Bitmap bitmapToBlur, Bitmap blurredBitmap) {
mBlurInput.copyFrom(bitmapToBlur);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(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 != null && !(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 blurredBitmap
* @param overlayColor
*/
protected void drawBlurredBitmap(Canvas canvas, Bitmap blurredBitmap, int overlayColor) {
if (blurredBitmap != null) {
mRectSrc.right = blurredBitmap.getWidth();
mRectSrc.bottom = blurredBitmap.getHeight();
mRectDst.right = getWidth();
mRectDst.bottom = getHeight();
canvas.drawBitmap(blurredBitmap, mRectSrc, mRectDst, null);
}
mPaint.setColor(overlayColor);
canvas.drawRect(mRectDst, mPaint);
}
private static class StopException extends RuntimeException {
}
private static StopException STOP_EXCEPTION = new StopException();
static {
try {
RealtimeBlurView.class.getClassLoader().loadClass("android.support.v8.renderscript.RenderScript");
} catch (ClassNotFoundException e) {
throw new RuntimeException("RenderScript support not enabled. Add \"android { defaultConfig { renderscriptSupportModeEnabled true }}\" in your build.gradle");
}
}
// 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 == Boolean.TRUE;
}
}
PS:demo上程序运行没问题,把代码移植到项目工程里,运行时报错:so文件引用重复 ,解决了还是不行,后来费了老大劲才发现是编译时,系统库下面没有librsjni.so这个so文件。