浅析onWindowsFocusChanged()方法

前言


在接手的项目中,看到了onWindowsFocusChanged()的方法,抽空了解下它的用途

内容


概述

   从字面上来讲,onWindowsFocusChanged()方法是指当窗口焦点变化的时候;从意义来说,onWindowsFocusChanged()就是指当前的Activity的Windows(窗口)获取或者失去焦点时这个方法就会被调用,并且当回调这个方法时,Activity是完全可见的。

   在Activity生命周期中,onStart(), onResume(), onCreate()都不是布局visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。从onWindowFocusChanged()被执行起,用户可以与应用进行交互了,换句话说,如果你想要在Activity加载后做些操作,可以在这个方法里调用而这之前,对用户的操作需要做一点限制。

onWindowFocusChanged()的使用情景与作用

   根据介绍可以了解,onWindowFocusChanged()使用于以下等情景:

  1. 首次进入一个Activity后会在onResume()方法后面调用;

  2. 从Activity 跳到另一个Activity,新的窗口会获取焦点, 就的Activity的窗口会失去焦点;

  3. 打开软键盘进行输入时,窗口失去焦点;

  4. 软键盘输入完毕消失时,窗口重新获取焦点;

  5. 应用进入后台,窗口失去焦点;

  6. 应用从后台返回当前, 窗口重新获取焦点;

   因此其可以有如下作用:

  1. 监控一个Activity是否载完毕;

  2. 在Activity加载后进行一些操作,如获取手机屏幕的高度和宽度;

  3. 当Activity挂起或恢复时,可以在方法内进行一些数据的保存或恢复的操作;

onWindowFocusChanged()调用详解

   介绍完onWindowFocusChanged()基本的使用情景以及作用后,撸个demo来看下onWindowFocusChanged()具体的调用情况。

   首先在Activity中重写的onWindowFocusChanged()方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 添加窗体在视图初始化完成过后
*
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
//add Window.....
}
}

   其中参数hasFocus表示窗口是否获取或失去焦点,true表示获取焦点,false表示失去焦点。

   然后我们看下总体的代码:

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
public class WindosFoucesDemo extends AppCompatActivity {
private static final String TAG = "LifeCycleDemo";
//Activity创建时被调用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate is called.");
setContentView(R.layout.activity_main);
}
//Activity创建或者从后台重新回到前台时被调用
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart is called.");
}
//Activity创建或者从被覆盖、后台重新回到前台时被调用
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume is called.");
}
//Activity窗口获得或失去焦点时被调用,在onResume之后或onPause之后
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus){
Log.i(TAG, "onWindowFocusChanged is called and" + "hasFocus is true");
}else {
Log.i(TAG, "onWindowFocusChanged is called and" + "hasFocus is false");
}
}
//Activity被覆盖到下面或者锁屏时被调用
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause is called.");
//有可能在执行完onPause或onStop后,系统资源紧张将Activity杀死,所以有必要在此保存持久数据
}
//退出当前Activity或者跳转到新Activity时被调用
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop is called.");
}
//退出当前Activity时被调用,调用之后Activity就结束了
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestory is called.");
}
//Activity从后台重新回到前台时被调用
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "onRestart called.");
}
}

   运行该Demo,查看Log日志,可以得到以下日志结果。

   1. 启动Activity时:

image

   2. 按下任务栏键,Activity被遮挡时:

image

注意:按锁屏键和按任务栏或者Home键的生命周期是不同的,按Home执行onPause –> onStop –> onRestart –> onStart —> onResume 按锁屏键和对话框覆盖界面的生命周期是一样的,都只会进行onPause --> onResume .不会执行onStop,onRestart,onStart。

   3. Activity恢复到前台时:

image

   4. Activity退出时:

image

   从上面的例子以及结果可以清楚的onWindowFocusChanged()在整个Activity生命周期内的调用情况,可以根据这些结果,在开发中来充分利用onWindowFocusChanged()方法的作用。

onWindowFocusChanged()源码分析

   通过Activity源码去查看,发现onWindowFocusChanged()这个方法出现在WindowCallbackWrapper.java和View.java这两个类中,而WindowCallbackWrapper.java中其实也是调用View.java中的onWindowFocusChanged(),其代码如下:

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
/**
* Called when the window containing this view gains or loses focus. Note
* that this is separate from view focus: to receive key events, both
* your view and its window must have focus. If a window is displayed
* on top of yours that takes input focus, then your own window will lose
* focus but the view focus will remain unchanged.
*
* @param hasWindowFocus True if the window containing this view now has
* focus, false otherwise.
* 当前的window(窗口)获取或者失去焦点的时候会回调这个方法.请注意,这个焦点和
* view焦点是分离的,为了获取按键事件,view和view所在的窗口都必须获得焦点.如果 * 一个窗口处于你的输入事件的最上层,那么该窗口将失去焦点而view的焦点会保持不
* 变.
*/
public void onWindowFocusChanged(boolean hasWindowFocus) {
//获取软键盘a system window such as the keyguard may
InputMethodManager imm = InputMethodManager.peekInstance();
if (!hasWindowFocus) {
if (isPressed()) {
//键盘有按下事件,则强制将该view包含的所有子控件全部setPressed()设置为false
setPressed(false);
}
if (imm != null && (mPrivateFlags & FOCUSED) != 0) {
//这是一个隐藏的方法(带@hide标签),当view失去焦点时会调用该方法
imm.focusOut(this);
}
//移onWindowFocusChanged(boolean hasFocus) 被回调的触发时机是窗口获取或失去焦点的时候.除长按事件回调的接口方法
removeLongPressCallback();
//移除轻触探测器,源码中叫 "Remove the tap detection timer."
removeTapCallback();
//当焦点(fucos)从按下变成取消的时候会调用,属于隐藏方法
onFocusLost();
} else if (imm != null && (mPrivateFlags & FOCUSED) != 0) {
//当view获得焦点时调用该方法,属于隐藏方法
imm.focusIn(this);
}
//强制view刷新drawable state,并且会回调drawableStateChanged()方法
refreshDrawableState();
}

   从源码可以验证出,onWindowFocusChanged()方法被回调的触发时机是窗口获取或失去焦点的时候。并且在onResume()方法中的官方解释 Use {@link #onWindowFocusChanged} to know for certain that your activity is visible to the user (for example, to resume a game). 可以知道onWindowFocusChanged()第一次调用是在onResume()方法后面。


版权声明:本文为博主原创文章,转载请注明出处KidSea

小额赞助,鼓励作者写出更好的文章