因为安全风险中提到当app处于后台卡片状态时,显示的卡片页面应该为模糊效果,否则容易泄露用户隐私,尤其当前页涉及个人信息、资产信息等,都会造成信息泄露!基于这种场景,我研究了下这种业务下的模糊效果
找了半天,没有找到太现成的,只能自己动手写一写了,最后试了试,感觉效果凑乎,先用着吧
为了解决该安全风险,主要用到了 BlurView 三方框架
- github地址 - BlurView(国内外)
- gitcode地址 - BlurView(国内)
人生,哪有事事如意?
- 框架介绍
- 事前注意
- AndroidX 兼容了吗?
- View未加载完就设置 blurView 了?
- 仓库引用不到?
- 实践检验
- 控件引入
- 使用方式
- 基础使用
- 兼容使用
- 有那么一刻想优化一下么?
框架介绍
如果想详细了解框架使用,及其源代码的话可以直接前往上方地址
Effect
Tip:不支持 SurfaceView, TextureView, VideoView, MapFragment, GLSurfaceView, etc 模糊
框架引入
implementation 'com.github.Dimezis:BlurView:version-2.0.3'
视图引入
Tip:是处于BlurView的视图是不会被模糊的
<eightbitlab.com.blurview.BlurView
android:id="@+id/blurView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:blurOverlayColor="@color/colorOverlay">
<!--Any child View here, TabLayout for example. This View will NOT be blurred -->
</eightbitlab.com.blurview.BlurView>
调用方式
float radius = 20f;
View decorView = getWindow().getDecorView();
// ViewGroup you want to start blur from. Choose root as close to BlurView in hierarchy as possible.
ViewGroup rootView = (ViewGroup) decorView.findViewById(android.R.id.content);
// Optional:
// Set drawable to draw in the beginning of each blurred frame.
// Can be used in case your layout has a lot of transparent space and your content
// gets a too low alpha value after blur is applied.
Drawable windowBackground = decorView.getBackground();
blurView.setupWith(rootView, new RenderScriptBlur(this)) // or RenderEffectBlur
.setFrameClearDrawable(windowBackground) // Optional
.setBlurRadius(radius)
关于这部分尚未使用,就不做解释了
事前注意
主要记录我在使用中遇到的问题
AndroidX 兼容了吗?
android.useAndroidX=true
View未加载完就设置 blurView 了?
这个并是不必现问题,可能基本遇不到,仅做记录(该 rootView
可以使用xml中最外层布局控件)
rootView.viewTreeObserver.addOnGlobalLayoutListener {
blurView.setupWith(contextView, algorithm)
.setFrameClearDrawable(background)
.setBlurRadius(radius)//如不要需要,无需设置
}
仓库引用不到?
如果你运气不错的话,直接引入框架可能就可以使用了,但是我运气可能不太好
implementation 'com.github.Dimezis:BlurView:version-2.0.3'
根据介绍 JCenter 仓库已经关闭了,需要配置 jitpack
解决方式
repositories {
maven { url 'https://jitpack.io' }
}
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
小课堂
我们常见的远程仓库主要有三种 JCenter
、mavenCentral
、jitpack
- JCenter:JCenter是JFrog公司提供的Android第三方库的仓库,JFrog公司宣布即将废弃该仓库,jcenter仓库是也曾经google默认推荐的第三方库。最早宣布废弃时,2022年2月后,将不可以下载上边的库,如果这些库的开发
者不做库迁移,那么普通开发者将无法使用这些库,不过,好在最后JFrog公司可能和Google达成了什么协议,后续还能下载,但不能更新维护。目前Google推荐使用mavenCentral仓库。 - mavenCentral:sonatype公司提供的第三方仓库,当时使用比较麻烦,审核也比较严格,比如你发布库的时候,库的包名,你必须要有这个域名的所有权,才能发布,不像jcenter谁先用,就归谁。目前Google推荐使用的第三方仓库。
- jitpack (https://jitpack.io/):在jcenter废弃后,逐渐被用的越来越多,使用比较简单,适合个人开发者使用,缺点是不是Google官方推荐,使用时要手动添加maven依赖。
实践检验
可能是能力不足,解决小问题花了一些时间,所以建议大家保证已经解决了上述提到的注意点
build.gradle
引入框架
implementation 'com.github.Dimezis:BlurView:version-2.0.3'
控件引入
关于这部分要了解视图层次的概念,感觉有以下几点需要特别注意一下
- 最外层布局可以采用
FrameLayout
、RelativeLayout
、ConstraintLayout
,不然可能无法达到视图覆盖的效果 - 模糊效果是
直接将模糊后的视图覆盖到原正常视图之上
- 正常视图位于底层,从xml角度一般先写,模糊视图后写,可以参考栈结构
activity_main (引入BlurView
)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ic_launcher_background"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:textSize="20sp"
android:text="放置正常视图,模糊后被覆盖"
android:textColor="#333333" />
</LinearLayout>
<eightbitlab.com.blurview.BlurView
android:id="@+id/blur_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:blurOverlayColor="#78ffffff">
<!-- <LinearLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:orientation="vertical">-->
<!-- <TextView-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="50dp"-->
<!-- android:gravity="center"-->
<!-- android:text="放置模糊后的正常视图,模糊后依旧正常显示"-->
<!-- android:textColor="#333333" />-->
<!-- </LinearLayout>-->
</eightbitlab.com.blurview.BlurView>
</android.support.constraint.ConstraintLayout>
使用方式
关于 BlurView
所需 rootView
- 可以采用
xml
内的最外层ViewGroup
- 也可以采用
Window
的ContentView
基础使用
package com.example.blurview
import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.view.ViewGroup
import eightbitlab.com.blurview.BlurAlgorithm
import eightbitlab.com.blurview.BlurView
import eightbitlab.com.blurview.RenderEffectBlur
import eightbitlab.com.blurview.RenderScriptBlur
class MainActivity : AppCompatActivity() {
private val blurView: BlurView by lazy {
findViewById(R.id.blur_view)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val contextView = window.decorView.findViewById<ViewGroup>(android.R.id.content)
blurView.setupWith(contextView, RenderScriptBlur(this@MainActivity))
}
override fun onPause() {
super.onPause()
blurView.visibility = View.VISIBLE
}
override fun onResume() {
super.onResume()
blurView.visibility = View.GONE
}
}
兼容使用
- 用到了
Window
相关的DecorView
原理 (系统级) - 因
RenderScriptBlur
过时,用到了RenderEffectBlur
(框架级)
package com.example.blurview
import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.view.ViewGroup
import eightbitlab.com.blurview.BlurAlgorithm
import eightbitlab.com.blurview.BlurView
import eightbitlab.com.blurview.RenderEffectBlur
import eightbitlab.com.blurview.RenderScriptBlur
class MainActivity : AppCompatActivity() {
private val blurView: BlurView by lazy {
findViewById(R.id.blur_view)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val background = window.decorView.background
val contextView = window.decorView.findViewById<ViewGroup>(android.R.id.content)
val radius = 20f
val algorithm: BlurAlgorithm = getBlurAlgorithm()
blurView.setupWith(contextView, algorithm)
.setFrameClearDrawable(background)
.setBlurRadius(radius) //如不要需要,无需设置
}
override fun onPause() {
super.onPause()
blurView.visibility = View.VISIBLE
}
override fun onResume() {
super.onResume()
blurView.visibility = View.GONE
}
/**
* 兼容处理
* */
private fun getBlurAlgorithm(): BlurAlgorithm {
val algorithm: BlurAlgorithm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
RenderEffectBlur()
} else {
RenderScriptBlur(this)
}
return algorithm
}
}
有那么一刻想优化一下么?
我发现在后台切换卡片时,当卡片处于当前 position
,好像会偶显正常视图,基于这点可以考虑同时兼容前后台监听来实现更好的效果
以前写过一篇 Android进阶之路 - 前后台切换监听,有兴趣的话可以去看下
随机找了一篇别人的伪代码,各位可以简单参考下
public class MainActivity extends AppCompatActivity {
private boolean isForeground = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
if (isForeground) {
// 从后台进入前台,恢复界面状态和数据
// TODO: 恢复界面状态和数据
}
isForeground = true;
}
@Override
protected void onPause() {
super.onPause();
if (!isAppOnForeground()) {
// 从前台进入后台,保存界面状态和数据
// TODO: 保存界面状态和数据
}
isForeground = false;
}
/**
* 判断当前应用是否处于前台
*/
private boolean isAppOnForeground() {
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
String packageName = getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(packageName)
&& appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
}