編輯:關於Android編程
大家在平時的開發中,對於setContentView肯定不陌生,那麼對其內部的實現會不會比較好奇呢~~~有幸終於能看到一些PhoneWindow神馬的源碼,今天就帶大家來跑一回源碼~~
首先不用說,進入Activity的setContentView
public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); }
可以看到裡面獲取了Window,然後調用了Window的setContentView
這裡的Window的實現類是PhoneWindow(package com.android.internal.policy.impl;),我們直接看它的實現:
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
從這裡就能看出來mContentParent是個ViewGroup且包裹我們整個布局文件;而installDecor()估計就是去初始化我們這個mContentParent,一會我們會去驗證。
接下來,通過getCallBack拿到了一個CallBack對象,其實這個獲取到的這個CallBack就是我們Activity自己,你可以去看我們的Activity是實現了CallBack接口的。
這個Callback明顯就是一個回調,當PhoneWindow接收到系統分發給它的觸摸、IO、菜單等相關的事件時,可以回調相應的Activity進行處理。至於Callback可以回調哪些方法,自己看下這個接口的聲明方法即可。當然了這裡不是我們的關鍵,因為我們的setContentView裡面只是回調了onContentChanged,而onContentChanged在Activity中是空實現。
好了,接下來去看我們的installDecor()
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); //... } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); mTitleView = (TextView)findViewById(com.android.internal.R.id.title); if (mTitleView != null) { //根據FEATURE_NO_TITLE隱藏,或者設置mTitleView的值 //... } else { mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); if (mActionBar != null) { //設置ActionBar標題、圖標神馬的;根據FEATURE初始化Actionbar的一些顯示 //... } } } }
可以看到這裡不僅初始化mContentParent,而且在之前先調用generateDecor();初始化了一個mDecor,mDecor是DecorView對象,為FrameLayout的子類。
在得到mDecor以後設置其焦點的獲取方式為,當其子孫都不需要時,自己才獲取。
然後通過 generateLayout(mDecor);把mDecor做為參數傳入,然後獲取到了我們的mContentParent;
接下裡就開始通過findViewById進行獲取控件了,而這裡的findViewById的代碼是這樣的:
public View findViewById(int id) { return getDecorView().findViewById(id); }
這裡我們猜測下,首先去初始化mDecor,然後通過mDecor初始化了mContentParent,接下來mDecor就可以使用findViewById方法了。那麼我覺得,在初始化mDecor的方法
generateDecor()中,一定為我們的mDecor放入了布局或者控件(最簡單的就是使用inflate壓入了布局文件),而mContentParent可能就是mDecor中的某個子View。
是不是這樣呢?
我們一起來先看看generateDecor()方法的實現:
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
public DecorView(Context context, int featureId) { super(context); mFeatureId = featureId; }
很遺憾,我們的generateDecor()只是初始化了一個FrameLayout對象,並沒有在其內部壓入布局文件,看來我們的猜測有些問題;不過沒事,既然此方法沒有,那麼generateLayout(mDecor);中一定設置了layout文件,並且這名字也很像這麼回事。
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle(); //...Window_windowIsFloating,Window_windowNoTitle,Window_windowActionBar... //首先通過WindowStyle中設置的各種屬性,對Window進行requestFeature或者setFlags if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } //... if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } //...根據當前sdk的版本確定是否需要menukey WindowManager.LayoutParams params = getAttributes(); //通過a中設置的屬性,設置 params.softInputMode 軟鍵盤的模式; //如果當前是浮動Activity,在params中設置FLAG_DIM_BEHIND並記錄dimAmount的值。 //以及在params.windowAnimations記錄WindowAnimationStyle // 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!); } View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //... return contentParent; } }
然後就根據這些屬性的值,對我們的Window各種requestFeature,setFlags等等。所以這裡就是解析我們為Activity設置theme的地方,至於theme一般可以在AndroidManifest裡面進行設置。
接下來就到關鍵的部分了,21-75行:通過對features和mIsFloating的判斷,為layoutResource進行賦值,至於值可以為R.layout.screen_custom_title;R.layout.screen_action_bar;等等。至於features,除了theme中設置的,我們也可以在Activity的onCreate的setContentView之前進行requestFeature,也解釋了,為什麼需要在setContentView前調用requestFeature設置全屏什麼的。
得到了layoutResource以後,78行,通過LayoutInflater把布局轉化成view,加入到我們的decor,即傳入的mDecor中。
接下來81行:通過mDecor.findViewById傳入R.id.content(相信這個id大家或多或少都聽說過),返回mDecor(布局)中的id為content的View,一般為FrameLayout。
好了,可以看到我們的mDecor是一個FrameLayout,然後會根據theme去選擇系統中的布局文件,將布局文件通過inflate轉化為view,加入到mDecor中;這些布局文件中都包含一個id為content的FrameLayout,將其引用返回給mContentParent。
等我們的mContentParent有值了以後,還記得干嘛了麼?再貼一次PhoneWindow的setContentView
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
下面先來一張效果圖 根據圖片分析,要實現的有側邊欄DrawerLayout,ActionBar的顏色和菜單以及ActionBarDrawerTogg
本節為大家提供有關物理游戲的知識,講解了一個簡單的圓形自由落體Demo的編寫。本文要介紹的重力系統實際上是類似的。 &nbs
實現zip/tar的壓縮與解壓java中實際是提供了對 zip等壓縮格式的支持,但是為什麼這裡會用到ant呢?原因主要有兩個:1. java提供的類對於包括有
時候我們需要監聽ScroView的滑動情況,比如滑動了多少距離,是否滑到布局的頂部或者底部。可惜的是SDK並沒有相應的方法,不過倒是提供了一個 復制代碼 代碼如下:pro