Android Window纪要

Window概念理解

在Andriod开发中经常提到Activity和View,而位于它们之间的Window却较少涉及。Window所表示的是一个抽象的概念,实际上所有View都是依附于Window之上的,包括Activity中的视图、Dialog中的视图以及Toast中的视图。另外View的事件分发也是由Window传递给View的。

Window的管理

Window是一个抽象类,其具体实现为PhoneWindow,Window通过WindowMaganer来管理,如添加、修改和删除等操作。WindowManager是一个接口,其具体实现类为WindowManagerImpl,有如下几个比较重要的方法:

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
}

@Override
public void removeView(View view) {
    mGlobal.removeView(view, false);
}

@Override
public void removeViewImmediate(View view) {
    mGlobal.removeView(view, true);
}

由上述源码可知这几个方法的内部实现是由WindowManagerGlobal实现的。

Window与Activity的关联

通过Activity源码可知,Activity实现了Window.Callback,该接口定义如下:

/**
 * API from a Window back to its caller.  This allows the client to
 * intercept key dispatching, panels and menus, etc.
 */
public interface Callback {
    /**
     * Called to process key events.  At the very least your
     * implementation must call
     * {@link android.view.Window#superDispatchKeyEvent} to do the
     * standard key processing.
     *
     * @param event The key event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchKeyEvent(KeyEvent event);

    /**
     * Called to process a key shortcut event.
     * At the very least your implementation must call
     * {@link android.view.Window#superDispatchKeyShortcutEvent} to do the
     * standard key shortcut processing.
     *
     * @param event The key shortcut event.
     * @return True if this event was consumed.
     */
    public boolean dispatchKeyShortcutEvent(KeyEvent event);

    /**
     * Called to process touch screen events.  At the very least your
     * implementation must call
     * {@link android.view.Window#superDispatchTouchEvent} to do the
     * standard touch screen processing.
     *
     * @param event The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent event);

    /**
     * Called to process trackball events.  At the very least your
     * implementation must call
     * {@link android.view.Window#superDispatchTrackballEvent} to do the
     * standard trackball processing.
     *
     * @param event The trackball event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTrackballEvent(MotionEvent event);

    /**
     * Called to process generic motion events.  At the very least your
     * implementation must call
     * {@link android.view.Window#superDispatchGenericMotionEvent} to do the
     * standard processing.
     *
     * @param event The generic motion event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchGenericMotionEvent(MotionEvent event);

    /**
     * Called to process population of {@link AccessibilityEvent}s.
     *
     * @param event The event.
     *
     * @return boolean Return true if event population was completed.
     */
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);

    /**
     * Instantiate the view to display in the panel for 'featureId'.
     * You can return null, in which case the default content (typically
     * a menu) will be created for you.
     *
     * @param featureId Which panel is being created.
     *
     * @return view The top-level view to place in the panel.
     *
     * @see #onPreparePanel
     */
    @Nullable
    public View onCreatePanelView(int featureId);

    /**
     * Initialize the contents of the menu for panel 'featureId'.  This is
     * called if onCreatePanelView() returns null, giving you a standard
     * menu in which you can place your items.  It is only called once for
     * the panel, the first time it is shown.
     *
     * <p>You can safely hold on to <var>menu</var> (and any items created
     * from it), making modifications to it as desired, until the next
     * time onCreatePanelMenu() is called for this feature.
     *
     * @param featureId The panel being created.
     * @param menu The menu inside the panel.
     *
     * @return boolean You must return true for the panel to be displayed;
     *         if you return false it will not be shown.
     */
    public boolean onCreatePanelMenu(int featureId, Menu menu);

    /**
     * Prepare a panel to be displayed.  This is called right before the
     * panel window is shown, every time it is shown.
     *
     * @param featureId The panel that is being displayed.
     * @param view The View that was returned by onCreatePanelView().
     * @param menu If onCreatePanelView() returned null, this is the Menu
     *             being displayed in the panel.
     *
     * @return boolean You must return true for the panel to be displayed;
     *         if you return false it will not be shown.
     *
     * @see #onCreatePanelView
     */
    public boolean onPreparePanel(int featureId, View view, Menu menu);

    /**
     * Called when a panel's menu is opened by the user. This may also be
     * called when the menu is changing from one type to another (for
     * example, from the icon menu to the expanded menu).
     *
     * @param featureId The panel that the menu is in.
     * @param menu The menu that is opened.
     * @return Return true to allow the menu to open, or false to prevent
     *         the menu from opening.
     */
    public boolean onMenuOpened(int featureId, Menu menu);

    /**
     * Called when a panel's menu item has been selected by the user.
     *
     * @param featureId The panel that the menu is in.
     * @param item The menu item that was selected.
     *
     * @return boolean Return true to finish processing of selection, or
     *         false to perform the normal menu handling (calling its
     *         Runnable or sending a Message to its target Handler).
     */
    public boolean onMenuItemSelected(int featureId, MenuItem item);

    /**
     * This is called whenever the current window attributes change.
     *
     */
    public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);

    /**
     * This hook is called whenever the content view of the screen changes
     * (due to a call to
     * {@link Window#setContentView(View, android.view.ViewGroup.LayoutParams)
     * Window.setContentView} or
     * {@link Window#addContentView(View, android.view.ViewGroup.LayoutParams)
     * Window.addContentView}).
     */
    public void onContentChanged();

    /**
     * This hook is called whenever the window focus changes.  See
     * {@link View#onWindowFocusChanged(boolean)
     * View.onWindowFocusChanged(boolean)} for more information.
     *
     * @param hasFocus Whether the window now has focus.
     */
    public void onWindowFocusChanged(boolean hasFocus);

    /**
     * Called when the window has been attached to the window manager.
     * See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
     * for more information.
     */
    public void onAttachedToWindow();

    /**
     * Called when the window has been attached to the window manager.
     * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
     * for more information.
     */
    public void onDetachedFromWindow();

    /**
     * Called when a panel is being closed.  If another logical subsequent
     * panel is being opened (and this panel is being closed to make room for the subsequent
     * panel), this method will NOT be called.
     *
     * @param featureId The panel that is being displayed.
     * @param menu If onCreatePanelView() returned null, this is the Menu
     *            being displayed in the panel.
     */
    public void onPanelClosed(int featureId, Menu menu);

    /**
     * Called when the user signals the desire to start a search.
     *
     * @return true if search launched, false if activity refuses (blocks)
     *
     * @see android.app.Activity#onSearchRequested()
     */
    public boolean onSearchRequested();

    /**
     * Called when an action mode is being started for this window. Gives the
     * callback an opportunity to handle the action mode in its own unique and
     * beautiful way. If this method returns null the system can choose a way
     * to present the mode or choose not to start the mode at all.
     *
     * @param callback Callback to control the lifecycle of this action mode
     * @return The ActionMode that was started, or null if the system should present it
     */
    @Nullable
    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback);

    /**
     * Called when an action mode has been started. The appropriate mode callback
     * method will have already been invoked.
     *
     * @param mode The new mode that has just been started.
     */
    public void onActionModeStarted(ActionMode mode);

    /**
     * Called when an action mode has been finished. The appropriate mode callback
     * method will have already been invoked.
     *
     * @param mode The mode that was just finished.
     */
    public void onActionModeFinished(ActionMode mode);
}

其中常见的有如下几个回调:

  • public boolean dispatchTouchEvent(MotionEvent event);
  • public void onWindowFocusChanged(boolean hasFocus);
  • public void onAttachedToWindow();
  • public void onDetachedFromWindow();

由此可见,很多关于Window的回调都是在Activity中处理的,它们的关系非常紧密。

当Activity被创建时,会为该Activity创建一个Window对象,由于Activity实现了Window.Callback,因此对于Window的操作就会在Activity中回调。创建完Window后,就需要把Activity的视图附加到Window上了。这个过程的步骤如下:

1.Activity中的实现

/**
 * Set the activity content from a layout resource.  The resource will be
 * inflated, adding all top-level views to the activity.
 *
 * @param layoutResID Resource ID to be inflated.
 *
 * @see #setContentView(android.view.View)
 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
 */
public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

2.Activity把添加视图的任务交给Window来处理,而Window会先创建一个DecorView(是一个FrameLayout),DecorView包含一个ID为android.R.id.content的内容区,其实质也是一个FrameLayout。
3. 在Activity中经常调用的setContentView(int resId),就是把我们的视图添加到DecorView的内容区,也正因为如此,这个添加视图的方法不叫setView,而叫setContentView。添加完成后,Activity的onContentChanged被回调。
4. DecorView添加好视图后,还需要使用WindowMagager的addView方法将其添加到Window中。
5. 以上步骤之后,所添加的视图并不可见,当Activity执行onResume()之后,才会将DecorView设置为可见,这样用户才真正看到了Activity的视图并可以接收用户交互。

状态栏与导航栏

  1. 状态栏与导航栏也是window,并且与当前界面Activity处于不同的window。状态栏与导航栏window属于系统window,优先级高于普通window,为透明背景,覆盖于App的界面之上,在DecorView中对应状态栏和导航栏的位置有两个view占位。
  2. 我们通常看到状态栏和导航栏是有颜色的,并且可以通过诸如getWindow().setStatusBarColor(Color.RED)代码来设置颜色,其本质上并非真正改变状态栏的颜色,而是修改了DecorView中占位视图的颜色。
  3. 可以使用adb shell dumpsys window命令来分析当前的window状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值