(1)从源码中理解并巧用onWindowFocusChanged(boolean hasFocus)

这里开始到后面,想趁着有时间,将Android四大组件和一些系统组件做一些总结和记录.由于网上已经有很多写的很好并且总结也全面的文章.小弟我也囊中羞涩不敢献丑,就记录一些自己觉得重要的有用的知识点,顺便大家一起学习讨论啥的也好

Activity作为四大组件之一,对于整个Android开发有多重要就无需赘言了.关于它的生命周期,这里借用下官网的图,便一目了然:

那么它的生命周期和我们所说的onWindowFocusChanged(boolean hasFocus)方法有何关系?

Activity生命周期源于onCreate(),于是我们将很多数据的初始化放在这里,将数据的持久保存放在onStop() onPause()和onDestroy()等方法,将临时数据的一致性保存处理放在onSaveInstanceState()等等. 那提一个问题, Activity作为装在各种布局控件的组件容器,有没有试过当我们某一个组件的宽高肯定不为0, 但是对该组件使用getWidth() getHeight() 的时候,发现获得的宽高值为0? 因为这个时候在onCreate()那里setContentView() 的视图初始化以及各个组件的测量并没有完成, 而我们就已经调用getWidth() getHeight() 这两个方法了.

追溯下源码, 会发现 Activity在初始化视图的过程中, 会调用onMeasure() onLayout() onDraw()等方法,去绘制组件,测量组件 同时刷新当前的视图,直至完成整个视图的初始化过程.我们如何在适当时候知道Activity完成视图的初始化才去使用getWidth() getHeight()? 这里有一个方法, 使用onWindowFocusChanged(boolean hasFocus);

我们通过Activity源码去查看,发现onWindowFocusChanged(boolean hasFocus)出现在window$callback 和View.java这两个类中. 而window$callback 当中所使用的该方法实际上也是来自View.java. 那么我们看看这个方法在View中是怎样的:

/*** 当当前的window(窗口)获取或者失去焦点的时候会回调这个方法.请注意,这个焦点和view焦点* 是分离的,为了获取按键事件,view和view所在的窗口都必须获得焦点.如果一个窗口处于你的输入* 事件的最上层,那么该窗口将失去焦点而view的焦点会保持不变.* 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.*/public void onWindowFocusChanged(boolean hasWindowFocus) {//获取软键盘InputMethodManager imm = InputMethodManager.peekInstance();if (!hasWindowFocus) {if (isPressed()) {//键盘有按下事件,则强制将该view包含的所有子控件全部setPressed()设置为falsesetPressed(false);}if (imm != null && (mPrivateFlags & FOCUSED) != 0) {//这是一个隐藏的方法(带@hide标签),当view失去焦点时会调用该方法imm.focusOut(this);}//移除长按事件回调的接口方法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();}源码的注释已经写上,应该看出,窗口的焦点和view的焦点是分离的, 这个方法onWindowFocusChanged(boolean hasFocus) 被回调的触发时机是窗口获取或失去焦点的时候.

那么在一个Activity新建的时候,第一次获取焦点是在哪里调用这个方法?看源码:

/*** Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or* {@link #onPause}, for your activity to start interacting with the user.* This is a good place to begin animations, open exclusive-access devices* (such as the camera), etc.** 从下面这段官方注释中,这一段"Use {@link #onWindowFocusChanged} to know for certain that…"* 可知道,在onResume()方法之后会调用onWindowFocusChanged()方法~** <p>Keep in mind that onResume is not the best indicator that your activity* is visible to the user; a system window such as the keyguard may be in* front. Use {@link #onWindowFocusChanged} to know for certain that your* activity is visible to the user (for example, to resume a game).** <p><em>Derived classes must call through to the super class's* implementation of this method. If they do not, an exception will be* thrown.</em></p>** @see #onRestoreInstanceState* @see #onRestart* @see #onPostResume* @see #onPause*/protected void onResume() {getApplication().dispatchActivityResumed(this);mCalled = true;}

可见, 在Activity中的窗口获取焦点的时候,首次调用onWindowFocusChanged()是在onResume()方法后面.之后就会发生正常的窗口失去或重新获取焦点的事情等等.

那么让我们来想想, 哪些情况是会导致窗口失去或者获取焦点的?

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

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

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

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

5, 应用后台, 窗口失去焦点

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

… …

上面这些情况都会Activity都会调用onWindowFocusChanged() 方法.

好了,上面从源码理解了为什么以及何时会调用这个方法, 那么再来看看如何巧用?举个栗子:

巧用一:

临行之前,面对太多的疑问和不解:为何是一个人?

(1)从源码中理解并巧用onWindowFocusChanged(boolean hasFocus)

相关文章:

你感兴趣的文章:

标签云: