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_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对象,这些关联关系是由窗口管理器来维护的
简单来说,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
- Android Window、PhoneWindow、Activity学习心得第二弹
- Android Window PhoneWindow Activity学习心得--第三弹
- Android Window、PhoneWindow、WindowManager、Activity学习心得 第一弹
- Activity has leaked window com.android.internal.policy.impl.PhoneWindow
- android Activity.setContentView和Window PhoneWindow类学习
- Android Window PhoneWindow DecorView
- android Activity has leaked window com.android.internal.policy.impl.PhoneWindow$Deco...
- Android异常-Activity has leaked window com.android.internal.policy.impl.PhoneWindow... was originally
- ERROR/WindowManager(***): Activity *** has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@*** that was ori
- Activity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorVie
- Activity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView
- Activity com.hotel.ui.LoginActivity has leaked window com.android.internal.policy.impl.PhoneWindow
- Activity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorVie
- Error:activity xxx has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@41971600
- Activity抛出异常has leaked window com.android.internal.policy.impl.PhoneWindow处理办法以及注意事项
- PhoneWindow & Activity
- android PhoneWindow
- has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView
- 微信公众平台开发正确或错误的返回码
- 【c++程序】统计字符串中字符出现次数
- 第三章
- 使用 CXF 做 webservice 简单例子
- UDP Hole punching 概述
- Android Window、PhoneWindow、Activity学习心得第二弹
- Java并发包中的几种ExecutorService
- 第14题
- 如何在cmd状态下 导入 mysql 的 sql文件到数据库中
- cpp 1124 显示星期几
- 《C#程序设计》第二次作业 简易记事本
- IPython notebook搭建
- jsf标签 验证器的使用
- 操作系统服务:logging日志记录模块