本節(jié)開始講解Android的四大組件之一的Activity
官方介紹:
Activity 是一個(gè)應(yīng)用組件,用戶可與其提供的屏幕進(jìn)行交互御毅,以執(zhí)行撥打電話、拍攝照片、發(fā)送電子郵件或查看地圖等操作语泽。 每個(gè) Activity 都會(huì)獲得一個(gè)用于繪制其用戶界面的窗口。窗口通常會(huì)充滿屏幕视卢,但也可小于屏幕并浮動(dòng)在其他窗口之上踱卵。
從上面這段話,可以得到以下信息:
- Activity用于顯示用戶界面据过,用戶通過Activity交互完成相關(guān)操作
- 一個(gè)App允許有多個(gè)Activity
1. Activity的概念與Activity的生命周期:
在一個(gè)activity的生命周期中惋砂,系統(tǒng)會(huì)像金字塔模型一樣去調(diào)用一系列的生命周期回調(diào)函數(shù)。Activity生命周期的每一個(gè)階段就像金字塔中的臺(tái)階绳锅。當(dāng)系統(tǒng)創(chuàng)建了一個(gè)新的activity實(shí)例班利,每一個(gè)回調(diào)函數(shù)會(huì)向上一階移動(dòng)activity狀態(tài)。處在金字塔頂端意味著當(dāng)前activity處在前臺(tái)并處于用戶可與其進(jìn)行交互的狀態(tài)榨呆。
當(dāng)用戶退出這個(gè)activity時(shí)罗标,為了回收該activity,系統(tǒng)會(huì)調(diào)用其它方法來向下一階移動(dòng)activity狀態(tài)积蜻。在某些情況下闯割,activity會(huì)隱藏在金字塔下等待(例如當(dāng)用戶切換到其他app),此時(shí)activity可以重新回到頂端(如果用戶回到這個(gè)activity)并恢復(fù)用戶離開時(shí)的狀態(tài)。
理解生命周期的回調(diào)
Activity會(huì)在上圖所示不同狀態(tài)之間過渡的幾種情況竿拆。 但是宙拉,這些狀態(tài)中只有三種可以是靜態(tài)。 也就是說丙笋,Activity只能在三種狀態(tài)之一下存在很長時(shí)間谢澈。
Resumed: 繼續(xù)
在這種狀態(tài)下,Activity處于前臺(tái)御板,且用戶可以與其交互锥忿。(有時(shí)也稱為“運(yùn)行”狀態(tài)。)
Paused: 暫停
在這種狀態(tài)下怠肋,Activity被在前臺(tái)中處于半透明狀態(tài)或者未覆蓋整個(gè)屏幕的另一個(gè)Activity—部分阻擋敬鬓。 暫停的Activity不會(huì)接收用戶輸入并且無法執(zhí)行任何代碼。
Stopped: 停止
在這種狀態(tài)下,Activity被完全隱藏并且對用戶不可見钉答;它被視為處于后臺(tái)础芍。 停止時(shí),Activity實(shí)例及其諸如成員變量等所有狀態(tài)信息將保留数尿,但它無法執(zhí)行任何代碼仑性。
其他狀態(tài)(“創(chuàng)建”和“開始”)是瞬態(tài),系統(tǒng)會(huì)通過調(diào)用下一個(gè)生命周期回調(diào)方法從這些狀態(tài)快速移到下一個(gè)狀態(tài)右蹦。 也就是說诊杆,在系統(tǒng)調(diào)用 onCreate()之后,它會(huì)快速調(diào)用 onStart()
嫩实,緊接著快速調(diào)用 onResume()刽辙。
注意事項(xiàng):
- onPause()和onStop()被調(diào)用的前提是:
打開了一個(gè)新的Activity!而前者是舊Activity還可見的狀態(tài)甲献;后者是舊Activity已經(jīng)不可見宰缤!
- 另外,親測:AlertDialog和PopWindow是不會(huì)觸發(fā)上述兩個(gè)回調(diào)方法的~
2. Activity/ActionBarActivity/AppCompatActivity的區(qū)別:
在開始講解創(chuàng)建Activity之前要說下這三個(gè)的一個(gè)區(qū)別:
Activity就不用說啦晃洒,后面這兩個(gè)都是為了低版本兼容而提出的提出來的慨灭,他們都在v7包下,
ActionBarActivity已被廢棄球及,從名字就知道氧骤,ActionBar~,而在5.0后吃引,被Google棄用了筹陵,現(xiàn)在用
ToolBar…而我們現(xiàn)在在Android Studio創(chuàng)建一個(gè)Activity默認(rèn)繼承的會(huì)是:AppCompatActivity!
當(dāng)然你也可以只寫Activity,不過AppCompatActivity給我們提供了一些新的東西而已镊尺! 兩個(gè)選一個(gè) ~
3. Activity的創(chuàng)建流程
ps:
上面也說過朦佩,可以繼承Activity和AppCompatActivity,只不過后者提供了一些新的東西而已庐氮!
另外语稠,切記,Android中的四大組件弄砍,只要你定義了仙畦,無論你用沒用,都要在AndroidManifest.xml對
這個(gè)組件進(jìn)行聲明音婶,不然運(yùn)行時(shí)程序會(huì)直接退出慨畸,報(bào)ClassNotFindException…
4. onCreate()一個(gè)參數(shù)和兩個(gè)參數(shù)的區(qū)別:
相信用as的朋友在重寫Act的onCreate()方法時(shí)會(huì)發(fā)現(xiàn)
- 有兩個(gè)參數(shù):
@Override
public void onCreate(Bundle savedInstanceState,PersistableBundle persistentStable) {
super.onCreate(savedInstanceState,persistentStable);
}
- 有一個(gè)參數(shù)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
可是正常的才只有一個(gè)參數(shù)啊, 恩呢,這就是5.0給我們提供的新的方法桃熄,要用它先口,先要在配置文件中為我們的Activity設(shè)置一個(gè)屬性:
android:persistableMode="persistAcrossReboots"
然后我們的Activity就擁有了持久化的能力了型奥,一般我們會(huì)搭配另外兩個(gè)方法來使用:
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState)
相信有些朋友對這兩個(gè)方法名不陌生吧瞳收,前一個(gè)方法會(huì)在下述情形中被調(diào)用:
- 點(diǎn)擊home鍵回到主頁或長按后選擇運(yùn)行其他程序
- 按下電源鍵關(guān)閉屏幕
- 啟動(dòng)新的Activity
- 橫豎屏切換時(shí)碉京,肯定會(huì)執(zhí)行,因?yàn)闄M豎屏切換的時(shí)候會(huì)先銷毀Act螟深,然后再重新創(chuàng)建
- 重要原則:
當(dāng)系統(tǒng)“未經(jīng)你許可”時(shí)銷毀了你的activity谐宙,則onSaveInstanceState會(huì)被系統(tǒng)調(diào)用,
這是系統(tǒng)的責(zé)任界弧,因?yàn)樗仨氁峁┮粋€(gè)機(jī)會(huì)讓你保存你的數(shù)據(jù)(你可以保存也可以不保存)凡蜻。
而后一個(gè)方法,和onCreate同樣可以從取出前者保存的數(shù)據(jù):
一般是在onStart()和onResume()之間執(zhí)行垢箕!
之所以有兩個(gè)可以獲取到保存數(shù)據(jù)的方法划栓,是為了避免Act跳轉(zhuǎn)而沒有關(guān)閉,
然后不走onCreate()方法条获,而你又想取出保存數(shù)據(jù)~
說回來:
說回這個(gè)Activity擁有了持久化的能力忠荞,增加的這個(gè)PersistableBundle參數(shù)令這些方法
擁有了系統(tǒng)關(guān)機(jī)后重啟的數(shù)據(jù)恢復(fù)能力!帅掘!而且不影響我們其他的序列化操作委煤,臥槽,
具體怎么實(shí)現(xiàn)的修档,暫時(shí)還不了解碧绞,可能是另外弄了個(gè)文件保存吧~!后面知道原理的話會(huì)告知下大家吱窝!
另外讥邻,API版本需要>=21,就是要5.0以上的版本才有效~
5. 啟動(dòng)一個(gè)Activity的幾種方式
在Android中我們可以通過下面兩種方式來啟動(dòng)一個(gè)新的Activity,注意這里是怎么啟動(dòng)院峡,而非 啟動(dòng)模式P耸埂!分為顯示啟動(dòng)和隱式啟動(dòng)撕予!
- 顯式啟動(dòng):通過包名來啟動(dòng)鲫惶,寫法如下:
- ① 最常見的:
startActivity(new Intent(當(dāng)前Act.this,要啟動(dòng)的Act.class));
- ② 通過Intent的ComponentName:
ComponentName cn = new ComponentName(“當(dāng)前Act的全限定類名”,”啟動(dòng)Act的全限定類名”) ;
Intent intent = new Intent() ;
intent.setComponent(cn) ;
startActivity(intent) ; - ③ 初始化Intent時(shí)指定包名:
Intent intent = new Intent(“android.intent.action.MAIN”);
intent.setClassName(“當(dāng)前Act的全限定類名”,”啟動(dòng)Act的全限定類名”);
startActivity(intent);
- 隱式啟動(dòng):通過Intent-filter的Action,Category或data來實(shí)現(xiàn)
這個(gè)是通過Intent的 * intent-filter*來實(shí)現(xiàn)的,這個(gè)Intent那章會(huì)詳細(xì)講解实抡!
這里知道個(gè)大概就可以了欠母!
- 另外還有一個(gè)直接通過包名啟動(dòng)apk的:
Intent intent = getPackageManager().getLaunchIntentForPackage
(“apk第一個(gè)啟動(dòng)的Activity的全限定類名”) ;
if(intent != null) startActivity(intent) ;
6. 橫豎屏切換與狀態(tài)保存的問題
前面也說到了App橫豎屏切換的時(shí)候會(huì)銷毀當(dāng)前的Activity然后重新創(chuàng)建一個(gè),你可以自行在生命周期 的每個(gè)方法里都添加打印Log的語句吆寨,來進(jìn)行判斷赏淌,又或者設(shè)一個(gè)按鈕一個(gè)TextView點(diǎn)擊按鈕后,修改TextView
文本啄清,然后橫豎屏切換六水,會(huì)神奇的發(fā)現(xiàn)TextView文本變回之前的內(nèi)容了!
橫豎屏切換時(shí)Act走下述生命周期:
onPause-> onStop-> onDestory-> onCreate->onStart->onResume
關(guān)于橫豎屏切換可能遇到下述問題:
1).先說下如何禁止屏幕橫豎屏自動(dòng)切換
很簡單,在AndroidManifest.xml中為Act添加一個(gè)屬性:
android:screenOrientation
有下述可選值:
- unspecified:默認(rèn)值 由系統(tǒng)來判斷顯示方向.判定的策略是和設(shè)備相關(guān)的掷贾,所以不同的設(shè)備會(huì)有不同的顯示方向.
- landscape:橫屏顯示(寬比高要長)
- portrait:豎屏顯示(高比寬要長)
- user:用戶當(dāng)前首選的方向
- behind:和該Activity下面的那個(gè)Activity的方向一致(在Activity堆棧中的)
- sensor:有物理的感應(yīng)器來決定睛榄。如果用戶旋轉(zhuǎn)設(shè)備這屏幕會(huì)橫豎屏切換。
- nosensor:忽略物理感應(yīng)器想帅,這樣就不會(huì)隨著用戶旋轉(zhuǎn)設(shè)備而更改了(”unspecified”設(shè)置除外)场靴。
2). 橫豎屏?xí)r想加載不同的布局:
2.1)準(zhǔn)備兩套不同的布局,Android會(huì)自己根據(jù)橫豎屏加載不同布局:
創(chuàng)建兩個(gè)布局文件夾:
layout-land橫屏,layout-port豎屏
然后把這兩套布局文件丟這兩文件夾里港准,文件名一樣旨剥,Android就會(huì)自行判斷,然后加載相應(yīng)布局了浅缸!
2.2 )自己在代碼中進(jìn)行判斷轨帜,自己想加載什么就加載什么:
我們一般是在onCreate()方法中加載布局文件的,我們可以在這里對橫豎屏的狀態(tài)做下判斷衩椒,關(guān)鍵代碼如下
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
setContentView(R.layout.橫屏);
}
else if (this.getResources().getConfiguration().orientation ==Configuration.ORIENTATION_PORTRAIT) {
setContentView(R.layout.豎屏);
}
- . 如何讓模擬器橫豎屏切換
如果你的模擬器是GM的話蚌父。直接按模擬器上的切換按鈕即可,原生模擬器可按ctrl + f11/f12切換烟具!
- . 狀態(tài)保存問題:
這個(gè)上面也說過了梢什,通過一個(gè)Bundle savedInstanceState參數(shù)即可完成!
三個(gè)核心方法:
onCreate(Bundle savedInstanceState);
onSaveInstanceState(Bundle outState);
onRestoreInstanceState(Bundle savedInstanceState);
你只重寫onSaveInstanceState()方法朝聋,往這個(gè)bundle中寫入數(shù)據(jù)嗡午,比如:
outState.putInt(“num”,1);
這樣,然后你在onCreate或者onRestoreInstanceState中就可以拿出里面存儲(chǔ)的數(shù)據(jù)冀痕,不過拿之前要判斷下是否為null哦荔睹!
savedInstanceState.getInt(“num”);
然后想干嘛就干嘛~
7. 系統(tǒng)給我們提供的常見的Activity
好的,最后給大家附上一些系統(tǒng)給我們提供的一些常見的Activtiy吧言蛇!
//1.撥打電話
// 給移動(dòng)客服10086撥打電話
Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);
//2.發(fā)送短信
// 給10086發(fā)送內(nèi)容為“Hello”的短信
Uri uri = Uri.parse("smsto:10086");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", "Hello");
startActivity(intent);
//3.發(fā)送彩信(相當(dāng)于發(fā)送帶附件的短信)
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("sms_body", "Hello");
Uri uri = Uri.parse("content://media/external/images/media/23");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("image/png");
startActivity(intent);
//4.打開瀏覽器:
// 打開Google主頁
Uri uri = Uri.parse("http://www.baidu.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//5.發(fā)送電子郵件:(閹割了Google服務(wù)的沒戲!!!!)
// 給someone@domain.com發(fā)郵件
Uri uri = Uri.parse("mailto:someone@domain.com");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
// 給someone@domain.com發(fā)郵件發(fā)送內(nèi)容為“Hello”的郵件
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, "someone@domain.com");
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("text/plain");
startActivity(intent);
// 給多人發(fā)郵件
Intent intent=new Intent(Intent.ACTION_SEND);
String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人
String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送
String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_BCC, bccs);
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("message/rfc822");
startActivity(intent);
//6.顯示地圖:
// 打開Google地圖中國北京位置(北緯39.9僻他,東經(jīng)116.3)
Uri uri = Uri.parse("geo:39.9,116.3");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//7.路徑規(guī)劃
// 路徑規(guī)劃:從北京某地(北緯39.9,東經(jīng)116.3)到上海某地(北緯31.2腊尚,東經(jīng)121.4)
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=39.9 116.3&daddr=31.2 121.4");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//8.多媒體播放:
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/foo.mp3");
intent.setDataAndType(uri, "audio/mp3");
startActivity(intent);
//獲取SD卡下所有音頻文件,然后播放第一首=-=
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//9.打開攝像頭拍照:
// 打開拍照程序
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 0);
// 取出照片數(shù)據(jù)
Bundle extras = intent.getExtras();
Bitmap bitmap = (Bitmap) extras.get("data");
//另一種:
//調(diào)用系統(tǒng)相機(jī)應(yīng)用程序吨拗,并存儲(chǔ)拍下來的照片
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
time = Calendar.getInstance().getTimeInMillis();
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment
.getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg")));
startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);
//10.獲取并剪切圖片
// 獲取并剪切圖片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.putExtra("crop", "true"); // 開啟剪切
intent.putExtra("aspectX", 1); // 剪切的寬高比為1:2
intent.putExtra("aspectY", 2);
intent.putExtra("outputX", 20); // 保存圖片的寬和高
intent.putExtra("outputY", 40);
intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路徑
intent.putExtra("outputFormat", "JPEG");// 返回格式
startActivityForResult(intent, 0);
// 剪切特定圖片
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setClassName("com.android.camera", "com.android.camera.CropImage");
intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp")));
intent.putExtra("outputX", 1); // 剪切的寬高比為1:2
intent.putExtra("outputY", 2);
intent.putExtra("aspectX", 20); // 保存圖片的寬和高
intent.putExtra("aspectY", 40);
intent.putExtra("scale", true);
intent.putExtra("noFaceDetection", true);
intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp"));
startActivityForResult(intent, 0);
//11.打開Google Market
// 打開Google Market直接進(jìn)入該程序的詳細(xì)頁面
Uri uri = Uri.parse("market://details?id=" + "com.demo.app");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//12.進(jìn)入手機(jī)設(shè)置界面:
// 進(jìn)入無線網(wǎng)絡(luò)設(shè)置界面(其它可以舉一反三)
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
startActivityForResult(intent, 0);
//13.安裝apk:
Uri installUri = Uri.fromParts("package", "xxx", null);
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
//14.卸載apk:
Uri uri = Uri.fromParts("package", strPackageName, null);
Intent it = new Intent(Intent.ACTION_DELETE, uri);
startActivity(it);
//15.發(fā)送附件:
Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3");
sendIntent.setType("audio/mp3");
startActivity(Intent.createChooser(it, "Choose Email Client"));
//16.進(jìn)入聯(lián)系人頁面:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);
//17.查看指定聯(lián)系人:
Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);//info.id聯(lián)系人ID
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);