View加载详解
来源:互联网 发布:阿里云学生机续费 编辑:程序博客网 时间:2024/06/03 19:41
文章一开始我要对前面一篇文章做点补充
相信大家都知道View有两个方法。
public boolean post(Runnable action) public boolean postDelayed(Runnable action, long delayMillis) {
这两个方法也可以实现对View的包装(实现延时发送消息和更新UI)那么它和Handler有什么区别呢下面给出源码。
/** * <p>Causes the Runnable to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> *<BR>使Runnable被添加到消息队列。运行将运行在用户界面线程。@param行动的运行将被执行。 * @返回返回true如果运行成功放置到 *消息队列。执行失败将返回False,通常是因为 * looper处理消息队列退出。 * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. * * @see #post * @see #removeCallbacks */ public boolean postDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); return true; }
/** * <p>Causes the Runnable to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> *使Runnable被添加到消息队列,可以运行在指定时间的流逝之后 * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. * * @see #post * @see #removeCallbacks */ public boolean postDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Assume that post will succeed later ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); return true; }
关键的注释我也进行的翻译,相信大家现在已经明白了。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。
这种情况下,由于不是在新的线程中使用,所以千万别做复杂的计算逻辑,更不能进行耗时操作了
那大家又会问什么是ViewRootImpl?这特么又是什么鬼,这里给大家讲解一下。我们从最基础的讲起。
相信大家都知道我们通过调用setContentView(int layoutResID)把xml文件加载到当前Activity上的。
那我们就从setContentView讲起。
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
setContentView方法实现很简单,里面调用两个方法,我们这里分析第一个方法:
getWindow()得到一个Window对象 mWindow ,Window是一个抽象类,用来描述Activity视图最顶端的窗口显示和行为操作。Window是一个抽象类,那么里面的setContentView(layoutResID)是一个抽象方法,并没有具体的实现,既然Window是一个抽象类,那么在Activity里面就有一个Window抽象类的实现。们查找代码发现 mWindow对象赋值方法如下。
mWindow = PolicyManager.makeNewWindow(this);
继续看看 PolicyManager类
public final class More PolicyManager {30 private static final String POLICY_IMPL_CLASS_NAME =31 "com.android.internal.policy.impl.Policy";3233 private static final IPolicy sPolicy;3435 static {36 // Pull in the actual implementation of the policy at run-time37 try {38 Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);39 sPolicy = (IPolicy)policyClass.newInstance();40 } catch (ClassNotFoundException ex) {41 throw new RuntimeException(42 POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);43 } catch (InstantiationException ex) {44 throw new RuntimeException(45 POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);46 } catch (IllegalAccessException ex) {47 throw new RuntimeException(48 POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);49 }50 }5152 // Cannot instantiate this class53 private More PolicyManager() {}5455 // The static methods to spawn new policy-specific objects56 public static Window More makeNewWindow(Context context) {57 return sPolicy.makeNewWindow(context);58 }
通过分析我们发现sPolicy对象是有第 38,39行通过径”com.android.internal.policy.impl.Policy“生成的(通过反射拿到类的对象并且实例化)重点57,
public Window makeNewWindow(Context context) { return new PhoneWindow(context); }
由此可见,我们终于找到Activity类中的 mWindow对象的实现类了,就是PhoneWindow类。
可能又会有些童学问什么是反射呢?what Fuck,要是特么这样扯下去,估计三天三夜也扯不完,等这篇写完后,会专一分一篇来讲解Java的反射机制。
到PhoneWindow类发现了setContentView方法的实现也在这个类里面了
@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) { 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 { mLayoutInflater.inflate(layoutResID, mContentParent); } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
首先判断mContentParent是否为null,mContentParent是什么呢?接下来会分析,一开始条件成立,进入installDecor()方法
到installDecor()方法里,实现如下:
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // Set up decor part of UI to ignore fitsSystemWindows if appropriate. mDecor.makeOptionalFitsSystemWindows(); final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( R.id.decor_content_parent); if (decorContentParent != null) { mDecorContentParent = decorContentParent; mDecorContentParent.setWindowCallback(getCallback()); if (mDecorContentParent.getTitle() == null) { mDecorContentParent.setWindowTitle(mTitle); } final int localFeatures = getLocalFeatures(); for (int i = 0; i < FEATURE_MAX; i++) { if ((localFeatures & (1 << i)) != 0) { mDecorContentParent.initFeature(i); } } mDecorContentParent.setUiOptions(mUiOptions); if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 || (mIconRes != 0 && !mDecorContentParent.hasIcon())) { mDecorContentParent.setIcon(mIconRes); } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 && mIconRes == 0 && !mDecorContentParent.hasIcon()) { mDecorContentParent.setIcon( getContext().getPackageManager().getDefaultActivityIcon()); mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; } if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 || (mLogoRes != 0 && !mDecorContentParent.hasLogo())) { mDecorContentParent.setLogo(mLogoRes); } // Invalidate if the panel menu hasn't been created before this. // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu // being called in the middle of onCreate or similar. // A pending invalidation will typically be resolved before the posted message // would run normally in order to satisfy instance state restoration. PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); if (!isDestroyed() && (st == null || st.menu == null)) { invalidatePanelMenu(FEATURE_ACTION_BAR); } } else { mTitleView = (TextView)findViewById(R.id.title); if (mTitleView != null) { mTitleView.setLayoutDirection(mDecor.getLayoutDirection()); if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { View titleContainer = findViewById( 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); } } } if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) { mDecor.setBackgroundFallback(mBackgroundFallbackResource); } // Only inflate or create a new TransitionManager if the caller hasn't // already set a custom one. if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) { if (mTransitionManager == null) { final int transitionRes = getWindowStyle().getResourceId( R.styleable.Window_windowContentTransitionManager, 0); if (transitionRes != 0) { final TransitionInflater inflater = TransitionInflater.from(getContext()); mTransitionManager = inflater.inflateTransitionManager(transitionRes, mContentParent); } else { mTransitionManager = new TransitionManager(); } } mEnterTransition = getTransition(mEnterTransition, null, R.styleable.Window_windowEnterTransition); mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowReturnTransition); mExitTransition = getTransition(mExitTransition, null, R.styleable.Window_windowExitTransition); mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowReenterTransition); mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null, R.styleable.Window_windowSharedElementEnterTransition); mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowSharedElementReturnTransition); mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null, R.styleable.Window_windowSharedElementExitTransition); mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowSharedElementReenterTransition); if (mAllowEnterTransitionOverlap == null) { mAllowEnterTransitionOverlap = getWindowStyle().getBoolean( R.styleable.Window_windowAllowEnterTransitionOverlap, true); } if (mAllowReturnTransitionOverlap == null) { mAllowReturnTransitionOverlap = getWindowStyle().getBoolean( R.styleable.Window_windowAllowReturnTransitionOverlap, true); } if (mBackgroundFadeDurationMillis < 0) { mBackgroundFadeDurationMillis = getWindowStyle().getInteger( R.styleable.Window_windowTransitionBackgroundFadeDuration, DEFAULT_BACKGROUND_FADE_DURATION_MS); } if (mSharedElementsUseOverlay == null) { mSharedElementsUseOverlay = getWindowStyle().getBoolean( R.styleable.Window_windowSharedElementsUseOverlay, true); } } } }
重点在代码的第 3 行我们看到 mDecor = generateDecor();方法调用,继续跳进 generateDecor()方法:
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
这里生成一个DecorView对象,DecorView是PhoneWindow类的内部类,继承自FrameLayout。到目前为止,setContentView方法里生成一个FrameLayout类型的DecorView组件。
继续分析代码,看第 11 行:
mContentParent = generateLayout(mDecor);
把 DecorView 对象 mDecor 作为参数传递给 generateLayout方法得到 mContentParent。generateLayout()方法中的代码实现如下:protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle(); ......... /**以下这些是Activity 窗口属性特征的设置*/ //窗口是否浮动,一般用于Dialog窗口是否浮动:是否显示在布局的正中间。 mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, 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(R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); } //ActionBar导航栏是否不占布局空间叠加显示在当前窗口之上。 if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS); } //当前Activity是否支持全屏 if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } ................ //设置状态栏的颜色 if (!mForcedStatusBarColor) { mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); } //设置导航栏的颜色 if (!mForcedNavigationBarColor) { mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); } if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( R.styleable.Window_windowCloseOnTouchOutside, false)) { setCloseOnTouchOutsideIfNotSet(true); } } WindowManager.LayoutParams params = getAttributes(); //设置输入法的状态 if (!hasSoftInputMode()) { params.softInputMode = a.getInt( R.styleable.Window_windowSoftInputMode, params.softInputMode); } if (a.getBoolean(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); } } //设置当前Activity的出现动画效果 if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( 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( R.styleable.Window_windowBackground, 0); } if (mFrameResource == 0) { mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0); } mBackgroundFallbackResource = a.getResourceId( R.styleable.Window_windowBackgroundFallback, 0); if (false) { System.out.println("Background: " + Integer.toHexString(mBackgroundResource) + " Frame: " + Integer.toHexString(mFrameResource)); } } mElevation = a.getDimension(R.styleable.Window_windowElevation, 0); mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false); mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT); } //以下代码为当前Activity窗口添加 decor根布局。 // Inflate the window decor. int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = 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 = 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( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = 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( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); //通过布局添加器LayoutInflater获取layoutResource布局, View in = mLayoutInflater.inflate(layoutResource, null); //将XML资源为layoutResource的布局添加到decor容器里面,至此PhoneWindow 内部类DecorView就添加了之布局 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; //此处很重要,通过findViewById找到 contentParent容器,也是该方法的返回值。 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); } } if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { registerSwipeCallbacks(); } // Remaining setup -- of background and title -- that only applies // to top-level windows. //以下代码设置Activity窗口的背景,标题等 if (getContainer() == null) { final Drawable background; if (mBackgroundResource != 0) { background = getContext().getDrawable(mBackgroundResource); } else { background = mBackgroundDrawable; } mDecor.setWindowBackground(background); final Drawable frame; if (mFrameResource != 0) { frame = getContext().getDrawable(mFrameResource); } else { frame = null; } mDecor.setWindowFrame(frame); mDecor.setElevation(mElevation); mDecor.setClipToOutline(mClipToOutline); if (mTitle != null) { setTitle(mTitle); } if (mTitleColor == 0) { mTitleColor = mTextColor; } setTitleColor(mTitleColor); } mDecor.finishChanging(); return contentParent; }
以上代码代比较复杂,主要做了以下几件事情。
从第 8 行到第110行,主要是初始化窗口的特征,是否显示标题栏,是否全屏,是否支持ActionBar浮动等等。
第112-187行,主要是给容器DecorView添加id为layoutResource布局的根布局。待会分析layoutResource布局。
第178行,通过LayoutInflater 将xml布局转换成VIEW.
第180行将找到的View添加到DecorView根布局当中。
第184行,从根布局中找到id为R.id.content的 contentParent 容器。也就是当前方法的返回值。
接下来,看看 id为layoutResource的布局到底实现了什么?我们来看看171行的R.layout.screen_simple;资源
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <ViewStub android:id="@+id/action_mode_bar_stub" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" /> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>
原来我们的DecorView根布局里面添加了类似上面的布局,线性布局LinearLaout里包含两个组件,ViewStub是懒加载,默认不显示,FrameLayout是什么呢?看看id=content,就是我们184行找到的父容器 contentParent。那么这个父容器 contentParent有什么作用呢?
我们回到 Step4 的第 11行,mContentParent = generateLayout(mDecor); 获得 父容器 mContentParent。我们再次回到 Step3步的第17行, mLayoutInflater.inflate(layoutResID, mContentParent); 这里通过LayoutInflater将 setContentView(layoutResID)传进来的布局id加载到 父容器mContentParent中,至此,setContentView就将布局添加到Activity里面了。
现在我们来梳理一下流程:
Activity setContentView—>Window setContentView—>PhoneWindow setContentView—->PhoneWindow installDecor—–>PhoneWindow generateLayout——>PhoneWindow mLayoutInflater.inflate(layoutResID, mContentParent);
Activity 类中有一个Window抽象类的实现PhoneWindow类,该类中有个内部类DecorView,继承自FrameLayout,在DecorView容器中添加了根布局,根布局中包含了一个id为 contnet的FrameLayout 内容布局,我们的Activity加载的布局xml最后添加到 id为content的FrameLayout布局当中了
1.关于requestWindowFeature(Window.FEATURE_NO_TITLE); 去除标题栏的疑问,如果你自己的xxxActivity是继承自Activity,那么恭喜你使用以上方法可以去除标题栏,如果你自己的xxxActivity是继承自AppCompatActivity或者ActionBarActivity,那么很遗憾告诉你,此次系统默认的标题栏已经在主题中去除,此时显示的标题栏是ActionBar导航栏,如果需要去除导航栏,你可以通过如下代码:getSupportActionBar().hide();来隐藏导航栏。
2.requestWindowFeature(Window.FEATURE_NO_TITLE);方法需要在 setContentView方法之前使用,由上面 Step5分析可得,设置Activity Window 特征是在setContentView方法中设置的,因此,如果需要改变Activity Window窗口特征,需要在setContentView方法之前。其实这里有疑问???为什么设置全屏的方法getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
关于mLayoutInflater.inflate我会在下篇给大家详细讲解的。
太特么累了,我要喝脉动。
- View加载详解
- View加载详解(一)
- View加载详解(二)
- ViewStub(惰性加载View)详解
- view 详解 (一) 将view加载到窗口过程分析
- View加载
- sendMessage(View view)详解
- view的加载流程
- ViewFlipper动态加载View
- UITextView 加载其他View
- ViewFlipper动态加载View
- view全屏加载
- ViewFlipper动态加载View
- ViewFlipper动态加载View
- 动态加载view
- view加载初探
- Android View 加载进度条
- 加载xib自定义View
- JS中六种数据类型(一)——Undefined
- Android:GPS坐标转高德地图坐标
- Templates and Generic Programming
- 欢迎使用CSDN-markdown编辑器
- Android广播BroadcastReceiver
- View加载详解
- CornerStone SVN 管理基本操作
- jquery图表范例及推荐highchart+sparkline
- InfiniBand 技术及其在 Linux 系统中的配置简介
- Android开发必备应用
- list
- LeetCode Reverse Integer
- GB2312简体中文编码表
- cocos2d-x 通过JNI实现c/c++和Android的java层函数互调