Android Window、PhoneWindow、Activity学习心得第二弹

来源:互联网 发布:bt网络用语是什么意思 编辑:程序博客网 时间:2024/05/18 01:22

Android Window、PhoneWindow、Activity学习心得第二弹

                                                                                                                                                  Window 分析

这里先给出部分源码
目录(Android 4.4/frameworks/base/core/java/android/view/Window.java)
public abstract class Window {public static final int FEATURE_OPTIONS_PANEL = 0;public static final int FEATURE_NO_TITLE = 1;.......public static final int FEATURE_CONTEXT_MENU = 6;public static final int FEATURE_CUSTOM_TITLE = 7;public static final int FEATURE_ACTION_BAR = 8;public static final int FEATURE_ACTION_BAR_OVERLAY = 9;.......public interface Callback {.....public boolean dispatchKeyEvent(KeyEvent event);......public boolean dispatchTouchEvent(MotionEvent event);.....public boolean dispatchGenericMotionEvent(MotionEvent event);....public View onCreatePanelView(int featureId);....public boolean onMenuItemSelected(int featureId, MenuItem item);.....public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);public void onContentChanged();public void onWindowFocusChanged(boolean hasFocus);public void onAttachedToWindow();public void onDetachedFromWindow();public void onPanelClosed(int featureId, Menu menu);public boolean onSearchRequested();public ActionMode onWindowStartingActionMode(ActionMode.Callback callback);public void onActionModeStarted(ActionMode mode);public void onActionModeFinished(ActionMode mode);}public void setCallback(Callback callback) {mCallback = callback;}public void setFlags(int flags, int mask) {final WindowManager.LayoutParams attrs = getAttributes();attrs.flags = (attrs.flags&~mask) | (flags&mask);if ((mask&WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0) {attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;        }mForcedWindowFlags |= mask;if (mCallback != null) {mCallback.onWindowAttributesChanged(attrs);}}public boolean requestFeature(int featureId) {final int flag = 1<<featureId;mFeatures |= flag;mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;return (mFeatures&flag) != 0;}public View findViewById(int id) {return getDecorView().findViewById(id);}public abstract void setContentView(int layoutResID);public abstract void setTitle(CharSequence title);public abstract LayoutInflater getLayoutInflater();}

首先,正如我们所知道的通过回调的方式Activity implements  Window.Callback
而 Callback主要用来处理
按键事件(dispatchKeyEvent)
触摸事件 (dispatchTouchEvent) 滑动事件(dispatchTrackballEvent)等等一系列事件
设置菜单、ActionMOde、监控内容等一系列动作

正是因为
        mWindow = PolicyManager.makeNewWindow(this);        mWindow.setCallback(this);

监听回调,所以一系列最终的处理交给了Activity
当然,这里对于处理不做具体分析
接下来,我们看看
Window属性

       FEATURE_OPTIONS_PANEL = 0; 功能不明,参见后面的说明(默认使能)
  FEATURE_NO_TITLE = 1; 无标题栏
  FEATURE_PROGRESS = 2; 在标题栏上显示加载进度,例如webview加载网页时(条状进度条)
  FEATURE_LEFT_ICON = 3; 在标题栏左侧显示一个图标
  FEATURE_RIGHT_ICON = 4; 在标题栏右侧显示一个图标
  FEATURE_INDETERMINATE_PROGRESS = 5; 不确定的进度(圆圈状等待图标)
  FEATURE_CONTEXT_MENU = 6; 上下文菜单,相当于PC上的右键菜单(默认使能)
  FEATURE_CUSTOM_TITLE = 7; 自定义标题栏,该属性不能与其他标题栏属性合用
  FEATURE_OPENGL = 8; 如果开启OpenGL,那么2D将由OpenGL处理(OpenGL中2D是3D的子集)
  PROGRESS_VISIBILITY_ON = -1; 进度条可见
  PROGRESS_VISIBILITY_OFF = -2; 进度条不可见
  PROGRESS_INDETERMINATE_ON = -3; 开启不确定模式
  PROGRESS_INDETERMINATE_OFF = -4; 关闭不确定模式
  PROGRESS_START = 0; 第一进度条的最小值
  PROGRESS_END = 10000; 第一进度条的最大值
  PROGRESS_SECONDARY_START = 20000; 第二进度条的最小值
  PROGRESS_SECONDARY_END = 30000; 第二进度条的最大值

这些 Feature 和 Flag  有什么用呢,其实很明显他们决定了我们Activity外貌(风格和样式)
那么,接下来我们就来看看Window的具体实现类PhoneWindow
目录(Android 4.4/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java)
public class PhoneWindow extends Window implements MenuBuilder.Callback {......public PhoneWindow(Context context) {super(context);mLayoutInflater = LayoutInflater.from(context);}.....}

还记得我们第一弹中给出的关于Window、PhoneWindow、DecorView的关系图吗,这里我们稍作回顾




同样,我们知道
知道,每一个应用程序窗口的视图对象都有一个关联的ViewRoot对象,这些关联关系是由窗口管理器来维护的





 简单来说,ViewRoot相当于是MVC模型中的Controller,它有以下职责:

        1. 负责为应用程序窗口视图创建Surface。

        2. 配合WindowManagerService来管理系统的应用程序窗口。

        3. 负责管理、布局和渲染应用程序窗口视图的UI。

既然如此,那么接下来我们便开始构建视图UI
例如:我们创建自己的Activity,并在其中写道
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);}

同样我们跟踪发现
public class Activity extends ContextThemeWrapper          implements LayoutInflater.Factory,          Window.Callback, KeyEvent.Callback,          OnCreateContextMenuListener, ComponentCallbacks {      ......        private Window mWindow;      ......        public Window getWindow() {          return mWindow;      }      ......    public final boolean requestWindowFeature(int featureId) {    return getWindow().requestFeature(featureId);}    public void setContentView(int layoutResID) {          getWindow().setContentView(layoutResID);      }        ......  }  

他们最终交给了PhoneWindow(Window)处理
同样在PhoneWindow中
public class PhoneWindow extends Window implements MenuBuilder.Callback {    ......    // This is the view in which the window contents are placed. It is either    // mDecor itself, or a child of mDecor where the contents go.    private ViewGroup mContentParent;    ......public boolean requestFeature(int featureId) {//这里 requestFeature  必须在 setContentView之前      if (mContentParent != null) {            throw new AndroidRuntimeException("requestFeature() must be called before adding content");}final int features = getFeatures();//最终在Window中处理return super.requestFeature(featureId);}    @Override    public void setContentView(int layoutResID) {        if (mContentParent == null) {            installDecor();        } else {            mContentParent.removeAllViews();        }        mLayoutInflater.inflate(layoutResID, mContentParent);        final Callback cb = getCallback();        if (cb != null) {            cb.onContentChanged();        }    }    ......}


 PhoneWindow类的成员变量mContentParent用来描述一个类型为DecorView的视图对象,或者这个类型为DecorView的视图对象的一个子视图对象,用作UI容器。当它的值等于null的时候,就说明正在处理的应用程序窗口的视图对象还没有创建。在这种情况下,就会调用成员函数installDecor来创建应用程序窗口视图对象。否则的话,就说明是要重新设置应用程序窗口的视图。在重新设置之前,首先调用成员变量mContentParent所描述的一个ViewGroup对象来移除原来的UI内空。

由于我们是在Activity组件启动的过程中创建应用程序窗口视图的,因此,我们就假设此时PhoneWindow类的成员变量mContentParent的值等于null。接下来,函数就会调用成员函数installDecor来创建应用程序窗口视图对象,接着再通过调用PhoneWindow类的成员变量mLayoutInflater所描述的一个LayoutInflater对象的成员函数inflate来将参数layoutResID所描述的一个UI布局设置到前面所创建的应用程序窗口视图中去,最后还会调用一个Callback接口的成员函数onContentChanged来通知对应的Activity组件,它的视图内容发生改变了。

那么接下来我们便开始初始化DecorView
public class PhoneWindow extends Window implements MenuBuilder.Callback {    ......    // This is the top-level view of the window, containing the window decor.    private DecorView mDecor;    ......    // This is the view in which the window contents are placed. It is either    // mDecor itself, or a child of mDecor where the contents go.    private ViewGroup mContentParent;    ......    private TextView mTitleView;    ......    private CharSequence mTitle = null;    ......    private void installDecor() {        if (mDecor == null) {            mDecor = generateDecor();            ......        }        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);            if (mTitleView != null) {                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {                    View titleContainer =                            findViewById(com.android.internal.R.id.title_container);                    if (titleContainer != null) {                        titleContainer.setVisibility(View.GONE);                    } else {                        mTitleView.setVisibility(View.GONE);                    }                    if (mContentParent instanceof FrameLayout) {                        ((FrameLayout)mContentParent).setForeground(null);                    }                } else {                    mTitleView.setText(mTitle);                }            }        }    }    ......}

接下来,我们便开始构建
 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {......public DecorView(Context context, int featureId) {       super(context);       mFeatureId = featureId;}}protected DecorView generateDecor() {             return new DecorView(getContext(), -1);}protected ViewGroup generateLayout(DecorView decor) {        TypedArray a = getWindowStyle();        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);        mIsTranslucent = a.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, false);        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)                & (~getForcedWindowFlags());        if (mIsFloating) {                setLayout(WRAP_CONTENT, WRAP_CONTENT);            setFlags(0, flagsToUpdate);        } else {            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);}        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {            requestFeature(FEATURE_ACTION_BAR);}        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {            requestFeature(FEATURE_ACTION_BAR_OVERLAY);}if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {            requestFeature(FEATURE_ACTION_MODE_OVERLAY);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentStatus,                false)) {            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS                    & (~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation,                false)) {            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION                    & (~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,                getContext().getApplicationInfo().targetSdkVersion                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));        }        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,                    mFixedWidthMajor);        }        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,                    mFixedWidthMinor);        }        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,                    mFixedHeightMajor);        }        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,                    mFixedHeightMinor);        }        final Context context = getContext();        final int targetSdk = context.getApplicationInfo().targetSdkVersion;        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;        final boolean targetHcNeedsOptions = context.getResources().getBoolean(                com.android.internal.R.bool.target_honeycomb_needs_options_menu);        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);        } else {            clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);        }        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion                >= android.os.Build.VERSION_CODES.HONEYCOMB) {            if (a.getBoolean(                    com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,                    false)) {                setCloseOnTouchOutsideIfNotSet(true);            }        }        WindowManager.LayoutParams params = getAttributes();        if (!hasSoftInputMode()) {            params.softInputMode = a.getInt(                    com.android.internal.R.styleable.Window_windowSoftInputMode,                    params.softInputMode);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,                mIsFloating)) {            /* All dialogs should have the window dimmed */            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;            }            if (!haveDimAmount()) {                params.dimAmount = a.getFloat(                        android.R.styleable.Window_backgroundDimAmount, 0.5f);            }        }        if (params.windowAnimations == 0) {            params.windowAnimations = a.getResourceId(                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);        }        // The rest are only done if this window is not embedded; otherwise,        // the values are inherited from our container.        if (getContainer() == null) {            if (mBackgroundDrawable == null) {                if (mBackgroundResource == 0) {                    mBackgroundResource = a.getResourceId(                            com.android.internal.R.styleable.Window_windowBackground, 0);                }                if (mFrameResource == 0) {                    mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);                }                if (false) {                    System.out.println("Background: "                            + Integer.toHexString(mBackgroundResource) + " Frame: "                            + Integer.toHexString(mFrameResource));                }            }            mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);        }        // Inflate the window decor.        int layoutResource;        int features = getLocalFeatures();        // System.out.println("Features: 0x" + Integer.toHexString(features));        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = com.android.internal.R.layout.screen_title_icons;            }            // XXX Remove this once action bar supports these features.            removeFeature(FEATURE_ACTION_BAR);            // System.out.println("Title Icons!");        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {            // Special case for a window with only a progress bar (and title).            // XXX Need to have a no-title version of embedded windows.            layoutResource = com.android.internal.R.layout.screen_progress;            // System.out.println("Progress!");        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {            // Special case for a window with a custom title.            // If the window is floating, we need a dialog layout            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = com.android.internal.R.layout.screen_custom_title;            }            // XXX Remove this once action bar supports these features.            removeFeature(FEATURE_ACTION_BAR);        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {            // If no other features and not embedded, only need a title.            // If the window is floating, we need a dialog layout            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {                layoutResource = com.android.internal.R.layout.screen_action_bar;            } else {                layoutResource = com.android.internal.R.layout.screen_title;            }            // System.out.println("Title!");        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;        } else {            // Embedded, so no decoration is needed.            layoutResource = com.android.internal.R.layout.screen_simple;            // System.out.println("Simple!");        }        mDecor.startChanging();        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "DecorView-inflate");        View in = mLayoutInflater.inflate(layoutResource, null);        Trace.traceEnd(Trace.TRACE_TAG_VIEW);                //add for Multi window        if (mMultiWindow && !mIsFloating){            if (context.getPackageName().contains("launcher") || mIsTranslucent){                Log.d(TAG, "MW phoneWindow do nothing");            } else {               Log.d(TAG, "MW PhoneWindow decor start ");                in = prepareMultiWindow(context,in);               Log.d(TAG, "MW PhoneWindow decor end ");            }        }        //end        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);        if (contentParent == null) {            throw new RuntimeException("Window couldn't find content container view");        }        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {            ProgressBar progress = getCircularProgressBar(false);            if (progress != null) {                progress.setIndeterminate(true);            }        }        //Remaining setup - of background and title - that only applies        //to top-level windows.        if (getContainer() == null) {            Drawable drawable = mBackgroundDrawable;            if (mBackgroundResource != 0) {                drawable = getContext().getResources().getDrawable(mBackgroundResource);            }            mDecor.setWindowBackground(drawable);            drawable = null;            if (mFrameResource != 0) {                drawable = getContext().getResources().getDrawable(mFrameResource);            }            mDecor.setWindowFrame(drawable);            // System.out.println("Text=" + Integer.toHexString(mTextColor) +            // " Sel=" + Integer.toHexString(mTextSelectedColor) +            // " Title=" + Integer.toHexString(mTitleColor));            if (mTitleColor == 0) {                mTitleColor = mTextColor;            }            if (mTitle != null) {                setTitle(mTitle);            }            setTitleColor(mTitleColor);        }        mDecor.finishChanging();        return contentParent;

到了这里,我们依据不同的Feature和Flag 来初始化layout  算是初步完成


0 0
原创粉丝点击