一、程序結構
Android原生應用採用了MVC的架構設計模式,因此可以將一個Android APP中的對象歸爲Model、View或Controller中的一種。
具體到某個實際的APP結構中,它一般會由若干個activity、layout文件和自定義類組成,activity是Android SDK中Activity類的實例,負責管理用戶與應用界面的交互,應用的功能是通過編寫Activity子類來實現的;layout文件則用於定義需要顯示的UI對象以及指定它們在屏幕上所處的位置,layout文件的後綴爲XML。
由此可知,layout文件等屬於視圖對象,此外Android還自帶了很多可配置的視圖類,在後面逐步瞭解。控制器則通常是Activity、Fragment或Service的子類。
二、layout文件內容
layout文件定義了界面顯示的UI組件及其佈局方式,對於下面這樣一個簡單界面
其layout文件內容爲:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/question_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="24dp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/true_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/true_button" /> <Button android:id="@+id/false_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/false_button" /> <Button android:id="@+id/cheat_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/cheat_button"/> <Button android:id="@+id/next_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/next_button" android:drawableRight="@drawable/arrow_right"/> </LinearLayout> </LinearLayout>
其組成有:
一個垂直的LinerLayout組件,包含一個TextView組件和一個水平的LinerLayout組件;
水平的LinerLayout組件中又包含4個Button組件。
先看一下這裏麪包含的一些後續會經常用到的屬性:
1. android:layout_width和android:layout_height,可以設置爲wrap_content(視圖與其父視圖大小相同)或match_content(視圖會根據其內容自動調整大小)。可以看到作爲根節點的LinerLayout也有layout_width和layout_height屬性,這是因爲Android系統爲其提供了容納整個應用的父視圖。
2.android:orientation,orientation是LinerLayout組件具有的屬性,它指定LinerLayout的子組件是水平放置還是水平放置。
3.android:text,TextView和Button具有text屬性,指定組件要顯示的文字內容。
三、字符串資源
可以看到android:text的值爲類似"@string/next_button"這樣的形式,這是對字符串資源的應用,字符串資源(string resource)在string.xml中定義。通過把字符串內容放置在字符串資源,然後再間接引用它們,這樣可以方便地修改需要顯示的內容,更重要的是便於應用的本地化。
比如@string/next_button在字符串資源中的形式爲:
<string name="next_button">Next</string>
那麼爲什麼在layout文件中輸入“@string/”後,Android會自動提示出“next_button”呢,這要從資源ID說起。非代碼形式的內容都屬於資源,比如圖像文件、音頻文件、XML文件等,資源文件都存放在app/res的子目錄下,string.xml的路徑爲app/res/values,layout.xml的路徑爲app/res/layout,要獲取這些xml中定義的資源需要先知道對應的資源ID,所有資源的ID都存放在R.java文件中,將Android Studio的項目視圖切換爲Project後,可根據路徑app/build/generated/source/r/debug/對於項目包名稱/R.java找到R文件,在這裏我們可以在public static final class string下找到
public static final int next_button=0x7f0b0025,這便是next_button的資源ID,資源ID都是int型。
R文件在編譯時自動生成,手動修改可能會導致未知錯誤。修改資源內容後,R文件不會實時刷新,只有在應用安裝到模擬器或物理設備時纔會重新生成,Android Studio還另外保存有一份用於代碼編譯的隱藏的R文件。
四、Activity
1.組件的引用
一個頁面的Activity與layout的名稱有對應關係,比如activity_quiz.xml對應的activity名稱爲QuizActivity.java。在Activity中使用組件的第一步,也是通過資源ID獲取該組件,比如引用一個Button時的代碼爲:
private Button mTrueButton;
...
mTrueButton=(Button) findViewById(R.id.true_button);
拿到組件後,就可以做後續的操作了,比如爲Button設置監聽器(listener):
mTrueButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
}
});
或者改變TextView的顯示內容:
mQuestionTextView=(TextView) findViewById(R.id.question_text_view);
mQuestionTextView.setText(“New Text”);
2.Activity的生命週期
在使用Android Studio嚮導創建一個Activity後,默認會有onCreate方法:
@Override
protected void onCreate(Bundle savedinstanceState){
}
onCreate屬於Activity的生命週期方法,此外還有onStart(), onResume(), onPause(), onStop(), onDestroy()。頁面在不同的狀態間切換時,這些方法會被Android系統執行
啓動APP時,依次執行onCreate, onStart, onResume,直接進入運行態;
按Home鍵回到主頁時,依次執行onPause, onStop,進入停止態;
點擊返回鍵退出APP時,則會依次執行onPause, onStop和onDestroy,頁面被銷燬;
需要注意的是,旋轉屏幕時也會依次執行onCreate, onStart, onResume,這是因爲發生屏幕旋轉時,Android會銷燬當前activity,尋找合適的備選資源並重新創建。這一特性對於需要保存頁面狀態的activity會造成問題,因爲重新創建activity會丟失當前頁面的操作狀態,這是我們不希望發生的。要解決這個問題,可以覆蓋系統的onSaveInstanceState(Bundle)方法,在APP轉入停止狀態時,這個方法在onStop之前由系統調用,我們可以在這個方法中將activity視圖狀態相關的數據存入Bundle對象中:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
}
然後在每次重新創建activity時嘗試讀取Bundle中的內容:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mCurrentIndex = savedInstanceState.getInt(KEY_INDEX);
}
....
}
五、頁面通信
一般來說一個APP不可能只有一個頁面,通過主頁面打開子頁面時就涉及到了頁面間的通信。
Android使用基於Intent的通信方式,intent對象是component用來與操作系統通信的媒介工具。activity就是一種component對象。Intent是一種多用途通信工具,Intent類有多個構造方法,能滿足不同的使用需要。
1.從父頁面啓動子頁面
假設父頁面爲QuizActivity,子頁面爲CheatActivity,則從父頁面啓動子頁面的方法爲:
...
Intent intent = new Intent(QuizActivity.this, CheatActivity.class);
intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivity(intent);
...
使用intent.putExtra方法可以在啓動子頁面的同時傳入需要的值,然後子頁面可讀取傳入值的方法爲:
mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
2.子頁面返回結果給父頁面
在很多場景中都需要子頁面返回結果給父頁面。有兩個方法可以使用:
public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)
resultCode的值爲預定義常量,有:
Activity.RESULT_OK,對應確認操作
Activity.RESULT_CANCELED,對應取消操作
Activity.RESULT_FIRST_USER,用於自定義結果代碼
使用public final void setResult(int resultCode, Intent data)即可將結果數據通過Intent返回給父頁面。
相應的,在父頁面要取得子頁面的返回結果,需要覆蓋onActivityResult(int, int, Intent方法)
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK) { return; } if (requestCode == REQUEST_CODE_CHEAT) { if (data == null) { return; } //TODO } }