Window analysis of reading Android source code

Window analysis of reading Android source code

This article and the next article mainly analyze the content related to Window and WindowManager

Abstract base class for a top-level window look and behavior policy.

Window is a concept of a window in Android. We don't have much contact with it in daily development. We are more exposed to View, but View is presented through Window, and Window is the direct manager of View. The WindowManager assumes the responsibility of managing Window.

First, the window type

Window has three types in Android:

  • Application Window : z-indexBetween 1 and 99, it often corresponds to an Activity.
  • Child Window : z-indexBetween 1000 and 1999, it often cannot exist independently and needs to be attached to the parent Window, such as Dialog.
  • System Window : z-indexBetween 2000 and 2999, it often needs to declare permissions to create. For example, Toast, status bar, system volume bar, and error prompt box are all system windows.

z-indexIt is the concept of the hierarchy of Android windows. The larger the z-index, the more at the top level.

z-indexCorresponds to the type parameter in WindowManager.LayoutParams, specifically.

ApplicationWindow

  • public static final int FIRST_APPLICATION_WINDOW = 1;//1
  • public static final int TYPE_BASE_APPLICATION = 1;//The base value of the window, other window values ​​should be greater than this value
  • public static final int TYPE_APPLICATION = 2;//Ordinary application window type
  • public static final int TYPE_APPLICATION_STARTING = 3;//Application startup window type, used for the window displayed by the system before the application window starts.
  • public static final int TYPE_DRAWN_APPLICATION = 4;
  • public static final int LAST_APPLICATION_WINDOW = 99;//2

child window

  • public static final int FIRST_SUB_WINDOW = 1000;//Initial value of sub-window type
  • public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
  • public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
  • public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
  • public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
  • public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
  • public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
  • public static final int LAST_SUB_WINDOW = 1999;//Sub-window type end value

SystemWindow

  • public static final int FIRST_SYSTEM_WINDOW = 2000;//Initial value of system window type
  • public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//System status bar window
  • public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//Search bar window
  • public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//Call window
  • public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//System ALERT window
  • public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//Lock screen window
  • public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//TOAST窗口

2. Window parameters

An inner class is defined in WindowManager LayoutParams, which describes the parameter information of the window, mainly including:

  • public int x: window x-axis coordinate
  • public int y: window y-axis coordinate
  • public int type: window type
  • public int flags: window properties
  • public int softInputMode: input method keyboard mode

Regarding window properties, which control the behavior of the window, here are a few common ones:

  • FLAG_ALLOW_LOCK_WHILE_SCREEN_ON Allow screen lock on open screens as long as the window is visible
  • FLAG_NOT_FOCUSABLE The window cannot get the input focus. When this flag is set, FLAG_NOT_TOUCH_MODAL will also be set
  • FLAG_NOT_TOUCHABLE window does not receive any touch events
  • FLAG_NOT_TOUCH_MODAL The touch events outside the window area are passed to other windows, and they will only process touch events in the window area.
  • FLAG_KEEP_SCREEN_ON The screen will stay on as long as the window is visible
  • FLAG_LAYOUT_NO_LIMITS allows the window to go beyond the screen
  • FLAG_FULLSCREEN hides all screen decoration windows, such as full screen display in games and players
  • FLAG_SHOW_WHEN_LOCKED window can be displayed on top of the lock screen window
  • FLAG_IGNORE_CHEEK_PRESSES When the user's face is close to the screen (such as making a phone call), this event will not be responded to
  • FLAG_TURN_SCREEN_ON Turn the screen on when the window is displayed

Regarding the window type, it corresponds to the level of the window, which we also mentioned above.

Its constructor is also mainly for these parameters.

 public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
            public LayoutParams() {
                super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                type = TYPE_APPLICATION;
                format = PixelFormat.OPAQUE;
            }

            public LayoutParams(int _type) {
                super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                type = _type;
                format = PixelFormat.OPAQUE;
            }

            public LayoutParams(int _type, int _flags) {
                super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                type = _type;
                flags = _flags;
                format = PixelFormat.OPAQUE;
            }

            public LayoutParams(int _type, int _flags, int _format) {
                super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                type = _type;
                flags = _flags;
                format = _format;
            }

            public LayoutParams(int w, int h, int _type, int _flags, int _format) {
                super(w, h);
                type = _type;
                flags = _flags;
                format = _format;
            }

            public LayoutParams(int w, int h, int xpos, int ypos, int _type,
                    int _flags, int _format) {
                super(w, h);
                x = xpos;
                y = ypos;
                type = _type;
                flags = _flags;
                format = _format;
            }
 }

3. Window mode

We are familiar with the window mode, we will set it under the Activity tag in AndroidManifest.xml android:windowSoftInputMode="adjustNothing"to control the input keyboard display behavior.

There are 6 optional parameters, and there are 6 values ​​in the source code corresponding to them:

  • SOFT_INPUT_STATE_UNSPECIFIED: The display state of the soft keyboard input area is not specified.
  • SOFT_INPUT_STATE_UNCHANGED: Do not change the display state of the soft keyboard input area.
  • SOFT_INPUT_STATE_HIDDEN: Hide the soft keyboard input area when appropriate, for example, when the user navigates to the current window.
  • SOFT_INPUT_STATE_ALWAYS_HIDDEN: Always hide the soft keyboard input area when the window has focus.
  • SOFT_INPUT_STATE_VISIBLE: Display the soft keyboard input area when appropriate, for example, when the user navigates to the current window.
  • SOFT_INPUT_STATE_ALWAYS_VISIBLE: Always show the soft keyboard input area when the window has focus.

Of course, we can also set the keyboard mode through code.

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

Fourth, the window callback

An interface is defined in Window Callback, Activity implements the Window.Callbackinterface, associates Activity with Window, and Window can hand over some events to Activity for processing.

 public interface Callback {

        //键盘事件分发
        public boolean dispatchKeyEvent(KeyEvent event);

        //触摸事件分发
        public boolean dispatchTouchEvent(MotionEvent event);

        //轨迹球事件分发
        public boolean dispatchTrackballEvent(MotionEvent event);

        //可见性事件分发
        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);

        //创建Panel View
        public View onCreatePanelView(int featureId);

        //创建menu
        public boolean onCreatePanelMenu(int featureId, Menu menu);

        //画板准备好时回调
        public boolean onPreparePanel(int featureId, View view, Menu menu);

        //menu打开时回调
        public boolean onMenuOpened(int featureId, Menu menu);

        //menu item被选择时回调
        public boolean onMenuItemSelected(int featureId, MenuItem item);

        //Window Attributes发生变化时回调
        public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);

        //Content View发生变化时回调
        public void onContentChanged();

        //窗口焦点发生变化时回调
        public void onWindowFocusChanged(boolean hasFocus);

        //Window被添加到WIndowManager时回调
        public void onAttachedToWindow();

        //Window被从WIndowManager中移除时回调
        public void onDetachedFromWindow();

         */
        //画板关闭时回调
        public void onPanelClosed(int featureId, Menu menu);

        //用户开始执行搜索操作时回调
        public boolean onSearchRequested();
    }

Five, window implementation

Window is an abstract class, and its only implementation class is PhoneWindow, PhoneWindowwhich contains the following:

  • private DecorView mDecor: DecorView is the top-level View in Activity, it is essentially a FrameLayout, generally speaking, it contains title bar and content bar (com.android.internal.R.id.content).
  • ViewGroup mContentParent: window content view, which is mDecor itself or its child View.
  • private ImageView mLeftIconView: upper left icon
  • private ImageView mRightIconView: top right icon
  • private ProgressBar mCircularProgressBar:圆形loading条
  • private ProgressBar mHorizontalProgressBar: horizontal loading bar
  • Some other transitions and listeners related to transition animations

Seeing these, do you feel familiar? This is what we often see in our daily development, and it PhoneWindowwas created in it. In addition, the actual implementation of the methods we often call in Activity is also
in PhoneWindow, let's take a look at them separately.

setContentView()

This is a method we are very familiar with, but we usually call it in the Activity, but its actual implementation is PhoneWindowthere.

public class PhoneWindow extends Window implements MenuBuilder.Callback {

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            //1. 如果没有DecorView则创建它,并将创建好的DecorView赋值给mContentParent
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //2. 将Activity传入的布局文件生成View并添加到mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            //3. 回调Window.Callback里的onContentChanged()方法,这个Callback也被Activity
            //所持有,因此它实际回调的是Activity里的onContentChanged()方法,通知Activity
            //视图已经发生改变。
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }    
}

This method mainly does two things:

  1. If not DecorView, create it and assign the created one DecorViewtomContentParent
  2. Generate View from the layout file passed in by Activity and add it mContentParentto
  3. Window.CallbackThe method in the callback onContentChanged(), this Callback is also held by the Activity, so it actually calls back the onContentChanged()method in the Activity to notify the Activity that the view has changed.

Creating a DecorView is done through the installDecor() method, and its logic is also very simple, that is, create a ViewGroup and return it to mDecorsum mContentParent.

public class PhoneWindow extends Window implements MenuBuilder.Callback {

 public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

  private void installDecor() {
         mForceDecorInstall = false;
         if (mDecor == null) {
             //生成DecorView
             mDecor = generateDecor(-1);
             mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
             mDecor.setIsRootNamespace(true);
             if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
             }
         } else {
             mDecor.setWindow(this);
         }
         if (mContentParent == null) {
             mContentParent = generateLayout(mDecor);
             ...
             } else {
                ...
             }
            ...
         }
     }

 protected ViewGroup generateLayout(DecorView decor) {
        //读取并设置主题颜色、状态栏颜色等信息
        ...
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //设置窗口参数等信息
        ...
        return contentParent;
    }    
}

Through the above processes, DecorViewit has been created and initialized, and the layout file in the Activity has also been successfully added ( PhoneWindowin mContentParentfact DecorView, it is the top-level View of the Activity)
, but it DecorViewhas not really been added to the Window by the WindowManager at this time. , it is still unable to accept the user's input information and focus events. At this time, it is equivalent to going to the Activity onCreate()process, and the interface has
not been displayed to the user.

Until it goes to the onResume()method of Activity, it will call the makeVisiable()method of Activity, and it is DecorViewnot really seen by the user.

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback {

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
}

Usually the above analysis, we understand the working principle of setContentView, in addition to addContentView, clearContentView, as their names, setContentView is to replace View, addContentView is to add View. The implementation principle is the same.

Well, the above is the whole content of this article. In the next article, we will analyze the content of WindowManager and analyze the process of adding, removing and updating Window.

appendix

At the end of the article, we will provide you with a WindowUtils tool class.

import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.view.Surface;
import android.view.Window;
import android.view.WindowManager;

public final class WindowUtils {

    /**
     * Don't let anyone instantiate this class.
     */
    private WindowUtils() {
        throw new Error("Do not need instantiate!");
    }

    /**
     * 获取当前窗口的旋转角度
     *
     * @param activity activity
     * @return  int
     */
    public static int getDisplayRotation(Activity activity) {
        switch (activity.getWindowManager().getDefaultDisplay().getRotation()) {
            case Surface.ROTATION_0:
                return 0;
            case Surface.ROTATION_90:
                return 90;
            case Surface.ROTATION_180:
                return 180;
            case Surface.ROTATION_270:
                return 270;
            default:
                return 0;
        }
    }

    /**
     * 当前是否是横屏
     *
     * @param context  context
     * @return  boolean
     */
    public static final boolean isLandscape(Context context) {
        return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
    }

    /**
     * 当前是否是竖屏
     *
     * @param context  context
     * @return   boolean
     */
    public static final boolean isPortrait(Context context) {
        return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
    }
    /**
     *  调整窗口的透明度  1.0f,0.5f 变暗
     * @param from  from>=0&&from<=1.0f
     * @param to  to>=0&&to<=1.0f
     * @param context  当前的activity
     */
    public static void dimBackground(final float from, final float to, Activity context) {
        final Window window = context.getWindow();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);
        valueAnimator.setDuration(500);
        valueAnimator.addUpdateListener(
                new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        WindowManager.LayoutParams params
                                = window.getAttributes();
                        params.alpha = (Float) animation.getAnimatedValue();
                        window.setAttributes(params);
                    }
                });
        valueAnimator.start();
    }
}

original address

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324851749&siteId=291194637