用户界面开发基础
Activity是Adnroid中唯一可視化的應(yīng)用程序組件。
代碼托管 Github
Activity的使用方法
Activity是Android中最核心的應(yīng)用程序組件,也是大多數(shù)程序必須使用的用于顯示界面的組件。
創(chuàng)建Activity
實(shí)際上一個(gè)類只要繼承Activity類就可以當(dāng)成一個(gè)Activity來(lái)使用了,只是沒(méi)有任何控件,只有屏幕頂端默認(rèn)的標(biāo)題欄。
想要在Activity中添加控件,最直接的方法就是在onCreate中裝載xml布局文件或者使用Java代碼添加控件。
如果要重寫onCreate方法,必須要調(diào)用Activity類的onCreate()方法,也就是super.onCreate(savedInstanceState)
,否則顯示Activity時(shí)會(huì)拋出異常。
這是因?yàn)锳ctivity類中沒(méi)有不帶參數(shù)的onCreate().如果不顯示的調(diào)用super.onCreate(savedInstanceState),系統(tǒng)會(huì)試圖調(diào)用super.onCreate()方法,然而在Activity類中并沒(méi)有此方法。
配置Activity
在AndroidManifest.xml中配置Activity。
<activity android:name=".MainActivity_"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>每一個(gè)Activity都會(huì)對(duì)應(yīng)AndroidManifest.xml問(wèn)價(jià)中的一個(gè)<activity>標(biāo)簽。
必選屬性:android:name ,用于指定Activity類名。
指定android:name屬性值有三種方式:
package=”com.turing.base” >
其他一些常用的屬性
android:label
AndroidMainfest.xml中在application和activity中都可以設(shè)置android:lable。
android:label用來(lái)設(shè)置應(yīng)用名和標(biāo)題名。
應(yīng)用名:
當(dāng)主activity和application中都設(shè)置此值后,應(yīng)用名會(huì)優(yōu)先使用主activity中的值。
標(biāo)題名:
當(dāng)application和activity中都設(shè)置android:label時(shí),標(biāo)題名會(huì)優(yōu)先使用各個(gè)activity中的值。當(dāng)存在activity中沒(méi)有設(shè)置值時(shí),會(huì)使用application中的值。
android:icon
android:icon 必須指定一個(gè)圖像資源ID,用來(lái)作為應(yīng)用程序列表中的程序圖標(biāo)。 如果沒(méi)有在activity標(biāo)簽中指定,系統(tǒng)這會(huì)使用application標(biāo)簽中的android:icon屬性值來(lái)代替。
intent-filter
intent-filter標(biāo)簽的作用就是對(duì)Activity進(jìn)行分類,
intent-filter標(biāo)簽內(nèi)部的
action標(biāo)簽表示Activity可以接收的動(dòng)作
category表示Activity所屬的種類
實(shí)際上,action和category標(biāo)簽中的android:name屬性值只是一個(gè)普通的字符串。 在Android系統(tǒng)中預(yù)定了一些表中的動(dòng)作和種類。
例如:
android.intent.action.Main 和android.intent.action.category.LAUNCHER
其中android.intent.action.Main需要定義在Main Activity類的activity標(biāo)簽中。
當(dāng)Android系統(tǒng)運(yùn)行時(shí),會(huì)首先啟動(dòng)包含android.intent.action.Main的Activity。 作為MainActivity必須使用android.intent.category.LAUNCHER 作為其類別,表示該Activity顯示在最頂層。
顯示其他的Activity(Intent與Activity)
想要?jiǎng)?chuàng)建和顯示Activity,必須使用android.content.Intent作為中間的代理,并使用startActivity或startActivityForResult方法創(chuàng)建并顯示Activity。
顯示調(diào)用Intent
Intent intent = new Intent(this,MyActivity.class); startActivity(intent);第一個(gè)參數(shù)指定了Context類型的對(duì)象,第二個(gè)參數(shù)指定需要顯示的Activity類的class對(duì)象。
如果不想再Intent類的構(gòu)造方法中指定這兩個(gè)參數(shù),也可以通過(guò)Intent類的setClass方法來(lái)指定,代碼如下:
Intent intent = new Intent(); intent.setClass(this,MyActivity.class); startActivity(intent);隱式調(diào)用Intent
隱式調(diào)用仍然需要使用Intent,但是并不需要指定要調(diào)用的Activity,而只是要指定一個(gè)Action和相應(yīng)的Category即可。 action和category這兩個(gè)標(biāo)簽,不光是提供Android系統(tǒng)使用,我們也可以將他們應(yīng)用到自定義的Activity中。
如果是自定義的種類(category),category標(biāo)簽的屬性值至少要有一個(gè)android.intent.category.DEFAULT.
action標(biāo)簽的android:name屬性,可以是任意字符串,但建議使用有意義的字符串,并要在程序中通過(guò)常量來(lái)引用。
一個(gè)intent-filter標(biāo)簽可以包含多個(gè)action和category標(biāo)簽。
一個(gè)Activity中可以包含多個(gè)intent-filter標(biāo)簽。
當(dāng)intent-filter標(biāo)簽中,只有一個(gè)值為android.intent.action.category.DEFAULT的category時(shí),并不需要在Intent對(duì)象中指定這個(gè)category。如果包含了其他的category,必須要使用intent.addCategory方法添加相應(yīng)的category.
實(shí)例如下:
<!-- 該Activity未設(shè)置任何intent-filter,用顯式的方式調(diào)用這個(gè)Activity --><activity android:name=".activity.intentAct.XianSiDiaoyongAct"android:label="XianSiDiaoyongAct" /><!-- 隱式調(diào)用Activity --><activity android:name=".activity.intentAct.YinSiDiaoyngAct"android:icon="@drawable/flag_mark_red"android:label="YinSiDiaoyngAct"><intent-filter><action android:name="myAction1" /><category android:name="android.intent.category.DEFAULT" /></intent-filter><!--這個(gè)intent-filter和YinSiSelectAct的intent的相同--><intent-filter><action android:name="myAction2" /><category android:name="android.intent.category.DEFAULT" /><category android:name="mycategory"/></intent-filter></activity><activity android:name=".activity.intentAct.YinSiSelectAct"android:icon="@drawable/flag_mark_yellow"android:label="YinSiSelectAct"><!--這個(gè)intent-filter和YinSiDiaoyngAct的第二個(gè)intent-filter相同,使用這個(gè)intent-filter,屏幕會(huì)彈出一個(gè)列表,供用戶選擇--><intent-filter><action android:name="myAction2" /><category android:name="android.intent.category.DEFAULT" /><category android:name="mycategory"/></intent-filter></activity> switch (position){case 0: // 顯示調(diào)用ActivityToast.makeText(UI_Base.this,String.valueOf(position),Toast.LENGTH_SHORT).show();// 第一種方式Intent intent = new Intent(UI_Base.this, XianSiDiaoyongAct.class);startActivity(intent);// 第二種方式// Intent intent1 = new Intent();// intent1.setClass(UI_Base.this,XianSiDiaoyongAct.class);// startActivity(intent1);break;case 1:// 隱式調(diào)用ActivityIntent intent2 = new Intent("myAction1");startActivity(intent2);break;case 2: // 隱式調(diào)用兩個(gè)符合過(guò)濾條件的ActivityIntent intent3 = new Intent("myAction2");intent3.addCategory("mycategory");startActivity(intent3);break;如果在intent-filter標(biāo)簽中使用了默認(rèn)的category(android.intent.category.DEFAULT),在隱式調(diào)用中并不需要在Intent對(duì)象中使用addCategory方法指定。 如果非要指定,使用定義在Intent類中的常量 Intent.CATEGORY_DEFAULT.
intent.addCategory(Intent.CATEGORY_DEFAULT);代碼說(shuō)明:
第一個(gè)顯示調(diào)用,會(huì)根據(jù)指定的class來(lái)動(dòng)態(tài)創(chuàng)建Activity對(duì)象實(shí)例。
第二個(gè)隱式調(diào)用,系統(tǒng)會(huì)查找包含myaction1的Activity,如果找到,顯示。否則拋出異常。
第三個(gè)隱式調(diào)用符合過(guò)濾條件的Activity,由于有兩個(gè)Activity都包含了名為myaction2的動(dòng)作,并且都屬于名為mycategory的種類,系統(tǒng)會(huì)彈出選擇界面,用戶可以選擇其中一個(gè)運(yùn)行,如果勾選了”Use by default for this action”復(fù)選框,下次運(yùn)行會(huì)直接運(yùn)行上次選擇的Activity。
Activity的生命周期
整體描述
從Activity創(chuàng)建到銷毀的過(guò)程中需要在不同的階段調(diào)用7個(gè)生命周期方法。
procted void onCreate(Bundle savedInstanceState) procted void onStart() procted void onResume() procted void onPause() procted void onStop() procted void onRestart() procted void onDestory()上述7個(gè)生命周期方法分別在4個(gè)階段按照一定的順序進(jìn)行調(diào)用,4個(gè)階段分別如下
onCreate—onStart—onResume
onPause—onStop
onRestart—onStart—onResume
onStop—onDestory
如果在這4個(gè)階段執(zhí)行生命周期方法的過(guò)程中不發(fā)生狀態(tài)的改變,系統(tǒng)會(huì)按照上面的藐視依次執(zhí)行這4個(gè)階段的生命周期方法,但是如果執(zhí)行過(guò)程中改變了狀態(tài),系統(tǒng)會(huì)按更加復(fù)雜的方法調(diào)用生命周期方法。
從上圖中我們可以看出,
在執(zhí)行的過(guò)程中可以改變系統(tǒng)的執(zhí)行軌跡的生命周期方法是onPause和onStop。
如果在onPause的過(guò)程中Activity重新獲得了焦點(diǎn),然后又失去了焦點(diǎn),系統(tǒng)將不會(huì)執(zhí)行onStop方法,而是按照如下順序執(zhí)行相應(yīng)的生命周期方法:
onPause—onResume—onPause
如果在執(zhí)行onStop方法的過(guò)程中Activity重新獲得了焦點(diǎn),然后又失去了焦點(diǎn),系統(tǒng)將不會(huì)執(zhí)行onDestory方法,而是按照如下的順序執(zhí)行相應(yīng)的生命周期方法:
onStop—onRestart—onStart—onResume—onPause—onStop
從官方給出的Activity生命周期圖中不難看出,這個(gè)圖中包含兩層循環(huán),第一層是:onPause—onResume—onPause。
第二層是onStop—onRestart—onStart—onResume—onPause—onStop.
第一層稱為焦點(diǎn)生命周期
第二層稱為可視生命周期
也就是說(shuō)第一層循環(huán)在Activity焦點(diǎn)的獲得與失去中循環(huán),這一過(guò)程中Activity始終可見(jiàn)。
第二層循環(huán)在Activity可見(jiàn)與不可見(jiàn)的過(guò)程中循環(huán),在這個(gè)過(guò)程中伴隨著Activity焦點(diǎn)的獲得與失去。也就是說(shuō)Activity首先被顯示,然后會(huì)獲得焦點(diǎn),接著失去焦點(diǎn),最后由于彈出其他的Activity,使當(dāng)前的Activity變得不可見(jiàn)。
因此Activity有如下三種生命周期:
- 整體生命周期:onCreate……..onDestory
- 可視生命周期:onStart…….onStop
- 焦點(diǎn)生命周期:onResume—onPause
演示
public class LifeCircleActivity extends Activity {private static final String TAG = LifeCircleActivity.class.getSimpleName();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_life_circle);Log.e(TAG, "onCreate");}@Overrideprotected void onStart() {super.onStart();Log.e(TAG, "onStart");}@Overrideprotected void onResume() {super.onResume();Log.e(TAG, "onResume");}@Overrideprotected void onPause() {super.onPause();Log.e(TAG, "onPause");}@Overrideprotected void onStop() {super.onStop();Log.e(TAG, "onStop");}@Overrideprotected void onRestart() {super.onRestart();Log.e(TAG, "onRestart");}@Overrideprotected void onDestroy() {super.onDestroy();Log.e(TAG, "onDestroy");} }啟動(dòng)應(yīng)用程序: onCreate—onStart—onResume
按home鍵:(失去焦點(diǎn)) onPause—onStop
按home鍵后重新進(jìn)入:(重新獲得焦點(diǎn))onRestart—onStart—onResume
按返回鍵:(退出) onPause–onStop—onDestory
在不同Activity之間傳遞數(shù)據(jù)
Activity之間切換時(shí),不可避免的要進(jìn)行數(shù)據(jù)傳遞,例如在單擊列表中的某個(gè)列表項(xiàng)時(shí),小需要編輯與這個(gè)列表項(xiàng)相關(guān)的數(shù)據(jù),這個(gè)時(shí)候就需要在顯示一個(gè)Activity,然后將原始數(shù)據(jù)傳遞個(gè)這個(gè)Activity,這就是一個(gè)典型的數(shù)據(jù)傳遞的過(guò)程。
在Android中傳遞數(shù)據(jù)的方法很多,介紹4中比較常用的數(shù)據(jù)傳遞方法
- 通過(guò)Intent傳遞數(shù)據(jù)
- 通過(guò)靜態(tài)(static)變量傳遞數(shù)據(jù)
- 通過(guò)剪切板(Clipboard)傳遞數(shù)據(jù)
- 通過(guò)全局變量傳遞數(shù)據(jù)
使用Intent傳遞數(shù)據(jù)
這是最常用的一種數(shù)據(jù)傳遞方法。
通過(guò)Intent類的putExtra方法可以將簡(jiǎn)單類型的數(shù)據(jù)或者可序列化的對(duì)象保存在Intent對(duì)象中,然后在目標(biāo)Activity中使用getXXX(getInt,getString。。。。)方法獲得這些數(shù)據(jù)。
關(guān)鍵代碼如下:
Intent intent5 = new Intent(UI_Base.this,GetIntentActivity.class);//簡(jiǎn)單類型intent5.putExtra("intent_string","通過(guò)Intent傳遞的字符串");intent5.putExtra("intent_int", 20);// 可序列化的對(duì)象Data data = new Data();data.setId(99);data.setName("ZTE");intent5.putExtra("intent_object", data);startActivity(intent5); // 獲取StringString msg = getIntent().getStringExtra("intent_string");LogUtils.d("String:" + msg);// 獲取Intint value = getIntent().getExtras().getInt("intent_int");LogUtils.d("int:" + value);int vaule2 = getIntent().getIntExtra("intent_int" , 0);LogUtils.d("第二種獲取方式:" + vaule2 );// 獲取可序列化對(duì)象Data data = (Data) getIntent().getSerializableExtra("intent_object");LogUtils.d("Data: name" + data.getName() + ",id:" + data.getId());StringBuffer sb = new StringBuffer();sb.append("intent_string:" + msg);sb.append("\n");sb.append("intent_int:" + value);sb.append("\n");sb.append("intent_object: name-"+data.getName());sb.append("\n");sb.append("intent_object: id-"+data.getId());tv_getIntent.setText(sb.toString());Data implements Serializable
public class Data implements Serializable {public int id;public String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;} }Data類是可序列化的,也就是實(shí)現(xiàn)了java.io.Serializable接口。
使用靜態(tài)變量傳遞數(shù)據(jù)
雖然intent可以很方便的在Activity中間傳遞數(shù)據(jù),這也是官方推薦的數(shù)據(jù)傳遞方式。
但是Intent也有其局限性,Intent無(wú)法傳遞不能序列化的對(duì)象,也即是沒(méi)有實(shí)現(xiàn)java.io.Serializable接口的類的對(duì)象。
比如Canvas對(duì)象就無(wú)法通過(guò)Intent對(duì)象傳遞,如果傳遞自定義類的對(duì)象,也必須實(shí)現(xiàn)java.io.Serializable接口才可以。 如果沒(méi)有源代碼,而且還沒(méi)有實(shí)現(xiàn)Serializable接口,使用Intent對(duì)象就無(wú)能為力了。
步驟:
1.startActivity之前,為靜態(tài)變量賦值
2.目標(biāo)類定義靜態(tài)變量接收(也可以在其他類中定義)
使用剪切板傳遞變量(String類型和復(fù)雜對(duì)象)
在Activity之間傳遞對(duì)象還可以利用一些技巧。無(wú)論是windows 還是Linux,都會(huì)支持一種叫做剪切板的技術(shù)。
String類型
Intent intent7 = new Intent(UI_Base.this, ClipBoardTransActivity.class);ClipboardManager clipboardManager = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); // api 11的方法 @TargetApi(Build.VERSION_CODES.HONEYCOMB) clipboardManager.setText("通過(guò)Clipboard傳遞數(shù)據(jù)"); startActivity(intent7); public class ClipBoardTransActivity extends AppCompatActivity {@TargetApi(Build.VERSION_CODES.HONEYCOMB)@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_clip_board_trans);ClipboardManager clipboardManager = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);String msg = clipboardManager.getText().toString();TextView textView = (TextView) findViewById(R.id.id_tv_clipboard);textView.setText(msg);} }在上述的代碼中使用了getSystemService方法獲得了一個(gè)系統(tǒng)服務(wù)對(duì)象,也就是ClipboardManager對(duì)象,該對(duì)象用于管理系統(tǒng)剪切板,并使用ClipboardManager.setText方法向剪切板中保存了一個(gè)字符串,通過(guò)getText可以獲取。
但是ClipboardManager對(duì)象只支持向剪切板讀寫字符串,并不支持其他的類型,更別提復(fù)雜的對(duì)象了。
當(dāng)然了,如果是其他類型的數(shù)據(jù),比如int ,可以將起轉(zhuǎn)換成字符串。
復(fù)雜對(duì)象
如果是對(duì)象類型呢,比如之前的Data對(duì)象能否通過(guò)剪切板傳遞呢?答案是肯定的,只是需要通過(guò)Base64進(jìn)行編碼解碼轉(zhuǎn)換。
由于Data是可序列化的對(duì)象,因此完全可以將Data抓換成byte[]類型的數(shù)據(jù),然后將byte[]類型的數(shù)據(jù)再進(jìn)行Base4編碼(通過(guò)Email發(fā)送附件就是將附件轉(zhuǎn)換成為Base64格式的字符串發(fā)送的)轉(zhuǎn)換成字符串。
代碼演示,通過(guò)剪切板傳遞Data對(duì)象。
解碼
public class ClipboardTransObjectDataAct extends AppCompatActivity {@TargetApi(Build.VERSION_CODES.HONEYCOMB)@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_clipboard_trans_object_data);ClipboardManager cbm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);// 從剪切板中獲取Base64編碼格式的字符串String base64Str = cbm.getText().toString();// 將Base64格式的字符串還原為byte[]格式的數(shù)據(jù)byte[] buffer = Base64.decode(base64Str,Base64.DEFAULT);ByteArrayInputStream bais = new ByteArrayInputStream(buffer);try {ObjectInputStream ois = new ObjectInputStream(bais);// 將byte[]數(shù)據(jù)還原為Data對(duì)象Data data = (Data)ois.readObject();// 輸出TextView tv = (TextView)findViewById(R.id.id_tv_clipboard_trans_object);tv.setText(base64Str + "\n\n data.id:" + data.getId() + "\ndata.name:"+data.getName());} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}} }說(shuō)明:
Base64類是從Android2.2開(kāi)始支持的,2.1及其以下版本無(wú)法通過(guò)Android SDK API 進(jìn)行Base64編碼和解碼,因此需要借助第三方的類庫(kù)如common httpclient才可以。
使用全局對(duì)象傳遞變量
雖然使用靜態(tài)變量可以傳遞任意類型的數(shù)據(jù),但是官方并不建議這樣做。如果在類中大量使用靜態(tài)變量(尤其是很占用資源的變量,如Bitmap對(duì)象)可能會(huì)造成內(nèi)存溢出異常,而且可能因?yàn)殪o態(tài)變量在很多類中出現(xiàn)而造成代碼難以維護(hù)和混亂。
我們可以使用一種更優(yōu)雅的數(shù)據(jù)傳遞方式–全局對(duì)象。這種方式可以完全取代靜態(tài)變量。
步驟:
Manifest.xml指定全局類
<applicationandroid:name=".AppContext"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme"> AppContext context = (AppContext)getApplication();context.appName = "ANDROID BASE";context.data.setId(0000);context.data.setName("通過(guò)全局變量來(lái)傳遞數(shù)據(jù)");Intent intent9 = new Intent(UI_Base.this, ApplicationTransActivity.class);startActivity(intent9); public class ApplicationTransActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_application_trans);// 獲取全局變量AppContext context = (AppContext)getApplication();String name = context.appName;Data data = context.data;StringBuffer sb = new StringBuffer();sb.append("AppContext appname:" + name );sb.append("\n");sb.append("AppContext data.id:" + data.getId());sb.append("\n");sb.append("AppContext data.name:" + data.getName());TextView textView = (TextView) findViewById(R.id.id_tv_app_trans);textView.setText(sb.toString());} }全局對(duì)象所對(duì)應(yīng)的類必須是android.app.Application的子類。
全局類中不需要定義靜態(tài)變量,只需要定義成員變量即可,
而且全局類中必須要有一個(gè)無(wú)參的構(gòu)造方法,或者不編寫任何代碼的構(gòu)造方法(系統(tǒng)會(huì)自動(dòng)的建立一個(gè)無(wú)參數(shù)的構(gòu)造方法)。
這個(gè)類和Activity一樣,由系統(tǒng)自動(dòng)創(chuàng)建,因此,必須要有一個(gè)無(wú)參的構(gòu)造方法。
只編寫一個(gè)全局類是不會(huì)自動(dòng)創(chuàng)建全局對(duì)象的,因?yàn)锳ndroid系統(tǒng)并不知道哪個(gè)是全局類,因此需要在AndroidManifest.xml中的application標(biāo)簽的android:name屬性來(lái)執(zhí)行這個(gè)類。
指定全局類后,在程序運(yùn)行后,全局對(duì)象會(huì)被自動(dòng)創(chuàng)建,而且會(huì)一直在內(nèi)存中駐留,直到應(yīng)用程序徹底退出內(nèi)存。
四種方式比較
雖然上述始終方法在某些情況下可以相互取代,但是根據(jù)具體情況使用不同的數(shù)據(jù)傳遞方法會(huì)使程序更加便于維護(hù)。
對(duì)于向其他Activity中傳遞簡(jiǎn)單類型(int 、String、short、bool等)或者可序列化的對(duì)象時(shí),建議使用Intent。
如果傳遞不可序列化的對(duì)象,可以采用靜態(tài)變量或者全局對(duì)象的方式,不過(guò)按照官方的建議,最好是采用全局對(duì)象的方式。
另外如果想要使某些數(shù)據(jù)長(zhǎng)時(shí)間駐留內(nèi)存,以便程序隨時(shí)的取用,最好采用全局對(duì)象的方式。當(dāng)然如果數(shù)據(jù)不復(fù)雜,也可以采用靜態(tài)變量的方式
至于剪切板,如果不是特殊情況,并不建議使用,因?yàn)檫@可能會(huì)影響到其他的程序(其他程序也可能使用剪切板)
返回?cái)?shù)據(jù)到前一個(gè)Activity
在應(yīng)用程序中,不僅要向Activity傳遞數(shù)據(jù),同時(shí)也要從Activity中返回?cái)?shù)據(jù),一般建議采用Intent這種方式來(lái)返回?cái)?shù)據(jù),需要使用startActivityForResult方法來(lái)顯示Activity。
代碼如下
其中startActivityForResult方法有2個(gè)參數(shù),第二個(gè)參數(shù)是一個(gè)int類型的請(qǐng)求碼,可以是任意的整數(shù),只是為了區(qū)分請(qǐng)求的來(lái)源,以便處理返回結(jié)果。
大致步驟如下:
啟動(dòng)一個(gè)ForResult的意圖:
Intent intent = new Intent(MainAcitvity.this,RequestActivity.class);
//發(fā)送意圖標(biāo)示為REQUSET=1
startActivityForResult(intent, REQUSET);
B Activity處理數(shù)據(jù):
Intent intent=new Intent();
intent.putExtra(KEY_USER_ID, et01.getText().toString());
setResult(RESULT_OK, intent);
finish();
代碼演示如下:
A類
Intent intent10 = new Intent(UI_Base.this, StarActivityForResultAct.class);startActivityForResult(intent10, 1); // 請(qǐng)求碼1 一定要>=0 @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode){ // 請(qǐng)求碼1case 1:switch (resultCode){ // 響應(yīng)碼case 2:Toast.makeText(UI_Base.this,data.getStringExtra("value"),Toast.LENGTH_SHORT).show();break;default:break;}break;default:break;}}B類
public class StarActivityForResultAct extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_star_activity_for_result);}/*** android:onClick="doSomethingThenReturn"* 對(duì)應(yīng)xml中的屬性,記得方法里面的參數(shù),否則報(bào)錯(cuò)* @param view*/public void doSomethingThenReturn(View view){Intent intent = new Intent();intent.putExtra("value","返回給前個(gè)Act的值");// 通過(guò)intent對(duì)象返回結(jié)果,setResult的第一個(gè)參數(shù)是響應(yīng)碼setResult(2, intent);// 關(guān)閉當(dāng)前Activityfinish();} } <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><Button android:id="@+id/id_btn_doSomethingThenReturn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="doSomethingThenReturn"android:text="doSomethingThenReturn"/></RelativeLayout>在Button標(biāo)簽中有一個(gè)屬性android:click,可以直接指定按鈕單擊事件的方法名。這樣在Activity中就不用創(chuàng)建按鈕對(duì)象而處理按鈕單擊事件了。 如果在程序中只處理單擊事件,而不直接引用相應(yīng)的對(duì)象,可以采用這種方法。
視圖(View)
在Android系統(tǒng)中任何可視化控件都必須從android.view.View類繼承。
兩種方式創(chuàng)建View對(duì)象:
視圖簡(jiǎn)介
Android中視圖類可以分為三種
- 布局類(Layout)
- 視圖容器(View Container)
- 視圖類 (例如TextView…)
使用xml布局文件定義視圖
注意事項(xiàng):
- XML布局文件的擴(kuò)展名必須是xml
- XML布局文件名必須符合Java變量的命名規(guī)則(以為在R類中會(huì)自動(dòng)生成一個(gè)變量),例如不能以數(shù)字開(kāi)頭
- XML布局文件的根節(jié)點(diǎn)可以是任意的控件標(biāo)簽,比如<LinearLayout> <TextView>
- XML布局文件的根節(jié)點(diǎn)必須包含android命名控件,且必須是http://schemas.android.com/apk/res/android
- 為XML布局文件中的標(biāo)簽指定ID時(shí)需要使用這樣的格式:@+id/value ,其中@+ 語(yǔ)法標(biāo)識(shí)如果ID在R.id類中不出在,這產(chǎn)生一個(gè)與ID同名的變量,如果存在,則直接使用。 value表示ID的值。
- 視圖ID的值也要符合java變量的命名規(guī)則
在獲得XML布局文件中的視圖對(duì)象需要注意如下幾點(diǎn)
- finddViewById需要在setContentView之后使用。
- findViewById只能獲得已經(jīng)裝載的XML布局文件中的視圖對(duì)象。
- 在不同的XML布局文件中可以存在相同ID的視圖,但是在同一個(gè)XML文件中,雖然也可以有相同ID的視圖,但是通過(guò)ID獲取視圖時(shí),只能夠獲取按照定義的順序的第一個(gè)視圖對(duì)象,其他相同ID值的視圖對(duì)象將無(wú)法回去,因此在同一個(gè)XML布局文件中盡量使視圖ID唯一。
在代碼中控制視圖
舉個(gè)例子:
TextView tv = (TextView)findViewById(R.id.textView1); tv.setText("UUUUU");還可以使用字符串資源對(duì)TextView進(jìn)行文本修改
tv.setText(R.string.hello);注意:當(dāng)seText方法的參數(shù)是int型時(shí),會(huì)被認(rèn)為這個(gè)參數(shù)值是一個(gè)字符串資源ID,因此,如果要將TextView的文本設(shè)置為一個(gè)整數(shù),需要將這個(gè)整數(shù)抓換位String類型。例如 tv.setText(String.valueOf(200));將TextView的文本設(shè)置為200
在更高級(jí)的Android應(yīng)用中,往往需要?jiǎng)討B(tài)的添加視圖,要實(shí)現(xiàn)這個(gè)功能,最重要的是要獲得被添加的視圖所在的容器對(duì)象,這個(gè)容器對(duì)象所對(duì)應(yīng)的類需要繼承ViewGroup類。
將其他的視圖添加到當(dāng)前的容器視圖中的步驟如下:
場(chǎng)景:
假設(shè)有兩個(gè)xml布局文件:test1.xml test2.xml
這兩個(gè)xml的根節(jié)點(diǎn)都是<LinearLayout>, 目的獲取test2.xml中的LinearLayout對(duì)象,并將該對(duì)象作為test1.xml文件中的<LinearLayout>標(biāo)簽的子節(jié)點(diǎn)添加到test1.xml的LinearLayout對(duì)象中。
第一種方式:
// 獲得test1.xml中的LinearLayout對(duì)象 LinearLayout l1 = (LinearLayout)getLayoutInflater().inflate(R.layout.test,null); // 將test1.xml中的LinearLayout對(duì)象設(shè)置為當(dāng)前容器視圖 setContentView(l1); // 獲取test2.xml中的LinearLayout對(duì)象,并將該對(duì)象添加到test1.xml中的LinearLayout中 LinearLayout l2 = (LinearLayout)getInflater().inflater(R.layout.test2,l1);參數(shù)解釋: inflate()方法第一個(gè)參數(shù)標(biāo)識(shí)XML布局資源文件的ID,
第二個(gè)參數(shù)標(biāo)識(shí)獲得容器對(duì)象后,要將該對(duì)象添加到哪個(gè)視圖對(duì)象中。
如果不想添加到任何其他的容器中,設(shè)置為null即可。
第二種方式:
// 獲得test1.xml中的LinearLayout對(duì)象 LinearLayout l1 = (LinearLayout)getLayoutInflater().inflate(R.layout.test,null); // 將test1.xml中的LinearLayout對(duì)象設(shè)置為當(dāng)前容器視圖 setContentView(l1); // 獲取test2.xml中的LinearLayout對(duì)象,并將該對(duì)象添加到test1.xml中的LinearLayout中 LinearLayout l2 = (LinearLayout)getInflater().inflater(R.layout.test2,null);l1.addView(l2);inflate方法第二個(gè)參數(shù)設(shè)置為null, 通過(guò)addView方法添加
第三種方式
完全使用Java代碼創(chuàng)建一個(gè)視圖對(duì)象,并將該對(duì)象添加到容器視圖中
TextView tv = new TextView(this); l1.addView(tv)注意事項(xiàng):
- 如果使用setContentView方法將試圖容器設(shè)置為當(dāng)前視圖后,還想要向試圖容器中添加新的視圖或者進(jìn)行其他操作,setContentView方法的參數(shù)值應(yīng)直接使用容器視圖對(duì)象,因?yàn)檫@樣可以向容器視圖對(duì)象中添加新的視圖。
- 一個(gè)視圖只能有一個(gè)父視圖。也就是說(shuō)一個(gè)視圖只能被包含在一個(gè)容器視圖中。因此,在向容器視圖中添加其他視圖時(shí),不能將XML布局文件中非根節(jié)點(diǎn)的視圖對(duì)象添加到其他的容器視圖中。
布局(Layout)
框架布局FrameLayout
最簡(jiǎn)單的布局方式,FrameLayout 以層疊放方式顯示,第一個(gè)添加到框架布局中的視圖顯示在最底層,最后一個(gè)放在最頂層。
上一層視圖會(huì)覆蓋下一層視圖,類似于堆棧,因此也被稱為堆棧布局。
線性布局LinearLayout
最常用的布局方式。
線性布局可以分為水平線性布局和垂直先行布局。
android:orientation ,兩個(gè)字 horizontal 、vertical。 默認(rèn)horizontal。
一個(gè)非常重要的屬性 gravity,用于控制布局中視圖的位置。
設(shè)置多個(gè)屬性,需要使用“|”分隔,在屬性值和“|”之間不能有其他符號(hào)(例如空格和制表符等)。
| top | 將視圖放到屏幕頂端 |
| bottom | 將視圖放到屏幕底端 |
| left | 將視圖放到屏幕左側(cè) |
| right | 將視圖放到屏幕右側(cè) |
| center_vertical | 將視圖按垂直方向居中顯示 |
| center_horizontal | 將視圖按水平方向居中顯示 |
| center | 將視圖按垂直和水平方向居中顯示 |
LinearLayout標(biāo)簽中的子標(biāo)簽還可以使用layout_gravity和layout_weight屬性來(lái)設(shè)置每一個(gè)視圖的位置
layout_gravity 屬性的取值和gravity的取值相同,表示當(dāng)前視圖在布局中的位置。
layout_weight屬性是一個(gè)非負(fù)整數(shù),如果該屬性值大于0,線性布局會(huì)根據(jù)水平或者垂直方向以及不同視圖的layout_weight屬性值占所有視圖的layout_weight屬性值之和的比例為這些視圖分配自己說(shuō)占用的區(qū)域,視圖將按相應(yīng)比例拉伸。
相對(duì)布局RelativeLayout
設(shè)置某一個(gè)視圖相對(duì)于其他視圖的位置。
表格布局TableLayout
一個(gè)表格布局由一個(gè)<TableLayout>標(biāo)簽和若干個(gè)<TableRow>標(biāo)簽組成
絕對(duì)布局AbsoluteLayout
android:layout_x 和android:layout_y設(shè)置橫縱坐標(biāo)。
重用XML布局
布局重用
<inclued>??include標(biāo)簽可以實(shí)現(xiàn)在一個(gè)layout中引用另一個(gè)layout的布局,這通常適合于界面布局復(fù)雜、不同界面有共用布局的APP中,比如一個(gè)APP的頂部布局、側(cè)邊欄布局、底部Tab欄布局、ListView和GridView每一項(xiàng)的布局等,將這些同一個(gè)APP中有多個(gè)界面用到的布局抽取出來(lái)再通過(guò)include標(biāo)簽引用,既可以降低layout的復(fù)雜度,又可以做到布局重用(布局有改動(dòng)時(shí)只需要修改一個(gè)地方就可以了)。
inclued標(biāo)簽,首字母要小寫,只有l(wèi)ayout屬性是必選的。
?include標(biāo)簽的使用很簡(jiǎn)單,只需要在布局文件中需要引用其它布局的地方,使用layout=”@layout/child_layout”就可以了:
<include layout="@layout/titlebar"/>如果要覆蓋布局的尺寸,必須同時(shí)覆蓋android:layout_weight和android:layout_height . 不能只覆蓋一個(gè),否則無(wú)效
建議將給include標(biāo)簽調(diào)用布局設(shè)置寬高、位置、ID等工作放在調(diào)用布局的根標(biāo)簽中,這樣可以避免給include標(biāo)簽設(shè)置屬性不當(dāng)造成的各種問(wèn)題(之前遇到過(guò)給include標(biāo)簽設(shè)置android:id屬性后,程序?qū)嵗硬季种薪M件失敗的現(xiàn)象):
?應(yīng)該這樣:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/bottomBarLayoutId"android:layout_width="match_parent"android:layout_height="61dp"android:orientation="horizontal"android:layout_alignParentBottom="true">。。。 </LinearLayout><include layout="@layout/include_voice_ctrl_bar_layout" />而不是這樣:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal">。。。 </LinearLayout><include android:id="@+id/bottomBarLayoutId"android:layout_width="match_parent"android:layout_height="61dp"android:layout_alignParentBottom="true"layout="@layout/include_voice_ctrl_bar_layout"/>優(yōu)化XML布局
減少視圖層級(jí)
<merge />
<merge />標(biāo)簽在UI的結(jié)構(gòu)優(yōu)化中起著非常重要的作用,它可以刪減多余的層級(jí),優(yōu)化UI。<merge />多用于替換FrameLayout或者當(dāng)一個(gè)布局包含另一個(gè)時(shí),<merge />標(biāo)簽消除視圖層次結(jié)構(gòu)中多余的視圖組。例如你的主布局文件是垂直布局,引入了一個(gè)垂直布局的include,這是如果include布局使用的LinearLayout就沒(méi)意義了,使用的話反而減慢你的UI表現(xiàn)。這時(shí)可以使用<merge />標(biāo)簽優(yōu)化
<merge xmlns:android="http://schemas.android.com/apk/res/android"><Button android:layout_width="fill_parent" android:layout_height="wrap_content"android:text="@string/add"/><Button android:layout_width="fill_parent" android:layout_height="wrap_content"android:text="@string/delete"/></merge>現(xiàn)在,當(dāng)你添加該布局文件時(shí)(使用<include />標(biāo)簽),系統(tǒng)忽略<merge />節(jié)點(diǎn)并且直接添加兩個(gè)Button。更多<merge />介紹可以參考《Android Layout Tricks #3: Optimize by merging》
需要時(shí)使用<ViewStub />
<ViewStub />標(biāo)簽最大的優(yōu)點(diǎn)是當(dāng)你需要時(shí)才會(huì)加載,使用他并不會(huì)影響UI初始化時(shí)的性能。各種不常用的布局想進(jìn)度條、顯示錯(cuò)誤消息等可以使用<ViewStub />標(biāo)簽,以減少內(nèi)存使用量,加快渲染速度。<ViewStub />是一個(gè)不可見(jiàn)的,大小為0的View。<ViewStub />標(biāo)簽使用如下:
<ViewStub android:id="@+id/stub_import"android:inflatedId="@+id/panel_import"android:layout="@layout/progress_overlay"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_gravity="bottom" />當(dāng)你想加載布局時(shí),可以使用下面其中一種方法:
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE); // or View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();當(dāng)調(diào)用inflate()函數(shù)的時(shí)候,ViewStub被引用的資源替代,并且返回引用的view。 這樣程序可以直接得到引用的view而不用再次調(diào)用函數(shù)findViewById()來(lái)查找了。
注:ViewStub目前有個(gè)缺陷就是還不支持 <merge /> 標(biāo)簽。
更多<ViewStub />標(biāo)簽介紹可以參考《Android Layout Tricks #3: Optimize with stubs》
查看APK文件中的布局
AXMLPrinter2工具
總結(jié)
- 上一篇: Android程序设计基础
- 下一篇: TextView