前言
逛instagram的時候,偶然發現,instagram的對話框設計的很有意思,如下圖:
它的dialog的背景竟然是毛玻璃效果的,在我看來真漂亮,恩,對話框和迪麗熱巴都漂亮。看到這麼好的效果,當然就要開始搞事情了,自己動手實作差不多的效果。最終的實作效果如下圖:
分别實作了對話框背景的虛化和手動調節虛化程度。
實作方法對比
最開始想要實作毛玻璃效果時,我是一臉懵逼的,不知道如何下手。幸虧,有萬能的google。搜尋之後發現常見的實作方法有4種,分别是:
renderscript
java算法
ndk算法
opengl
處理一整張圖檔這麼大計算量的工作,opengl的性能最好,而用java實作肯定是最差的了。而renderscript和ndk的性能相當,但是你懂得,ndk和opengl我無可奈何,綜合考慮,renderscript應該是最适合的。
但并不是說renderscript就是完全沒有問題的:
模糊半徑(radius)越大,性能要求越高,模糊半徑不能超過25,是以并不能得到模糊度非常高的圖檔。
scriptintrinsicblur在api 17時才被引入,如果需要在android 4.2以下的裝置上實作,就需要引入renderscript support library,當然,安裝包體積會相應的增大。
renderscript實作
首先在app目錄下build.gradle檔案中添加如下代碼:
defaultconfig {
applicationid "io.github.marktony.gaussianblur"
minsdkversion 19
targetsdkversion 25
versioncode 1
versionname "1.0"
renderscripttargetapi 19
renderscriptsupportmodeenabled true
}
renderscriptintrinsics提供了一些可以幫助我們快速實作各種圖檔處理的操作類,例如,scriptintrinsicblur,可以簡單高效實作 高斯模糊效果。
package io.github.marktony.gaussianblur;
import android.content.context;
import android.graphics.bitmap;
import android.support.annotation.intrange;
import android.support.annotation.nonnull;
import android.support.v8.renderscript.allocation;
import android.support.v8.renderscript.element;
import android.support.v8.renderscript.renderscript;
import android.support.v8.renderscript.scriptintrinsicblur;
public class renderscriptgaussianblur {
private renderscript renderscript;
public renderscriptgaussianblur(@nonnull context context) {
this.renderscript = renderscript.create(context);
}
public bitmap gaussianblur(@intrange(from = 1, to = 25) int radius, bitmap original) {
allocation input = allocation.createfrombitmap(renderscript, original);
allocation output = allocation.createtyped(renderscript, input.gettype());
scriptintrinsicblur scriptintrinsicblur = scriptintrinsicblur.create(renderscript, element.u8_4(renderscript));
scriptintrinsicblur.setradius(radius);
scriptintrinsicblur.setinput(input);
scriptintrinsicblur.foreach(output);
output.copyto(original);
return original;
}
然後就可以直接使用renderscriptgaussianblur,愉快地根據seekbar的值,實作不同程度的模糊了。
import android.content.dialoginterface;
import android.graphics.bitmapfactory;
import android.support.v7.app.alertdialog;
import android.support.v7.app.appcompatactivity;
import android.os.bundle;
import android.util.log;
import android.view.view;
import android.view.window;
import android.view.windowmanager;
import android.widget.framelayout;
import android.widget.imageview;
import android.widget.linearlayout;
import android.widget.seekbar;
import android.widget.textview;
public class mainactivity extends appcompatactivity {
private imageview imageview;
private imageview container;
private linearlayout layout;
private textview textviewprogress;
private renderscriptgaussianblur blur;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
imageview = (imageview) findviewbyid(r.id.imageview);
container = (imageview) findviewbyid(r.id.container);
container.setvisibility(view.gone);
layout = (linearlayout) findviewbyid(r.id.layout);
layout.setvisibility(view.visible);
seekbar seekbar = (seekbar) findviewbyid(r.id.seekbar);
textviewprogress = (textview) findviewbyid(r.id.textviewprogress);
textview textviewdialog = (textview) findviewbyid(r.id.textviewdialog);
blur = new renderscriptgaussianblur(mainactivity.this);
seekbar.setmax(25);
seekbar.setonseekbarchangelistener(new seekbar.onseekbarchangelistener() {
@override
public void onprogresschanged(seekbar seekbar, int progress, boolean fromuser) {
textviewprogress.settext(string.valueof(progress));
}
public void onstarttrackingtouch(seekbar seekbar) {
public void onstoptrackingtouch(seekbar seekbar) {
int radius = seekbar.getprogress();
if (radius < 1) {
radius = 1;
}
bitmap bitmap = bitmapfactory.decoderesource(getresources(), r.drawable.image);
imageview.setimagebitmap(blur.gaussianblur(radius, bitmap));
});
textviewdialog.setonclicklistener(new view.onclicklistener() {
public void onclick(view v) {
container.setvisibility(view.visible);
layout.setdrawingcacheenabled(true);
layout.setdrawingcachequality(view.drawing_cache_quality_low);
bitmap bitmap = layout.getdrawingcache();
container.setimagebitmap(blur.gaussianblur(25, bitmap));
layout.setvisibility(view.invisible);
alertdialog dialog = new alertdialog.builder(mainactivity.this).create();
dialog.settitle("title");
dialog.setmessage("message");
dialog.setbutton(dialoginterface.button_positive, "ok", new dialoginterface.onclicklistener() {
@override
public void onclick(dialoginterface dialog, int which) {
dialog.dismiss();
}
});
dialog.setbutton(dialoginterface.button_negative, "cancel", new dialoginterface.onclicklistener() {
dialog.setoncancellistener(new dialoginterface.oncancellistener() {
public void oncancel(dialoginterface dialog) {
container.setvisibility(view.gone);
layout.setvisibility(view.visible);
dialog.show();
在代碼裡做了一些view的可見性的操作,比較簡單,相信你能看懂的。和instagram中dialog的實作有一點不同的是,我沒有截取整個頁面的bitmap,隻是截取了actionbar下的内容,如果一定要實作一樣的效果,調整一下頁面的布局就可以了。這裡不多說了。
是不是很簡單呢?
輪子
除了renderscript外,還有一些優秀的輪子:
<a href="https://github.com/500px/500px-android-blur">500px-android-blur</a>
<a href="https://github.com/wasabeef/blurry">blurry</a>
<a href="https://github.com/kikoso/android-stackblur">android-stackblur</a>
blurtestandroid對不同類庫的實作方式、采取的算法和所耗費的時間做了統計和比較,你也可以下載下傳它的demo app,自行測試。
作者:tonny
來源:51cto