├── settings.gradle
├── imgs
├── 1.gif
├── 2.png
└── blurring.apk
├── sample
├── res
│ ├── values
│ │ └── strings.xml
│ ├── drawable-xxhdpi
│ │ ├── p0.jpg
│ │ ├── p1.jpg
│ │ ├── p2.jpg
│ │ ├── p3.jpg
│ │ ├── p4.jpg
│ │ ├── p5.jpg
│ │ ├── p6.jpg
│ │ ├── p7.jpg
│ │ ├── p8.jpg
│ │ └── p9.jpg
│ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ └── layout
│ │ ├── list_item.xml
│ │ ├── list_item_blur.xml
│ │ ├── popup_layout.xml
│ │ └── activity_main.xml
├── src
│ └── com
│ │ └── github
│ │ └── mmin18
│ │ └── realtimeblurview
│ │ └── sample
│ │ ├── ListActivity.java
│ │ ├── MyListAdapter.java
│ │ ├── CustomShapeBlurView.java
│ │ └── MainActivity.java
├── AndroidManifest.xml
└── build.gradle
├── library
├── res
│ └── values
│ │ └── attrs.xml
├── AndroidManifest.xml
├── build.gradle
└── src
│ └── com
│ └── github
│ └── mmin18
│ └── widget
│ └── RealtimeBlurView.java
├── LICENSE
├── .gitignore
└── README.md
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library', ':sample'
2 |
--------------------------------------------------------------------------------
/imgs/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/imgs/1.gif
--------------------------------------------------------------------------------
/imgs/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/imgs/2.png
--------------------------------------------------------------------------------
/imgs/blurring.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/imgs/blurring.apk
--------------------------------------------------------------------------------
/sample/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Realtime Blurring Overlay
3 |
4 |
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p0.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p1.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p2.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p3.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p4.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p5.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p6.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p7.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p8.jpg
--------------------------------------------------------------------------------
/sample/res/drawable-xxhdpi/p9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/drawable-xxhdpi/p9.jpg
--------------------------------------------------------------------------------
/sample/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmf19870210/RealtimeBlurView/HEAD/sample/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/library/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/library/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/src/com/github/mmin18/realtimeblurview/sample/ListActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.mmin18.realtimeblurview.sample;
2 |
3 | import android.os.Bundle;
4 |
5 | /**
6 | * Created by mmin18 on 02/11/2017.
7 | */
8 |
9 | public class ListActivity extends android.app.ListActivity {
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 |
14 | setListAdapter(new MyListAdapter(this, R.layout.list_item_blur));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/sample/res/layout/list_item_blur.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2016 Tu Yimin (http://github.com/mmin18)
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
--------------------------------------------------------------------------------
/sample/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.ap_
3 |
4 | # Files for the Dalvik VM
5 | *.dex
6 |
7 | # Java class files
8 | *.class
9 |
10 | # Generated files
11 | bin/
12 | gen/
13 | out/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
28 | # Android Studio Navigation editor temp files
29 | .navigation/
30 |
31 | # Android Studio captures folder
32 | captures/
33 |
34 | # Intellij
35 | *.iml
36 |
37 | # Android Studio
38 | .idea/
39 | .gradle
40 | /*/local.properties
41 | /*/out
42 | /*/*/build
43 | /*/*/production
44 | *.iml
45 | *.iws
46 | *.ipr
47 | *~
48 | *.swp
49 |
--------------------------------------------------------------------------------
/sample/res/layout/popup_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
20 |
21 |
28 |
29 |
--------------------------------------------------------------------------------
/sample/src/com/github/mmin18/realtimeblurview/sample/MyListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.mmin18.realtimeblurview.sample;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 |
10 | /**
11 | * Created by mmin18 on 9/21/16.
12 | */
13 | public class MyListAdapter extends BaseAdapter {
14 |
15 | View[] cells;
16 |
17 | public MyListAdapter(Context ctx, int layoutId) {
18 | super();
19 |
20 | int[] imgs = {
21 | R.drawable.p0, R.drawable.p1, R.drawable.p2, R.drawable.p3, R.drawable.p4,
22 | R.drawable.p5, R.drawable.p6, R.drawable.p7, R.drawable.p8, R.drawable.p9
23 | };
24 | LayoutInflater inflater = LayoutInflater.from(ctx);
25 | cells = new View[imgs.length];
26 | for (int i = 0; i < imgs.length; i++) {
27 | View cell = inflater.inflate(layoutId, null);
28 | ((ImageView) cell.findViewById(android.R.id.icon)).setImageResource(imgs[i]);
29 | cells[i] = cell;
30 | }
31 |
32 | }
33 |
34 | @Override
35 | public int getCount() {
36 | return cells.length;
37 | }
38 |
39 | @Override
40 | public Object getItem(int position) {
41 | return position;
42 | }
43 |
44 | @Override
45 | public long getItemId(int position) {
46 | return position;
47 | }
48 |
49 | @Override
50 | public View getView(int position, View convertView, ViewGroup parent) {
51 | return cells[position];
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/sample/src/com/github/mmin18/realtimeblurview/sample/CustomShapeBlurView.java:
--------------------------------------------------------------------------------
1 | package com.github.mmin18.realtimeblurview.sample;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapShader;
6 | import android.graphics.Canvas;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.RectF;
10 | import android.graphics.Shader;
11 | import android.util.AttributeSet;
12 |
13 | import com.github.mmin18.widget.RealtimeBlurView;
14 |
15 | /**
16 | * Created by mmin18 on 9/27/16.
17 | */
18 | public class CustomShapeBlurView extends RealtimeBlurView {
19 | Paint mPaint;
20 | RectF mRectF;
21 |
22 | public CustomShapeBlurView(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | mPaint = new Paint();
25 | mRectF = new RectF();
26 | }
27 |
28 | /**
29 | * Custom oval shape
30 | */
31 | @Override
32 | protected void drawBlurredBitmap(Canvas canvas, Bitmap blurredBitmap, int overlayColor) {
33 | if (blurredBitmap != null) {
34 | mRectF.right = getWidth();
35 | mRectF.bottom = getHeight();
36 |
37 | mPaint.reset();
38 | mPaint.setAntiAlias(true);
39 | BitmapShader shader = new BitmapShader(blurredBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
40 | Matrix matrix = new Matrix();
41 | matrix.postScale(mRectF.width() / blurredBitmap.getWidth(), mRectF.height() / blurredBitmap.getHeight());
42 | shader.setLocalMatrix(matrix);
43 | mPaint.setShader(shader);
44 | canvas.drawOval(mRectF, mPaint);
45 |
46 | mPaint.reset();
47 | mPaint.setAntiAlias(true);
48 | mPaint.setColor(overlayColor);
49 | canvas.drawOval(mRectF, mPaint);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | dependencies {
4 | compile project(':library')
5 | compile fileTree(dir: 'libs', include: ['*.jar'])
6 | }
7 |
8 | android {
9 | compileSdkVersion rootProject.ext.compileSdkVersion
10 | buildToolsVersion rootProject.ext.buildToolsVersion
11 |
12 | defaultConfig {
13 | minSdkVersion rootProject.ext.minSdkVersion
14 | targetSdkVersion rootProject.ext.targetSdkVersion
15 | renderscriptTargetApi 19
16 | renderscriptSupportModeEnabled true
17 | }
18 |
19 | lintOptions {
20 | abortOnError rootProject.ext.lintAbortOnError
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility rootProject.ext.sourceCompatibility
25 | targetCompatibility rootProject.ext.targetCompatibility
26 | }
27 |
28 | sourceSets {
29 | main {
30 | manifest.srcFile 'AndroidManifest.xml'
31 | java.srcDirs = ['src']
32 | resources.srcDirs = ['src']
33 | aidl.srcDirs = ['src']
34 | renderscript.srcDirs = ['src']
35 | res.srcDirs = ['res']
36 | assets.srcDirs = ['assets']
37 | }
38 |
39 | // Move the tests to tests/java, tests/res, etc...
40 | instrumentTest.setRoot('tests')
41 |
42 | // Move the build types to build-types/
43 | // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
44 | // This moves them out of them default location under src//... which would
45 | // conflict with src/ being used by the main source set.
46 | // Adding new build types or product flavors should be accompanied
47 | // by a similar customization.
48 | debug.setRoot('build-types/debug')
49 | release.setRoot('build-types/release')
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release' // must be applied after your artifact generating plugin (eg. java / com.android.library)
3 |
4 | dependencies {
5 | compile fileTree(dir: 'libs', include: '*.jar')
6 | }
7 |
8 | android {
9 | compileSdkVersion rootProject.ext.compileSdkVersion
10 | buildToolsVersion rootProject.ext.buildToolsVersion
11 |
12 | defaultConfig {
13 | minSdkVersion rootProject.ext.minSdkVersion
14 | targetSdkVersion rootProject.ext.targetSdkVersion
15 | renderscriptTargetApi 19
16 | renderscriptSupportModeEnabled true
17 | }
18 |
19 | lintOptions {
20 | abortOnError rootProject.ext.lintAbortOnError
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility rootProject.ext.sourceCompatibility
25 | targetCompatibility rootProject.ext.targetCompatibility
26 | }
27 |
28 | sourceSets {
29 | main {
30 | manifest.srcFile 'AndroidManifest.xml'
31 | java.srcDirs = ['src']
32 | resources.srcDirs = ['src']
33 | aidl.srcDirs = ['src']
34 | renderscript.srcDirs = ['src']
35 | res.srcDirs = ['res']
36 | assets.srcDirs = ['assets']
37 | }
38 |
39 | // Move the tests to tests/java, tests/res, etc...
40 | instrumentTest.setRoot('tests')
41 |
42 | // Move the build types to build-types/
43 | // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
44 | // This moves them out of them default location under src//... which would
45 | // conflict with src/ being used by the main source set.
46 | // Adding new build types or product flavors should be accompanied
47 | // by a similar customization.
48 | debug.setRoot('build-types/debug')
49 | release.setRoot('build-types/release')
50 | }
51 | }
52 |
53 | buildscript {
54 | repositories {
55 | jcenter()
56 | }
57 | dependencies {
58 | classpath 'com.novoda:bintray-release:0.5.0'
59 | }
60 | }
61 |
62 | publish {
63 | userOrg = 'mmin18'
64 | groupId = 'com.github.mmin18'
65 | artifactId = 'realtimeblurview'
66 | publishVersion = '1.1.2'
67 | desc = 'A realtime blurring overlay for Android (like iOS UIVisualEffectView)'
68 | website = 'https://github.com/mmin18/RealtimeBlurView'
69 | }
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RealtimeBlurView
2 |
3 | It's just a realtime blurring overlay like iOS UIVisualEffectView.
4 |
5 | 
6 |
7 | Just put the view in the layout xml, no Java code is required.
8 |
9 | // Views to be blurred
10 |
11 |
12 |
17 |
18 | // Views above blurring overlay
19 |
20 |
21 | Try the sample apk: [blurring.apk](imgs/blurring.apk)
22 |
23 | # Adding to project
24 |
25 | Add dependencies in your `build.gradle`:
26 |
27 | ```groovy
28 | dependencies {
29 | compile 'com.github.mmin18:realtimeblurview:1.1.2'
30 | }
31 | android {
32 | buildToolsVersion '24.0.2' // Use 23.0.3 or higher
33 | defaultConfig {
34 | minSdkVersion 15
35 | renderscriptTargetApi 19
36 | renderscriptSupportModeEnabled true // Enable RS support
37 | }
38 | }
39 | ```
40 |
41 | Add proguard rules if necessary:
42 |
43 | ```
44 | -keep class android.support.v8.renderscript.** { *; }
45 | ```
46 |
47 | # Limitations
48 |
49 | - Adding multiple RealtimeBlurView (even not visible) may hurt drawing performance, like use it in ListView or RecyclerView.
50 |
51 | - It will not work with SurfaceView / TextureView like VideoView, GoogleMapView
52 |
53 | # Performance
54 |
55 | RealtimeBlurView use RenderScript to blur the bitmap, just like [500px-android-blur](https://github.com/500px/500px-android-blur).
56 |
57 | Everytime your window draw, it will render a blurred bitmap, so there is a performance cost. Set downsampleFactor>=4 will significantly reduce the render cost. However, if you just want to blur a static view, 500px-android-blur is good enough.
58 |
59 | I've run the sample on some old phones like Samsung Galaxy S2, Samsung Galaxy S3, it runs at full FPS. Here is a performance chart while scrolling the list on Nexus 5.
60 |
61 | 
62 |
63 | # Changelog
64 |
65 | ### 1.1.2 (2018-05-28)
66 |
67 | Bug fix (Draw overlay color in rect).
68 |
69 | ### 1.1.1 (2018-05-22)
70 |
71 | Fix downsample factor issue when blur radius is very big.
72 |
73 | ### 1.1.0 (2017-01-02)
74 |
75 | Some improvements (OOM, resize, window background)
76 |
77 | ### 1.0.8 (2016-11-29)
78 |
79 | Fix view not refreshed issue on PopupWindow
80 |
81 | ### 1.0.6 (2016-11-7)
82 |
83 | Fix crash when view is very small (draw at least 1px)
84 |
85 | ### 1.0.5 (2016-11-5)
86 |
87 | Support Popup Window (Use it as dialog background)
88 | Ignore UnsatisfiedLinkError if APK is not debuggable.
89 |
90 | ### 1.0.4 (2016-9-28)
91 |
92 | Support custom shape (by override drawBlurredBitmap()), support view in ContextThemeWrapper.
93 |
--------------------------------------------------------------------------------
/sample/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
18 |
19 |
23 |
24 |
25 |
26 |
34 |
35 |
43 |
44 |
52 |
53 |
63 |
64 |
73 |
74 |
75 |
79 |
80 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/sample/src/com/github/mmin18/realtimeblurview/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.mmin18.realtimeblurview.sample;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.content.Intent;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.util.TypedValue;
10 | import android.view.LayoutInflater;
11 | import android.view.Menu;
12 | import android.view.MenuItem;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 | import android.widget.ListView;
16 | import android.widget.SeekBar;
17 | import android.widget.TextView;
18 |
19 | import com.github.mmin18.widget.RealtimeBlurView;
20 |
21 | import java.util.Random;
22 |
23 | /**
24 | * Created by mmin18 on 3/5/16.
25 | */
26 | public class MainActivity extends Activity {
27 | RealtimeBlurView blurView;
28 | SeekBar blurRadius;
29 | TextView blurRadiusText;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_main);
35 |
36 | blurView = (RealtimeBlurView) findViewById(R.id.blur_view);
37 | ((ListView) findViewById(R.id.list)).setAdapter(new MyListAdapter(this, R.layout.list_item));
38 |
39 | blurRadius = (SeekBar) findViewById(R.id.blur_radius);
40 | blurRadius.setProgress(10);
41 | blurRadius.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
42 | @Override
43 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
44 | updateRadius();
45 | }
46 |
47 | @Override
48 | public void onStartTrackingTouch(SeekBar seekBar) {
49 | }
50 |
51 | @Override
52 | public void onStopTrackingTouch(SeekBar seekBar) {
53 | }
54 | });
55 | blurRadiusText = (TextView) findViewById(R.id.blur_radius_val);
56 | updateRadius();
57 |
58 | findViewById(R.id.drag).setOnTouchListener(touchListener);
59 | }
60 |
61 | @Override
62 | public boolean onCreateOptionsMenu(Menu menu) {
63 | menu.add("Popup").setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
64 | menu.add("List").setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
65 | return super.onCreateOptionsMenu(menu);
66 | }
67 |
68 | @Override
69 | public boolean onOptionsItemSelected(MenuItem item) {
70 | if ("Popup".equals(item.getTitle())) {
71 | showPopup();
72 | } else if ("List".equals(item.getTitle())) {
73 | startActivity(new Intent(this, ListActivity.class));
74 | }
75 | return true;
76 | }
77 |
78 | private void showPopup() {
79 | AlertDialog.Builder b = new AlertDialog.Builder(this);
80 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
81 | LayoutInflater inflater = LayoutInflater.from(this);
82 | View layout = inflater.inflate(R.layout.popup_layout, null);
83 | b.setView(layout);
84 | } else {
85 | b.setView(R.layout.popup_layout);
86 | }
87 | Dialog dlg = b.show();
88 | dlg.findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
89 | @Override
90 | public void onClick(View v) {
91 | ((ListView) findViewById(R.id.list)).smoothScrollToPosition(new Random(System.currentTimeMillis()).nextInt(10));
92 | }
93 | });
94 | }
95 |
96 | private void updateRadius() {
97 | blurView.setBlurRadius(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, blurRadius.getProgress(), getResources().getDisplayMetrics()));
98 | blurRadiusText.setText(blurRadius.getProgress() + "dp");
99 | }
100 |
101 | private View.OnTouchListener touchListener = new View.OnTouchListener() {
102 | float dx, dy;
103 |
104 | @Override
105 | public boolean onTouch(View v, MotionEvent event) {
106 | View target = findViewById(R.id.blur_frame);
107 | if (event.getAction() == MotionEvent.ACTION_DOWN) {
108 | dx = target.getX() - event.getRawX();
109 | dy = target.getY() - event.getRawY();
110 | } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
111 | target.setX(event.getRawX() + dx);
112 | target.setY(event.getRawY() + dy);
113 | }
114 | return true;
115 | }
116 | };
117 |
118 | private boolean slideUp;
119 |
120 | public void doSlide(View v) {
121 | final View view = findViewById(R.id.blur_frame);
122 | view.animate().translationYBy((slideUp ? -1 : 1) * view.getHeight()).setDuration(1000).start();
123 | slideUp = !slideUp;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/library/src/com/github/mmin18/widget/RealtimeBlurView.java:
--------------------------------------------------------------------------------
1 | package com.github.mmin18.widget;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.ContextWrapper;
6 | import android.content.pm.ApplicationInfo;
7 | import android.content.res.TypedArray;
8 | import android.graphics.Bitmap;
9 | import android.graphics.Canvas;
10 | import android.graphics.Paint;
11 | import android.graphics.Rect;
12 | import android.support.v8.renderscript.Allocation;
13 | import android.support.v8.renderscript.Element;
14 | import android.support.v8.renderscript.RenderScript;
15 | import android.support.v8.renderscript.ScriptIntrinsicBlur;
16 | import android.util.AttributeSet;
17 | import android.util.TypedValue;
18 | import android.view.View;
19 | import android.view.ViewTreeObserver;
20 |
21 | import com.github.mmin18.realtimeblurview.R;
22 |
23 | /**
24 | * A realtime blurring overlay (like iOS UIVisualEffectView). Just put it above
25 | * the view you want to blur and it doesn't have to be in the same ViewGroup
26 | *
27 | * - realtimeBlurRadius (10dp)
28 | * - realtimeDownsampleFactor (4)
29 | * - realtimeOverlayColor (#aaffffff)
30 | *
31 | */
32 | public class RealtimeBlurView extends View {
33 |
34 | private float mDownsampleFactor; // default 4
35 | private int mOverlayColor; // default #aaffffff
36 | private float mBlurRadius; // default 10dp (0 < r <= 25)
37 |
38 | private boolean mDirty;
39 | private Bitmap mBitmapToBlur, mBlurredBitmap;
40 | private Canvas mBlurringCanvas;
41 | private RenderScript mRenderScript;
42 | private ScriptIntrinsicBlur mBlurScript;
43 | private Allocation mBlurInput, mBlurOutput;
44 | private boolean mIsRendering;
45 | private Paint mPaint;
46 | private final Rect mRectSrc = new Rect(), mRectDst = new Rect();
47 | // mDecorView should be the root view of the activity (even if you are on a different window like a dialog)
48 | private View mDecorView;
49 | // If the view is on different root view (usually means we are on a PopupWindow),
50 | // we need to manually call invalidate() in onPreDraw(), otherwise we will not be able to see the changes
51 | private boolean mDifferentRoot;
52 | private static int RENDERING_COUNT;
53 |
54 | public RealtimeBlurView(Context context, AttributeSet attrs) {
55 | super(context, attrs);
56 |
57 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RealtimeBlurView);
58 | mBlurRadius = a.getDimension(R.styleable.RealtimeBlurView_realtimeBlurRadius,
59 | TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()));
60 | mDownsampleFactor = a.getFloat(R.styleable.RealtimeBlurView_realtimeDownsampleFactor, 4);
61 | mOverlayColor = a.getColor(R.styleable.RealtimeBlurView_realtimeOverlayColor, 0xAAFFFFFF);
62 | a.recycle();
63 |
64 | mPaint = new Paint();
65 | }
66 |
67 | public void setBlurRadius(float radius) {
68 | if (mBlurRadius != radius) {
69 | mBlurRadius = radius;
70 | mDirty = true;
71 | invalidate();
72 | }
73 | }
74 |
75 | public void setDownsampleFactor(float factor) {
76 | if (factor <= 0) {
77 | throw new IllegalArgumentException("Downsample factor must be greater than 0.");
78 | }
79 |
80 | if (mDownsampleFactor != factor) {
81 | mDownsampleFactor = factor;
82 | mDirty = true; // may also change blur radius
83 | releaseBitmap();
84 | invalidate();
85 | }
86 | }
87 |
88 | public void setOverlayColor(int color) {
89 | if (mOverlayColor != color) {
90 | mOverlayColor = color;
91 | invalidate();
92 | }
93 | }
94 |
95 | private void releaseBitmap() {
96 | if (mBlurInput != null) {
97 | mBlurInput.destroy();
98 | mBlurInput = null;
99 | }
100 | if (mBlurOutput != null) {
101 | mBlurOutput.destroy();
102 | mBlurOutput = null;
103 | }
104 | if (mBitmapToBlur != null) {
105 | mBitmapToBlur.recycle();
106 | mBitmapToBlur = null;
107 | }
108 | if (mBlurredBitmap != null) {
109 | mBlurredBitmap.recycle();
110 | mBlurredBitmap = null;
111 | }
112 | }
113 |
114 | private void releaseScript() {
115 | if (mRenderScript != null) {
116 | mRenderScript.destroy();
117 | mRenderScript = null;
118 | }
119 | if (mBlurScript != null) {
120 | mBlurScript.destroy();
121 | mBlurScript = null;
122 | }
123 | }
124 |
125 | protected void release() {
126 | releaseBitmap();
127 | releaseScript();
128 | }
129 |
130 | protected boolean prepare() {
131 | if (mBlurRadius == 0) {
132 | release();
133 | return false;
134 | }
135 |
136 | float downsampleFactor = mDownsampleFactor;
137 | float radius = mBlurRadius / downsampleFactor;
138 | if (radius > 25) {
139 | downsampleFactor = downsampleFactor * radius / 25;
140 | radius = 25;
141 | }
142 |
143 | if (mDirty || mRenderScript == null) {
144 | if (mRenderScript == null) {
145 | try {
146 | mRenderScript = RenderScript.create(getContext());
147 | mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
148 | } catch (android.support.v8.renderscript.RSRuntimeException e) {
149 | if (isDebug(getContext())) {
150 | if (e.getMessage() != null && e.getMessage().startsWith("Error loading RS jni library: java.lang.UnsatisfiedLinkError:")) {
151 | throw new RuntimeException("Error loading RS jni library, Upgrade buildToolsVersion=\"24.0.2\" or higher may solve this issue");
152 | } else {
153 | throw e;
154 | }
155 | } else {
156 | // In release mode, just ignore
157 | releaseScript();
158 | return false;
159 | }
160 | }
161 | }
162 |
163 | mBlurScript.setRadius(radius);
164 | mDirty = false;
165 | }
166 |
167 | final int width = getWidth();
168 | final int height = getHeight();
169 |
170 | int scaledWidth = Math.max(1, (int) (width / downsampleFactor));
171 | int scaledHeight = Math.max(1, (int) (height / downsampleFactor));
172 |
173 | if (mBlurringCanvas == null || mBlurredBitmap == null
174 | || mBlurredBitmap.getWidth() != scaledWidth
175 | || mBlurredBitmap.getHeight() != scaledHeight) {
176 | releaseBitmap();
177 |
178 | boolean r = false;
179 | try {
180 | mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
181 | if (mBitmapToBlur == null) {
182 | return false;
183 | }
184 | mBlurringCanvas = new Canvas(mBitmapToBlur);
185 |
186 | mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
187 | Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
188 | mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
189 |
190 | mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
191 | if (mBlurredBitmap == null) {
192 | return false;
193 | }
194 |
195 | r = true;
196 | } catch (OutOfMemoryError e) {
197 | // Bitmap.createBitmap() may cause OOM error
198 | // Simply ignore and fallback
199 | } finally {
200 | if (!r) {
201 | releaseBitmap();
202 | return false;
203 | }
204 | }
205 | }
206 | return true;
207 | }
208 |
209 | protected void blur(Bitmap bitmapToBlur, Bitmap blurredBitmap) {
210 | mBlurInput.copyFrom(bitmapToBlur);
211 | mBlurScript.setInput(mBlurInput);
212 | mBlurScript.forEach(mBlurOutput);
213 | mBlurOutput.copyTo(blurredBitmap);
214 | }
215 |
216 | private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
217 | @Override
218 | public boolean onPreDraw() {
219 | final int[] locations = new int[2];
220 | Bitmap oldBmp = mBlurredBitmap;
221 | View decor = mDecorView;
222 | if (decor != null && isShown() && prepare()) {
223 | boolean redrawBitmap = mBlurredBitmap != oldBmp;
224 | oldBmp = null;
225 | decor.getLocationOnScreen(locations);
226 | int x = -locations[0];
227 | int y = -locations[1];
228 |
229 | getLocationOnScreen(locations);
230 | x += locations[0];
231 | y += locations[1];
232 |
233 | // just erase transparent
234 | mBitmapToBlur.eraseColor(mOverlayColor & 0xffffff);
235 |
236 | int rc = mBlurringCanvas.save();
237 | mIsRendering = true;
238 | RENDERING_COUNT++;
239 | try {
240 | mBlurringCanvas.scale(1.f * mBitmapToBlur.getWidth() / getWidth(), 1.f * mBitmapToBlur.getHeight() / getHeight());
241 | mBlurringCanvas.translate(-x, -y);
242 | if (decor.getBackground() != null) {
243 | decor.getBackground().draw(mBlurringCanvas);
244 | }
245 | decor.draw(mBlurringCanvas);
246 | } catch (StopException e) {
247 | } finally {
248 | mIsRendering = false;
249 | RENDERING_COUNT--;
250 | mBlurringCanvas.restoreToCount(rc);
251 | }
252 |
253 | blur(mBitmapToBlur, mBlurredBitmap);
254 |
255 | if (redrawBitmap || mDifferentRoot) {
256 | invalidate();
257 | }
258 | }
259 |
260 | return true;
261 | }
262 | };
263 |
264 | protected View getActivityDecorView() {
265 | Context ctx = getContext();
266 | for (int i = 0; i < 4 && ctx != null && !(ctx instanceof Activity) && ctx instanceof ContextWrapper; i++) {
267 | ctx = ((ContextWrapper) ctx).getBaseContext();
268 | }
269 | if (ctx instanceof Activity) {
270 | return ((Activity) ctx).getWindow().getDecorView();
271 | } else {
272 | return null;
273 | }
274 | }
275 |
276 | @Override
277 | protected void onAttachedToWindow() {
278 | super.onAttachedToWindow();
279 | mDecorView = getActivityDecorView();
280 | if (mDecorView != null) {
281 | mDecorView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
282 | mDifferentRoot = mDecorView.getRootView() != getRootView();
283 | if (mDifferentRoot) {
284 | mDecorView.postInvalidate();
285 | }
286 | } else {
287 | mDifferentRoot = false;
288 | }
289 | }
290 |
291 | @Override
292 | protected void onDetachedFromWindow() {
293 | if (mDecorView != null) {
294 | mDecorView.getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
295 | }
296 | release();
297 | super.onDetachedFromWindow();
298 | }
299 |
300 | @Override
301 | public void draw(Canvas canvas) {
302 | if (mIsRendering) {
303 | // Quit here, don't draw views above me
304 | throw STOP_EXCEPTION;
305 | } else if (RENDERING_COUNT > 0) {
306 | // Doesn't support blurview overlap on another blurview
307 | } else {
308 | super.draw(canvas);
309 | }
310 | }
311 |
312 | @Override
313 | protected void onDraw(Canvas canvas) {
314 | super.onDraw(canvas);
315 | drawBlurredBitmap(canvas, mBlurredBitmap, mOverlayColor);
316 | }
317 |
318 | /**
319 | * Custom draw the blurred bitmap and color to define your own shape
320 | *
321 | * @param canvas
322 | * @param blurredBitmap
323 | * @param overlayColor
324 | */
325 | protected void drawBlurredBitmap(Canvas canvas, Bitmap blurredBitmap, int overlayColor) {
326 | if (blurredBitmap != null) {
327 | mRectSrc.right = blurredBitmap.getWidth();
328 | mRectSrc.bottom = blurredBitmap.getHeight();
329 | mRectDst.right = getWidth();
330 | mRectDst.bottom = getHeight();
331 | canvas.drawBitmap(blurredBitmap, mRectSrc, mRectDst, null);
332 | }
333 | mPaint.setColor(overlayColor);
334 | canvas.drawRect(mRectDst, mPaint);
335 | }
336 |
337 | private static class StopException extends RuntimeException {
338 | }
339 |
340 | private static StopException STOP_EXCEPTION = new StopException();
341 |
342 | static {
343 | try {
344 | RealtimeBlurView.class.getClassLoader().loadClass("android.support.v8.renderscript.RenderScript");
345 | } catch (ClassNotFoundException e) {
346 | throw new RuntimeException("RenderScript support not enabled. Add \"android { defaultConfig { renderscriptSupportModeEnabled true }}\" in your build.gradle");
347 | }
348 | }
349 |
350 | // android:debuggable="true" in AndroidManifest.xml (auto set by build tool)
351 | static Boolean DEBUG = null;
352 |
353 | static boolean isDebug(Context ctx) {
354 | if (DEBUG == null && ctx != null) {
355 | DEBUG = (ctx.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
356 | }
357 | return DEBUG == Boolean.TRUE;
358 | }
359 | }
360 |
--------------------------------------------------------------------------------