preface
When browsing Instagram, I happened to find that the dialog box of Instagram is very interesting, as shown below:
The background of its dialog is actually frosted glass. It looks beautiful to me. Well, the dialog and Dilreba are both beautiful 😂. Seeing such good results, of course, you have to start doing things yourself to achieve similar results. The final result is as follows:
The background of the dialog box is blurred and the degree of blur is manually adjusted.
Comparison of implementation methods
When I first tried to achieve the frosted glass effect, I didn’t know how to do it. Thanks to the almighty Google. After searching, it is found that there are four common implementation methods, which are:
- RenderScript
- Java algorithm
- The NDK algorithm
- openGL
OpenGL has the best performance, and Java is definitely the worst. RenderScript and NDK are comparable in performance, but you know, NDK and openGL I can’t do anything about it, so RenderScript is probably the best fit.
But that’s not to say RenderScript is completely problem-free:
- The larger the blur radius (RADIUS) is, the higher the performance requirement is. The blur radius cannot exceed 25, so the image with very high blur cannot be obtained.
- ScriptIntrinsicBlur was only introduced in API 17, but if you want to implement it on Android devices below 4.2, you need to introduce the RenderScript Support Library, and of course, the size of the installation package will increase accordingly.
RenderScript implementation
First add the following code to the build.gradle file in the app directory:
defaultConfig {
applicationId "io.github.marktony.gaussianblur"
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"
renderscriptTargetApi 19
renderscriptSupportModeEnabled true
}Copy the code
RenderScriptIntrinsics provides a number of action classes that can help us quickly implement various image manipulations. For example, ScriptIntrinsicBlur can easily and efficiently implement Gaussian blur effects.
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);
returnoriginal; }}Copy the code
Then you can go straight to RenderScriptGaussianBlur and happily achieve varying degrees of blur depending on the SeekBar values.
package io.github.marktony.gaussianblur;
import android.content.DialogInterface;
import android.graphics.Bitmap;
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));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}@Override
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() {
@Override
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() {
@Override
public void onClick(DialogInterface dialog, int which) {}}); dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {}}); dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) { container.setVisibility(View.GONE); layout.setVisibility(View.VISIBLE); }}); dialog.show(); }}); }}Copy the code
In the code to do some view visibility operations, relatively simple, I believe you can understand. Unlike the Dialog implementation in Instagram, I didn’t capture the entire page’s bitmap, just the content under the ActionBar. If I had to achieve the same effect, I could have just adjusted the layout of the page. I won’t go into that here.
Is it easy?
The wheel
In addition to RenderScript, there are some excellent wheels:
- 500px-android-blur
- Blurry
- android-stackblur
- FastBlur:Java algorithm implementation
BlurTestAndroid provides statistics and comparisons of how different libraries are implemented, the algorithms used, and the time spent, or you can download its demo app and test it out yourself.
The sample code is here: GaussianBlur