在某些特定时刻,我们需要把app所有界面变为灰色,在web界面中我们可以使用
filter:gray或者-webkit-filter: grayscale(100%);添加灰色滤镜,那么在app中该如何实现呢?
原理
在APP中将界面变灰,需要创建一个饱和度为0的颜色过滤器(饱和度为0时,view就变成了黑白色),将这个颜色过滤器通过setLayerType方法设置到当前View上。(此方法会开启此View的硬件加速功能)
因为我们要实现的功能是全局灰度化,所以首先view的选型最好是一个根view,这样我们就不用在每个子view上重复操作了,其次我们还要适配Dialog和PopWindow等Window类型的组件,所以我们不能从Activity上动心思了,因为Activity本质也是一个Window,所以综上所述,我们将代码的执行时机选在了WindowManager的addView方法上面,因为不论是Activity的创建还是Dialog等组件的创建最终都会走到这个方法里面。
我们只要在WindowManager执行addView方法时进行hook,加入我们上面的代码逻辑即可。
实现
要进行hook操作,那么我们就要先确定hook点:
WindowManager是一个接口类,其实现类为WindowManagerImpl,而WindowManagerImpl中真正实现窗口操作的类为WindowManagerGlobal,是一个单例类,我们看一下其addView的实现:
1 | public void addView(View view, ViewGroup.LayoutParams params, |
我们最终是要操作view,可以看到所有的view都是添加到mViews这个字段中,那我们可以hook这个list,在其添加view的时候,我们就调用上面的代码将其置灰,那如何才能感知到view的添加操作呢?
实现具有数据感知能力的list:
我们可以自定义一个数组,让其继承于 ArrayList,在其执行添加删除操作时,通过回调进行通知,具体实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108public class ObservableArrayList<T> extends ArrayList<T> {
private List<OnListChangeListener> mListeners = new ArrayList<>();
public void addOnListChangedListener(OnListChangeListener listener) {
if (mListeners != null) {
mListeners.add(listener);
}
}
public void removeOnListChangedListener(OnListChangeListener listener) {
if (mListeners != null) {
mListeners.remove(listener);
}
}
public boolean add(T object) {
super.add(object);
notifyAdd(size() - 1, 1);
return true;
}
public void add(int index, T object) {
super.add(index, object);
notifyAdd(index, 1);
}
public boolean addAll(Collection<? extends T> collection) {
int oldSize = size();
boolean added = super.addAll(collection);
if (added) {
notifyAdd(oldSize, size() - oldSize);
}
return added;
}
public void clear() {
int oldSize = size();
super.clear();
if (oldSize != 0) {
notifyRemove(0, oldSize);
}
}
public T remove(int index) {
T val = super.remove(index);
notifyRemove(index, 1);
return val;
}
public boolean remove(Object object) {
int index = indexOf(object);
if (index >= 0) {
remove(index);
return true;
} else {
return false;
}
}
public T set(int index, T object) {
T val = super.set(index, object);
if (mListeners != null) {
for (OnListChangeListener listener : mListeners) {
listener.onChange(this, index, 1);
}
}
return val;
}
protected void removeRange(int fromIndex, int toIndex) {
super.removeRange(fromIndex, toIndex);
notifyRemove(fromIndex, toIndex - fromIndex);
}
private void notifyAdd(int start, int count) {
if (mListeners != null) {
for (OnListChangeListener listener : mListeners) {
listener.onAdd(this, start, count);
}
}
}
private void notifyRemove(int start, int count) {
if (mListeners != null) {
for (OnListChangeListener listener : mListeners) {
listener.onRemove(this, start, count);
}
}
}
public interface OnListChangeListener {
void onChange(ArrayList list, int index, int count);
void onAdd(ArrayList list, int start, int count);
void onRemove(ArrayList list, int start, int count);
}
}hook操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59public class GlobalGray {
/**
* @param enable 是否开启全局灰色调
*/
public static void enable(boolean enable) {
if (!enable) {
return;
}
try {
//灰色调Paint
final Paint mPaint = new Paint();
ColorMatrix mColorMatrix = new ColorMatrix();
mColorMatrix.setSaturation(0);
mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
//反射获取windowManagerGlobal
Class<?> windowManagerGlobal = Class.forName("android.view.WindowManagerGlobal");
java.lang.reflect.Method getInstanceMethod = windowManagerGlobal.getDeclaredMethod("getInstance");
getInstanceMethod.setAccessible(true);
Object windowManagerGlobalInstance = getInstanceMethod.invoke(windowManagerGlobal);
//反射获取mViews
Field mViewsField = windowManagerGlobal.getDeclaredField("mViews");
mViewsField.setAccessible(true);
Object mViewsObject = mViewsField.get(windowManagerGlobalInstance);
//创建具有数据感知能力的ObservableArrayList
ObservableArrayList<View> observerArrayList = new ObservableArrayList<>();
observerArrayList.addOnListChangedListener(new ObservableArrayList.OnListChangeListener() {
public void onChange(ArrayList list, int index, int count) {
}
public void onAdd(ArrayList list, int start, int count) {
View view = (View) list.get(start);
if (view != null) {
view.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
}
}
public void onRemove(ArrayList list, int start, int count) {
}
});
//将原有的数据添加到新创建的list
observerArrayList.addAll((ArrayList<View>) mViewsObject);
//替换掉原有的mViews
mViewsField.set(windowManagerGlobalInstance, observerArrayList);
} catch (Exception e) {
e.printStackTrace();
}
}
}最后,只需在Application的onCreate中调用上述代码进行hook即可。
1
GlobalGray.enable(true);