編輯:關於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.,
* 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();
}
}
實際上就是將layoutResId這個布局的視圖附加到mContentParent中。移步 : 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啟動時,就將這些內容就會顯示在手機上。
Android應用開發之簡易、大氣音樂播放器實現專輯倒影效果
今天要實現的功能是實現專輯倒影效果,這個功能已經屬於圖像處理方面的了,對圖像處理我不怎麼在行,等一下會介紹一個很實用的工具類,專門用來進行圖像處理的。這個工具類不是我寫的
Android開發之SoundPool使用詳解
使用SoundPool播放音效 如果應用程序經常播放密集、急促而又短暫的音效(如游戲音效)那麼使用MediaPlayer顯得有些不太適合了。因為MediaPlayer
android項目 之 記事本(13) ----- 查看圖片及播放錄音
本文是自己學習所做筆記,歡迎轉載,但請注明出處:http://blog.csdn.net/jesson20121020 今天就來實現下查看圖片及
實例講解Android中ContentProvider組件的使用方法
ContentProvider基本使用為了在應用程序之間交換數據,android提供了ContentProvider,ContentProvider是不同應用程序之間進行