android四大组件之Activity - (1)从源码中理解并巧用onWindowFocusChanged(boolean hasFocus)

时间:2015-03-05 09:21:27   收藏:0   阅读:6743

这里开始到后面,想趁着有时间,将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()设置为false
                setPressed(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() 方法.


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


巧用一:

当Activity来到onResume()的时候,视图已经初始化完毕了. 在文章开始的时候提到,视图初始化还没完成就调用getwidth()  getHeight()获取宽高值是为0的. 那么现在将获取宽高的方法放在onWindowFocusChanged() 中去,就会发现获得的宽高就是正确的了~ 


巧用二:

当应用被用户按Home键变为后台, 或者从后台中将应用变为前台时,如果要对某些数据进行保存和恢复, 也可以在这里做一些操作. 当然这并不是唯一的选择, 也可以放在onPause(), onResume()等方法中去.只是说, 当知道了onWindowFocusChanged() 的作用, 这里我们就能多一个选择,不是挺好嘛.


当然,还有其他一些用法,可以参考上面所提及的触发onWindowFocusChanged() 方法的时机.同时我们也不能为了用而用,知道多一些,理解深一些,我们才能做出更好的选择和实现.本文到此,晚安啦~




评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!