編輯:關於Android編程
Android其中最重要的特性之一,就是一個應用可以基於“action”來切換到另一個應用。比如,你的應用想要查找地方,在地圖上顯示。但是不一定要創建一個activity來顯示地圖,可以使用Intent發起一個請求來查看地址,然後Android系統會啟動一個可以顯示地圖的應用。
之前,會使用到顯式的Intent來讓一個activity跳轉到另一個activity。但是,當想要跳轉到一個獨立的應用時,比如查看地圖,這時候就一定要使用隱式Intent。
隱式Intent不用像顯式Intent那樣傳入一個目標activity的類名,只需要傳入操作的響應,也就是action。這個action表明了你想要做什麼。Intents通常會包含與action相關的數據,比如想要查看的地址,或者想要發送的郵件信息等。這取決於你想要做什麼,數據可能是Uri類型,也可能是其他類型,或者根本就不需要數據。
如果數據是Uri類型的,下面一個簡單的Intent()構造函數例子,可以用來定義action和數據。
比如說,怎麼樣創建一個Intent初始化一個電話撥號,用Uri類型來作為電話號碼的類型。
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
查看地圖:
Uri location = Uri.parse("geo:38.899533,-77.036476");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
打開網頁:
Uri website = Uri.parse("http://xxxxx.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, website);
發送郵件:
Uri emailUri = Uri.parse("mailto:[email protected]");
intent emailIntent = new Intent(Intent.ACTION_SENDTO, emailUri);
想要傳遞其他不同類型的數據,可以調用不同的putExtra()方法,把數據添加進去隱式的Intent中。
默認的情況下,系統基於所包含的 Uri 數據確定Intent需要的相應 MIME 類型。如果Intent中不包含Uri,應該使用 setType()來指定與Intent相關的數據。設置MIME類型指定那一種類型的activity可以接收這個Intent。
發送一個郵件附件:
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));
這個Intent沒有Uri,所以聲明MIME類型為 “text/plain”
注意:定義Intent時要盡可能具體,這是非常重要的。比如,如果你使用ACTION_VIEW inten想要展示一張圖片,那你就應該指定MIME類型為 image/*. 這就防止了你的app被Intent觸發之後意外地查看到其它的數據類型(比如地圖應用)
盡管Android系統保證每一個確切的Intent都會對應apps中發起的一個請求響應(比如電話、郵件或者地圖應用等),但是在調用一個Intent之前還是應該包含驗證這一步驟的。如果調用了一個Intent,但是設備中並沒有一個可以處理這個Intent請求響應的應用,這時候app就會崩潰了。
檢驗過程並不復雜,創建一個PackageManager的對象,調用queryIntentActivities() 來獲取activity的List容器,這個可以用來處理Intent。如果這個List不為空的話,那就說明至少有一個應用可以接這個Intent,調用Intent是安全的。
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
一旦已經創建了Intent,並且設置好了額外的信息,就可以調用startActivity()來發送給系統了。如果系統發現有多個應用都可以出來響應這個Intent,那麼就會顯示一個對話框出來,讓你選擇其中一個應用。如果你之前已經選擇了默認的應用,那就會直接打開。
效果圖,因為已經選擇默認的浏覽應用了,就直接打開<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160521/20160521092232473.gif" title="\" />
結合上面的檢驗步驟,代碼如下:
Uri website = Uri.parse("http://www.baidu.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, website);
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
if(isIntentSafe){
startActivity(webIntent);
}
用戶可以勾選選擇框來默認啟動的應用,可能每一次用戶都會選擇用一個應用來打開網頁或者拍照。但是呢,如果類似於分享這種,用戶可能就會要選擇幾個不同的應用來分享了。以應該有一個選擇對話框可以讓用戶選擇。畢竟不能讓用戶只能勾選一個默認應用吧。
所以,需要使用 createChooser()來創建一個intent,然後再把它傳進startActivity()中。這樣,通過傳入createChooser()方法返回的Intent,就會顯示一個選擇窗口,可以選擇不同的應用。標題就是傳入的title。
Intent intent = new Intent(Intent.ACTION_SEND);
...
// 最好在字符串資源中定義UI文本,這個文本顯示的是"Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// 創建一個Intent來顯示選擇窗口,傳入intetn和標題
Intent chooser = Intent.createChooser(intent, title);
//驗證
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
啟動另一個應用並不是只有startActivity()這個方法,也可以使用startActivityForResult()來代替startActivity(),這樣啟動另一應用的同時,還可以獲得返回的結果。比如,啟動照相機應用,然後會接收拍到的照片作為結果。當然,在activity中,需要一個方法來接收這個返回結果。activity在 onActivityResult()方法中接收。
注意:當調用 startActivityForResult(),可以使用顯式Intent,也可以使用隱式Intent。當啟動自己的Activity來接收結果時,應使用顯式Intent確保可收到預期的結果。
接下來看看一個啟動照相機應用的例子。
先添加權限,請求系統硬件的權限,還有保存照片寫入文件的權限。
簡單的布局,一個Button啟動,一個ImageView顯示圖片
然後到MainActivity。首先,當然先初始化組件,在Oncreate()函數中調用startActivityForResult()方法啟動照相機,這個方法需要傳入兩個參數,一個是Intent對象,一個是請求碼,用來標識是哪一個發起的請求。
先定義兩個請求碼
public static final int TAKE_PHOTO = 1;
public static final int SHOW_PICTURE = 2;
onCreate方法中,先new一個File用來保存圖片,使用隱式Intent發起請求android.media.action.IMAGE_CAPTURE
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
takePhoto = (Button) findViewById(R.id.take_photo);
picture = (ImageView) findViewById(R.id.picture);
takePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
File outImage = new File(Environment.getExternalStorageDirectory(),"my_image.jpg");
try {
if(outImage.exists()){
outImage.delete();
}
outImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
imageUri = Uri.fromFile(outImage);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
boolean IsIntentSafe = activities.size() > 0;
if(IsIntentSafe){
startActivityForResult(intent, TAKE_PHOTO);
}
}
接下來是另一方法onActivityResult,就是用來接收結果、處理結果的方法。
有三個參數
第一個是請求碼,就是上面 startActivityForResult方法中傳入的請求碼。
第二個是結果碼,RESULT_OK表示操作成功, RESULT_CANCELED表示用戶退出或者因為某種原因。
第三個參數就是傳送數據的Intent。
下面第一個處理是裁剪照片,使用startActivityForResult啟動裁剪,Intent傳入的是com.android.camera.action.CROP,請求碼SHOW_PICTURE。操作成功後,第二個處理是顯示照片
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case TAKE_PHOTO :
if(resultCode==RESULT_OK){
Intent intent = new Intent("com.android.camera.action.CROP");
if(data !=null){
intent.setDataAndType(data.getData(),"image/*");
} else {
intent.setDataAndType(imageUri,"image/*");
}
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent,SHOW_PICTURE);
}
break;
case SHOW_PICTURE:
if(resultCode==RESULT_OK){
try {
Bitmap bitmap = BitmapFactory.decodeStream(
getContentResolver().openInputStream(imageUri)
);
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
前面講的都是:從你的應用開始另一個應用的Activity。但如果你的應用可以執行對另一個應用有用的操作,應准備好響應來自其他應用的操作請求。 例如,如果構建一款可與用戶的好友分享消息或照片的社交應用,最關注的是支持 ACTION_SEND,以便用戶可以從另一應用發起 “共享”操作並且啟動你的應用執行該操作。
要允許其他應用開始你的Activity,您需要 在相應元素的Manifest文件中添加一個 元素。
當應用安裝在手機上時,系統會識別你的intent-filter並添加信息至所有已安裝應用支持的Intent內部目錄。當其他應用通過隱式Intent調用 startActivity() 或 startActivityForResult() 時,系統會找到可以響應該Intent的Activity。
為了確定哪一種Intent你的應用可以出來,每一個intent filter的添加應該盡可能地指定響應的類型和接收的數據類型。如果Activity具有滿足以下 Intent 對象條件的intent filter,系統可能向Activity發送給定的 Intent:
Action
一個響應操作的字符串名稱。例如ACTION_SEND 或者 ACTION_VIEW 。在intent filter中使用< action >來指定。指定的值一定是響應的完整字符串名稱。
Data
跟intent相關的數據的描述。
在intent filter中使用< data >來指定。標簽中,可以使用一個或多個屬性,但是只能指定MIME類型,URI的前綴,URI的模式或者這些方法的組合和其他一些可接受的數據類型。如果你不需要聲明關於這個數據的Uri(比如當你的activity處理其他的“extra”類型,而不是Uri),應該只指定這個android:mimeType屬性來聲明處理數據的類型。比如text/plain或者 image/jpeg。
1、android:scheme
用於指定數據的協議部分,如 http 部分。
2、 android:host
用於指定數據的主機名部分,如 www.baidu.com部分。
3、android:port
用於指定數據的端口部分,一般緊隨在主機名之後。
4、 android:path
用於指定主機名和端口之後的部分,如一段網址中跟在域名之後的內容。
5、android:mimeType
用於指定可以處理的數據類型,允許使用通配符的方式進行指定。
Category
< category > 標簽則包含了一些附加信息,更精確地指明了當前的活動能夠響應的 Intent中還可能帶有的 category。只有< action >和< category >中的內容同時能夠匹配上 Intent 中指定的 action 和 category 時,這個活動才能響應該 Intent。但是category大部分很少用。所有的隱式intent默認定義為CATEGORY_DEFAULT。
每個Intent中只能指定一個action,但卻能指定多個category。為了能夠接收到其他應用發送過來的隱式intent,必須要在intent filter中包含 CATEGORY_DEFAULT類別 。方法 startActivity() 和 startActivityForResult() 就像聲明 了CATEGORY_DEFAULT 類別那樣處理所有的Intent。
比如,這裡定義了一個activity,可以出來 ACTION_SEND intent,當數據類型是text或者image。
為了決定在你的Activity執行哪種操作,先讀取用於開始Activity的 Intent。
當你的Activity開始時,調用 getIntent()來獲取一個啟動你的Activity的 Intent。可以在Activity生命周期的任何時間執行此操作,但通常應該在 onCreate() 或 onStart()中執行。
例如:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取啟動你的activity的Intent
Intent intent = getIntent();
Uri data = intent.getData();
// 找出這個intent的類型
if (intent.getType().indexOf("image/") != -1) {
// 使用image數據處理
} else if (intent.getType().equals("text/plain")) {
// 使用text來處理
}
}
如果想要向調用你的應用的Activity返回一個結果,只需調用 setResult() 來指定結果碼和結果的Intent。當操作完成且返回原來的Activity時,調用 finish() 關閉(和destroy)你的Activity。
例如:
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();
必須要指定一個結果碼作為結果。通常是RESULT_OK 或者 RESULT_CANCELED,如果需要的話,還可以提供一個裝有數據的Intent。結果碼默認設置為RESULT_CANCELED。所以,如果用戶在完成操作之前和返回你設置的結果之前就點擊返回鍵,那麼原來的activity就會接收到RESULT_CANCELED作為結果。
如果只是簡單地返回一個整數表示其中一個選擇的結果,可以設置結果碼為任何大於0的值。如果想用結果碼傳遞一個整數又不需要包含一個Intent,可以調用setResult()緊緊傳入結果碼。
例如:
setResult(RESULT_COLOR_RED);
finish();
這裡不需要檢查activity是被startActivity() 還是startActivityForResult()啟動的。如果啟動你的activity的inten可能期待返回一個結果,那麼就簡單地調用setResult()。如果原來的activity已經調用了startActivityForResult(),那麼系統會傳送你提供給setResult()的結果。否則,結果會被忽略掉。
到這裡,結合之前照相機的例子想一想,也明白了大概的流程了。
Android界面的坐標是以左上角為起始點,平行的為X軸,垂直的為Y軸,數值都是遞增的。如下圖所示: Android的Rect類是形成一個矩形的區域,區域
Android Studio中有一個被叫矢量工具(Vector Asset Studio)它能幫助我們增加多種圖標和導入可以拉伸的矢量圖形文件作為矢量資源圖片在項目中。利
這裡我們將采取的方案是使用組合View的方式,先自定義一個布局繼承自LinearLayout,然後在這個布局中加入下拉頭和ListView這兩個子元素,並讓這兩個子元素縱
微信附近的人是一個可以看到周邊人也在玩這個功能的人。有朋友說他可以看到別人,但是別人看不到他,這是為什麼呀?我的微信能看到附近的人,但附近的人看不見我,怎麼