編輯:關於Android編程
今天是中秋節,先祝各位中秋快樂吧。作為北漂的人,對於過節最大的感觸就是沒氣氛~ 中秋是一個特別重要的節日,小的時候過中秋都是特別快樂的,有月餅吃,和家人上月,過完中秋要去親戚家拜訪等等。現在對於我們來說也就是一個節日罷了,窩在家裡看點電視、看點書、吃頓好的,雖說生活好了,但日子過得沒啥滋味。廢話不多說,開始今天的學習吧。
對於學習編程的人而言,大多數人第一個項目都是著名的"Hello World",自從K&R開了這個先例,後面的人就很少有打破的。學習Android開發也是這樣,我們第一次創建應用,估計也就是運行程序,然後在模擬器上輸出一個Hello World,我們看到最簡單的Activity中的內容大致是這樣的:
public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); } }main_activity.xml大致是這樣的 :
然後執行程序,我們就可以看到模擬器中的Hello World了。
圖1
我們在整個過程中做的事情很少,在我們的main_activity.xml我們只有一個顯示文本的TextView,但是在上圖中卻還多了一個title。我們好奇的是整個過程是怎麼工作的?對於大型系統來說細節總是復雜的,在下水平有限,所以我們今天只來理一下它的基本脈絡。
一般來說我們設置頁面的內容視圖是都是通過setContentView方法,那麼我們就以2.3源碼為例就來看看Activity中的setContentView到底做了什麼吧。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD48cD48L3A+PHByZSBjbGFzcz0="brush:java;"> /** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. */ public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); } public Window getWindow() { return mWindow; } private Window mWindow;
我們可以看到,實際上調用的mWindow的setContentView方法,在Android Touch事件分發過程這篇文章中我們已經指出Window的實現類為PhoneWindow類,我們就移步到PhoneWindow的setConentView吧,核心源碼如下 :
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); // 1、生成DecorView } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent);// 2、將layoutResId的布局添加到mContentParent中 final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } } // 構建mDecor對象,並且初始化標題欄和Content Parent(我們要顯示的內容區域) private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); // 3、構建DecorView mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // 4、獲取ContentView容器,即顯示內容的區域 mTitleView = (TextView)findViewById(com.android.internal.R.id.title); 5、設置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); } } } } protected DecorView generateDecor() { return new DecorView(getContext(), -1); // 構建mDecor對象 }我們可以看到,setContentView的基本流程簡單概括就是如下幾步:
1、構建mDecor對象。mDecor就是整個窗口的頂層視圖,它主要包含了Title和Content View兩個區域 (參考圖1中的兩個區域 ),Title區域就是我們的標題欄,Content View區域就是顯示我們xml布局內容中的區域。關於mDecor對象更多說明也請參考Android Touch事件分發過程這篇文章;
2、設置一些關於窗口的屬性,初始化標題欄區域和內容顯示區域;
這裡比較復雜的就是generateLayout(mDecor)這個函數,我們一起來分析一下吧。
// 返回用於顯示我們設置的頁面內容的ViewGroup容器 protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. // 1、獲取窗口的Style屬性 TypedArray a = getWindowStyle(); if (false) { System.out.println("From style:"); String s = "Attrs:"; for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) { s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "=" + a.getString(i); } System.out.println(s); } // 窗口是否是浮動的 mIsFloating = a.getBoolean(com.android.internal.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); } // 設置是否不顯示title區域 if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } // 設置全屏的flag 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_windowShowWallpaper, false)) { setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); } 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; } 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. // 2、根據一些屬性來選擇不同的頂層視圖布局,例如設置了FEATURE_NO_TITLE的屬性,那麼就選擇沒有Title區域的那麼布局; // layoutResource布局就是整個Activity的布局,其中含有title區域和content區域,content區域就是用來顯示我通過 // setContentView設置進來的內容區域,也就是我們要顯示的視圖。 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) { layoutResource = com.android.internal.R.layout.dialog_title_icons; } else { layoutResource = com.android.internal.R.layout.screen_title_icons; } // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 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) { layoutResource = com.android.internal.R.layout.dialog_custom_title; } else { layoutResource = com.android.internal.R.layout.screen_custom_title; } } 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) { layoutResource = com.android.internal.R.layout.dialog_title; } else { layoutResource = com.android.internal.R.layout.screen_title; } // System.out.println("Title!"); } else { // Embedded, so no decoration is needed. layoutResource = com.android.internal.R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); // 3、加載視圖 View in = mLayoutInflater.inflate(layoutResource, null); // 4、將layoutResource的內容添加到mDecor中 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); // 5、獲取到我們的內容顯示區域,這是一個ViewGroup類型的,其實是FrameLayout 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); } } // 6、設置一些背景、title等屬性 // 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; }其實也就是這麼幾個步驟:
1、獲取用戶設置的一些屬性與Flag;
2、根據一些屬性選擇不同的頂層視圖布局,例如FEATURE_NO_TITLE則選擇沒有title的布局文件等;這裡我們看一個與圖1中符合的頂層布局吧,即layoutResource = com.android.internal.R.layout.screen_title的情形:
<frameLayout android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize" > </frameLayout> <frameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" />
我們可以看到有兩個區域,即title區域和content區域,generateLayout函數中的
// 5、獲取到我們的內容顯示區域,這是一個ViewGroup類型的,其實是FrameLayout ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
獲取的就是xml中id為content的FrameLayout,這個content就是我們的內容顯示區域。整個布局對應的效果如下 :
這兩個區域就組成了mDecor視圖,我們的main_activity.xml就是放在內容視圖這個區域的。
3、加載頂層布局文件,轉換為View,將其添加到mDecor中;
4、獲取內容容器Content Parent,即用於顯示我們的內容的區域;
5、設置一些背景圖和title等。
在經過這幾步,我們就得到了mContentParent,這就是用來裝載我們的視圖的ViewGroup。再回過頭來看setContentView函數:
public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); // 1、生成DecorView,並且根據窗口屬性加載頂級視圖布局、獲取mContentParent、設置一些基本屬性等 } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent);// 2、將layoutResId加載到mContentParent中,這裡的layoutResId就是我們的main_activity.xml final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } }我們看看LayoutInflater的inflate函數吧 :
/** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. * * @param resource ID for an XML layout resource to load (e.g., *實際上就是將layoutResId這個布局的視圖附加到mContentParent中。R.layout.main_page
) * @param root Optional view to be the parent of the generated hierarchy. * @return The root View of the inflated hierarchy. If root was supplied, * this is the root View; otherwise it is the root of the inflated * XML file. */ public View inflate(int resource, ViewGroup root) { return inflate(resource, root, root != null); } /** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. * * @param resource ID for an XML layout resource to load (e.g., *R.layout.main_page
) * @param root Optional view to be the parent of the generated hierarchy (if * attachToRoot is true), or else simply an object that * provides a set of LayoutParams values for root of the returned * hierarchy (if attachToRoot is false.) * @param attachToRoot Whether the inflated hierarchy should be attached to * the root parameter? If false, root is only used to create the * correct subclass of LayoutParams for the root view in the XML. * @return The root View of the inflated hierarchy. If root was supplied and * attachToRoot is true, this is root; otherwise it is the root of * the inflated XML file. */ public View inflate(int resource, ViewGroup root, boolean attachToRoot) { if (DEBUG) System.out.println("INFLATING from resource: " + resource); XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
移步 : DecorView 。
ViewGroup從語義上來說就是視圖組,它也繼承自View類,它其實就是視圖的容器。我們看官方的定義 :
* A ViewGroup is a special view that can contain other views * (called children.) The view group is the base class for layouts and views * containers. This class also defines the * {@link android.view.ViewGroup.LayoutParams} class which serves as the base * class for layouts parameters.我們通過ViewGroup來組織、管理子視圖,例如我們常見的FrameLayout、LinearLayout、RelativeLayout、ListView等都是ViewGroup類型,總之只要能包含其他View或者ViewGroup的都是ViewGroup類型。使用ViewGroup來構建視圖樹。
View就是UI界面上的一個可見的組件,任何在UI上可見的都為View的子類。我們看官方定義 :
* This class represents the basic building block for user interface components. A View * occupies a rectangular area on the screen and is responsible for drawing and * event handling. View is the base class for widgets, which are * used to create interactive UI components (buttons, text fields, etc.). The * {@link android.view.ViewGroup} subclass is the base class for layouts, which * are invisible containers that hold other Views (or other ViewGroups) and define * their layout properties.
TextView、Button、ImageView、FrameLayout、LinearLayout、ListView等都是View的子類。
整個窗口由Title區域和Content區域組成,Content區域就是我們要顯示內容的區域,在這個區域中mContentParent是根ViewGroup,由mContentParent組織、管理其子視圖,從而構建整個視圖樹。當Activity啟動時,就將這些內容就會顯示在手機上。
今天要實現的功能是實現專輯倒影效果,這個功能已經屬於圖像處理方面的了,對圖像處理我不怎麼在行,等一下會介紹一個很實用的工具類,專門用來進行圖像處理的。這個工具類不是我寫的
使用SoundPool播放音效 如果應用程序經常播放密集、急促而又短暫的音效(如游戲音效)那麼使用MediaPlayer顯得有些不太適合了。因為MediaPlayer
本文是自己學習所做筆記,歡迎轉載,但請注明出處:http://blog.csdn.net/jesson20121020 今天就來實現下查看圖片及
ContentProvider基本使用為了在應用程序之間交換數據,android提供了ContentProvider,ContentProvider是不同應用程序之間進行