上一節,掌握了活動的創建,但是在啟動器中點擊應用的圖標只會進入到該應用的主活動,那么,如何從主活動跳轉到其他活動呢?
- 顯式Intent
?????????????Intent有多個構造函數,其中一個是Intent(Context packContext,Class<?>cls),這個構造函數接收兩個參數,一個參數Context要求提供一個啟動活動的上下文,第二個參數Class則是想要啟動的目標活動。
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//構造一個IntentstartActivity(intent);// Activity類中提供了一個startActivity()方法,這個方法專門用來啟動活動
????? ? 實現功能:通過在FirstActivity這個活動的基礎上打開SecondActivity這個活動
- 隱式Intent
- 更多隱式Intent用法
????????setData()方法接收一個Uri對象,主要用于指定當前Intent正在操作的數據,而這些數據通常都是以字符串形式傳入到Uri.parse()方法解析產生的。
intent.setData(Uri.parse("https://www.baidu.com"));
- 向下一個活動傳遞數據
putExtra()方法傳遞了一個字符串,這里putExtra()方法接收兩個參數,第一個是鍵(類似于id),用于后面從Intent中取值,第二個參數才是要傳遞的數據。
- 返回數據給上一個活動
? ??Activity中還有一個startActivityForResult()方法也是用來啟動活動的,但是這個方法期望在活動銷毀的時候能夠返回一個結果給上一個活動。
------------------------------------------------------------------------------------
下面具體分析:
Intent是Android程序中個組件之間進行交互的一種重要方式,它不僅可以指明當前想要執行的動作,還可以在不同組件之間傳遞數據。Intent一般可被用于啟動活動,啟動服務以及發送廣播等場景。
Intent 分為顯示Intent和隱式Intent。
- 顯式Intent
快速在ActivityTest中創建一個活動,右擊com.example.activitytest包new一個Empty Activity,將活動命名為SecondActivity,并勾選Generate Layout File,給布局文件命名為second_layout,但不要勾選Launcher Activity選項。 完成創建會生成SecondActivity.java和second_layout.xml兩個文件,編輯second_layout.xml文件,添加代碼(添加一個Button),代碼如下:(勾選Generate Layout File,會自動生成SecondActivity.java和second_layout.xml兩個文件,并且second_layout.xml會在SecondActivity.java加載,不勾選Generate Layout File不會自動生成second_layout.xml這個文件,需要再手動創建,并在SecondActivity.java加載second_layout.xml即在onCreate()方法中添加:setContentView(R.layout.second_layout);)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/button_2"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Button 2"/>
</LinearLayout>
另外,每一個活動都需要在AndroidManifest.xml文件中注冊,不過這一步,AS已經自動完成了。打開AndroidManifest.xml文件,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.activitytest"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".FirstActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".SecondActivity"></activity></application></manifest>
因為SecondActivity不是主活動,因此不需要配置<intent-filter>標簽里的內容。
????Intent有多個構造函數,其中一個是Intent(Context packContext,Class<?>cls),這個構造函數接收兩個參數,一個參數Context要求提供一個啟動活動的上下文,第二個參數Class則是想要啟動的目標活動。
? ? Activity類中提供了一個startActivity()方法,這個方法專門用來啟動活動,它接收一個Intent參數,這里我們將構建好的Intent傳入startActivity()方法就可以啟動目標活動了,修改FirstActivity的按鈕事件,代碼如下:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_activity);Button button1 = (Button)findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();提醒Intent intent = new Intent(FirstActivity.this,SecondActivity.class);startActivity(intent);}});}
先構建出一個Intent,以FirstActivity.this為上下文,傳入SecondActivity.class為目標活動。即在FirstActivity這個活動的基礎上打開SecondActivity這個活動,然后通過startActivity()方法來執行這個Intent。
現在可以運行程序了。
按back鍵就可以銷毀當前活動,返回上一個活動。
- 隱式Intent
所謂隱式Intent,并不明確指出我們想要啟動哪一個活動,而是指定以一系列更為抽象的action和category等信息,然后交由系統去分析這個Intent,并幫我們找出合適的活動去啟動。
? ? 所謂合適的活動是指:可以相應我們這個隱式Intent的活動。目前SecondActivity還無法去響應隱式Intent。
通過在<activity>標簽下配置<intent-filter>的內容,可以指定當前活動能夠響應的action和category,打開ActivityManifest.xml文件中,添加如下代碼:
<activity android:name=".SecondActivity"><intent-filter><action android:name="com.example.activitytest.ACTION_START"/><category android:name="android.intent.category.DEFAULT"/></intent-filter>
</activity>
我們指明了當前活動可以響應com.example.activitytest.ACTION_START這個action,更精確的指明了當前活動能夠響應的Intent中可能還帶有的category。并且,只有<action>和<category>中的內容同時能夠匹配上Intent中指定的action和category時,這個活動才能響應該Intent。
修改FirstActivity中按鈕的點擊事件,代碼如下:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_activity);Button button1 = (Button)findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();提醒//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//startActivity(intent);Intent intent = new Intent("com.example.activitytest.ACTION_START");startActivity(intent);}});}
????這里是Intent的另一個構造函數,直接將action的字符串傳了進去,表明我們想要啟動能夠響應com.example.activitytest.ACTION_START這個action 的活動。因為android.intent.category.DEFAULT是一種默認的category,但是在調用startActivity()方法的時候會自動將這個默認的category添加到Intent中,所以可以不用寫android.intent.category.DEFAULT這條代碼,而且編譯之后,也不會再Intent里自動添加。
現在又可以運行一下程序。
????????每個Intent中只能指定一個action,但卻能指定多個category。現在我們只有一個默認的category(并且這條語句并不存在。)我們可以再添加一個自定義category。
修改FirstActivity中的按鈕的點擊事件。代碼如下:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_activity);Button button1 = (Button)findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();提醒//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//startActivity(intent);Intent intent = new Intent("com.example.activitytest.ACTION_START"); ?intent.addCategory("com.example.activitytest.MY_CATEGORY");startActivity(intent);}});}
????我們指定了一個自定義的category,值為com.example.activitytest.MY_CATEGORY。那我們是不是也可以指定一個自定義的category,值為com.example.activitytest.MYYY_CATEGORY呢?
重新運行成我們發現程序崩潰了,在logcat界面查看錯誤日志,我們發現沒有任何一個活動可以響應我們的Intent,因為我們剛剛在Intent中新增了一個category,而SecondActivity的<intent-filter>標簽中并沒有聲明這個category。
那這樣看來,我們是不是可以在SecondActivity的<intent-filter>標簽中聲明這個category,來挽救這個程序呢?
我們姑且一試。發現是可以的,程序又正常了。代碼如下:
<activity android:name=".SecondActivity"><intent-filter><action android:name="com.example.activitytest.ACTION_START"/><category android:name="android.intent.category.DEFAULT"/> <category android:name="com.example.activitytest.MY_CATEGORY"/></intent-filter></activity>
這樣或許才算是真正意義上的“只有<action>和<category>中的內容同時能夠匹配上Intent中指定的action和category時,這個活動才能響應該Intent”
- 更多隱式Intent的用法
使用隱式Intent,我們不僅可以啟動自己程序內的活動,還可以啟動其他程序的活動,這使得Android多個應用程序之間的功能共享成為了可能。好比你需要展示一個網頁,但是你沒必要自己去實現一個瀏覽器,只需要調用系統的瀏覽器即可。
修改FirstActivity中的按鈕點擊事件的代碼,如下:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_activity);Button button1 = (Button)findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();//提醒//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//顯示Intent//startActivity(intent);//啟動intent//Intent intent = new Intent("com.example.activitytest.ACTION_START");//1個Intent,1個默認的category(DEFAULT),1個自定義的category(MY_CATEGORY)//intent.addCategory("com.example.activitytest.MY_CATEGORY");//startActivity(intent);Intent intent = new Intent(Intent.ACTION_VIEW);//ACTION_VIEW是一個Android系統內置的動作,其常量值為android.intent.action.VIEWintent.setData(Uri.parse("https://www.baidu.com"));startActivity(intent);}});}
????????首先指定了action是Intent.ACTION_VIEW,這是一個android系統的內置動作,其常量值為android.intent.action。然后將Uri.parse()方法將一個網址字符串解析成一個Uri對象,再調用Intent的setData()方法將這個Uri對象傳遞進去。setData()方法接收一個Uri對象,主要用于指定當前Intent正在操作的數據,而這些數據通常都是以字符串形式傳入到Uri.parse()方法解析產生的。我們還可以在<intent-filter>標簽中再配置一個<data>標簽,用于精確地指定當前活動能夠響應什么類型的數據。只有<data>標簽中指定的內容和Intent中攜帶的Data完全一致時,當前活動才能夠響應該Intent.
讓我們再新建一個ThirdActivity,接著詳解上面的內容。
右擊com.example.activitytest,new一個Empty Activity,新建ThirdActivity,首先不勾選Generate Layout File,因為作者發現自己勾選之后,產生Root element的不是默認的LinearLayout類型的,而是其他類型,在這里,我們分別手動創建Activity和對應的布局文件,并在onCreate()中添加加載語句:setContentView(R.layout.third_layout);
ThirdActivity創建完成后,編輯third_layout.xml文件,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/button_3"android:layout_width="match_parent"android:layout_height="wrap_content" android:text="Button 3"/></LinearLayout>
簡言之,就是添加了一個Button。
然后在AndroidManifest.xml文件中修改ThirdActivity中注冊信息,代碼如下(我們只包含ThirdActivity):
<activity android:name=".ThirdActivity"> <intent-filter><action android:name="android.intent.action.VIEW"/><category android:name="android.intent.category.DEFAULT"/><data android:scheme="http"/></intent-filter>
</activity>
其中,這個<acitivity>是自動幫我們注冊,里面的<intent-filter>是需要我們手動寫入的。我們在<intent-filter>中配置了當前活動能夠響應的action是Intent.ACTION.VIEW的常量值,而category則毫無疑問制定了默認的category值,另外在<data>我們通過android:scheme指定了數據的協議必須是http協議,這樣ThirdActivity應該就和瀏覽器一樣,能夠響應一個打開網頁的Intent了。
現在我們又可以去運行一下程序了。除了http協議,我們還可以指定其他許多協議,比如geo表示顯示地理位置,tel表示撥打電話,我們可以在程序中調用系統撥號界面。代碼如下:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_activity);Button button1 = (Button)findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();提醒//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//顯示Intent//startActivity(intent);//啟動intent//Intent intent = new Intent("com.example.activitytest.ACTION_START");//1個Intent,1個默認的category(DEFAULT),1個自定義的category(MY_CATEGORY)//intent.addCategory("com.example.activitytest.MY_CATEGORY");//startActivity(intent);// Intent intent = new Intent(Intent.ACTION_VIEW);//ACTION_VIEW是一個Android系統內置的動作,其常量值為android.intent.action.VIEW// intent.setData(Uri.parse("https://www.baidu.com"));// startActivity(intent);Intent intent = new Intent(Intent.ACTION_DIAL);//ACTION_DIAL是一個Android 系統的內置動作,在Data部分指定了協議是tel,號碼是10086.intent.setData(Uri.parse("tel:10086"));startActivity(intent);}});
現在我們又可以運行一下程序了。
- 向下一個活動傳遞數據
我們可以簡單的使用Intent來啟動一個活動,其實Intent還可以在啟動活動的時候傳遞數據。
Intent中提供了一些列putExtra()方法的重載,可以把我們想要傳遞的數據暫存在intent中,啟動了另一個活動后,只需要把這些數據再從Intent中取出就可以了,可以在FirstActivity中定義一個字符串,現在想把這個字符串傳遞到SecondActivity中,可以這樣編寫,代碼如下:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.first_activity);Button button1 = (Button)findViewById(R.id.button_1);button1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();提醒//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//顯示Intent//startActivity(intent);//啟動intent//Intent intent = new Intent("com.example.activitytest.ACTION_START");//1個Intent,1個默認的category(DEFAULT),1個自定義的category(MY_CATEGORY)//intent.addCategory("com.example.activitytest.MY_CATEGORY");//startActivity(intent);// Intent intent = new Intent(Intent.ACTION_VIEW);//ACTION_VIEW是一個Android系統內置的動作,其常量值為android.intent.action.VIEW// intent.setData(Uri.parse("https://www.baidu.com"));// startActivity(intent);//Intent intent = new Intent(Intent.ACTION_DIAL);//ACTION_DIAL是一個Android 系統的內置動作,在Data部分指定了協議是tel,號碼是10086.//intent.setData(Uri.parse("tel:10086"));//startActivity(intent); String data = "Hello SecondActivity";Intent intent = new Intent(FirstActivity.this,SecondActivity.class);intent.putExtra("extra_data",data);startActivity(intent);}});}
我們是使用顯式Intent來啟動SecondActivity,并通過putExtra()方法傳遞了一個字符串,這里putExtra()方法接收兩個參數,第一個是鍵(類似于id),用于后面從Intent中取值,第二個參數才是要傳遞的數據。
然后我們要在SecondActivity中將要傳遞的數據取出,并打印出來。(在SecondActivity.java文件進行操作)代碼如下:
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.second_layout); Intent intent = getIntent();String data = intent.getStringExtra("extra_data");Log.d("SecondActivity",data);}
}
首先通過getIntent()方法來獲取到用來啟動SecondActivity的Intent,然后調用getStringExtra()方法來獲取傳遞的數據,如果傳遞的數據是整型,則調用getIntExtra()方法,如果傳遞的是布爾類型數據,則調用getBooleanExtra()方法。
現在,我們又可以運行程序了。點擊Button1 會跳轉到SecondActivity,查看logcat打印信息。我們可以看到從FirstActivity傳遞來的數據。
- 返回數據給上一個活動
既然可以給下一個活動傳遞數據,是不是也可以給上一個活動返回數據呢?不過不同的是,返回上一個活動只需要按一下Back鍵就可以了,沒有一個用于啟動活動的Intent來傳遞數據。
查閱文檔會發現:
????? ? Activity中還有一個startActivityForResult()方法也是用來啟動活動的,但是這個方法期望在活動銷毀的時候能夠返回一個結果給上一個活動。
? ? startActivityForResult()方法接收兩個參數,第一個參數是Intent;第二個參數是請求碼,用于在之后的回掉中判斷數據的來源。修改FirstActivity中按鈕的點擊事件,代碼如下:
public void onClick(View view) {// Toast.makeText(FirstActivity.this,"You clicked Button 1",Toast.LENGTH_SHORT).show();提醒//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//顯示Intent//startActivity(intent);//啟動intent//Intent intent = new Intent("com.example.activitytest.ACTION_START");//1個Intent,1個默認的category(DEFAULT),1個自定義的category(MY_CATEGORY)//intent.addCategory("com.example.activitytest.MY_CATEGORY");//startActivity(intent);// Intent intent = new Intent(Intent.ACTION_VIEW);//ACTION_VIEW是一個Android系統內置的動作,其常量值為android.intent.action.VIEW// intent.setData(Uri.parse("https://www.baidu.com"));// startActivity(intent);//Intent intent = new Intent(Intent.ACTION_DIAL);//ACTION_DIAL是一個Android 系統的內置動作,在Data部分指定了協議是tel,號碼是10086.//intent.setData(Uri.parse("tel:10086"));//startActivity(intent);//String data = "Hello SecondActivity"; //向下傳遞數據//Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//intent.putExtra("extra_data",data);//startActivity(intent);Intent intent = new Intent(FirstActivity.this,SecondActivity.class);startActivityForResult(intent,1);}
使用了startActivityForResult()方法來啟動SecodActivity,請求碼只要是一個唯一值就可以了,這里傳入了1。
我們要在SecondAcitivity中給按鈕注冊點擊事件,并在點擊事件中添加返回數據類型的邏輯,代碼如下:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.second_layout);// Intent intent = getIntent();//String data = intent.getStringExtra("extra_data");//Log.d("SecondActivity",data);Button button2 = (Button) findViewById(R.id.button_2);button2.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent = new Intent();intent.putExtra("data_return","Hellow FistActivity");setResult(RESULT_OK,intent);finish();}});}
我們還是構建了一個Intent,只不過這個Intent不僅僅是用來傳遞數據而已,它沒有指定任何的意圖,緊接著把要傳遞的數據存放在Intent中,然后調用了setResult()方法,setResult方法是非常重要的,是專門用來向上一個活動傳遞數據。setResult()方法接收兩個參數,第一個參數是用用于向上一個活動返回處理結果,一般只使用RESULT_OK或RESULT_CANCELED這兩個值,第二個參數則把帶有數據的Intent傳遞回去,然后調用finish()方法來銷毀當前活動。這樣就實現了在銷毀當前活動時,把要傳遞的數據返回給上一個活動。
由于我們是使用startActivityForResult()方法來啟動SecondActivity的,在SecondActivity被銷毀后會回調上一個活動的onActivityResult()方法,因此我們需要在FirstActivity中重寫這個方法來得到這個數據。代碼如下:
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {switch (requestCode){case 1:if(resultCode == RESULT_OK){String ReturnedData = data.getStringExtra("data_return");Log.d("FirstActivity",ReturnedData);}break;default:}}
onActivityResult()方法帶有三個參數,第一個參數是requestCode,即我們在啟動活動時傳入的請求碼(來判斷數據來源),第二個參數是resultCode(處理結果是否成功),就是我們在返回數據時傳入的數據集。第三個參數data,就是我們攜帶著返回數據的intent。
現在我們又可以運行程序了。
但是當我們不是通過點擊SecondActivity的按鈕來返回數據,而是通過按下Back鍵,這樣數據是不是沒有辦法返回了呢?我們可以在SecondActivity中重寫onBackPressed()方法,代碼如下:
@Overridepublic void onBackPressed() {Intent intent = new Intent();intent.putExtra("data_return","Hello FirstActivity");setResult(RESULT_OK,intent);finish();}
按下Back鍵,就會執行onBackPressed()方法中的代碼。
在@override處,按下Ctrl + O,就可以出現你重寫的方法,你就可以選擇你要重寫的方法。