編輯:關於Android編程
發送短信的時候,我們要使用的action是Intent.ACTION_SENDTO
,並且要指定其URI是smsto:
協議,這樣能保證是短信應用接收並處理我們的intent對象,而不是其他應用接收,從而准確實現發送短信的目的。如果我們的action不是Intent.ACTION_SENDTO
,而是Intent.ACTION_SEND
,且沒有指定smsto:
協議的URI的話,那麼Android在接收到intent對象之後不會直接啟動短信應用,而是彈出了App Chooser,讓我們選擇要啟動哪個應用,比如電子郵件、QQ等等,所以為了確保直接啟動短信應用,我們應該使用Intent.ACTION_SENDTO
並且指定smsto:
協議的URI。
示例代碼如下:
//使用ACTION_SENDTO而不是ACTION_SEND
Intent intent = new Intent(Intent.ACTION_SENDTO);
//指定URI使用smsto:協議,協議後面是接收短信的對象
Uri uri = Uri.parse(smsto:10086);
intent.setData(uri);
//設置消息體
intent.putExtra(sms_body, 手頭有點緊,借點錢吧~~);
ComponentName componentName = intent.resolveActivity(getPackageManager());
if(componentName != null){
startActivity(intent);
}
在構造發送短信的URI時,前面是smsto:
協議,後面跟的是接收短信的對方的手機號。如果在構建URI時,只寫了smsto:
,而沒有寫後面的手機的號的話,那麼該intent也可以成功啟動短信應用,不過這種情形下,在啟動了短信應用之後,還需要我們自己再手動輸入接收信息的手機號。我們通過key為sms_body
的extra設置短信的內容。
需要注意的是,在執行了startActivity(intent)
之後,雖然短信應用啟動了,但是短信沒有直接發出去,需要我們再點擊一下發送消息才可以。
發送郵件的時候,我們要使用的action也是Intent.ACTION_SENDTO
,並且要指定其URI是mailto:
協議,這樣能保證是郵件應用接收並處理我們的intent對象,而不是其他應用接收,從而准確實現發送郵件的目的。如果我們的action不是Intent.ACTION_SENDTO
,而是Intent.ACTION_SEND
,且沒有指定mailto:
協議的URI的話,那麼Android在接收到intent對象之後不會直接郵件應用,而是彈出了App Chooser,讓我們選擇要啟動哪個應用,比如短信、QQ等等,所以為了確保直接啟動郵件應用,我們應該使用Intent.ACTION_SENDTO
並且指定mailto:
協議的URI。
示例代碼如下:
//使用ACTION_SENDTO而不是ACTION_SEND
Intent intent = new Intent(Intent.ACTION_SENDTO);
//指定URI使用mailto:協議,確保只有郵件應用能接收到此intent對象
Uri uri = Uri.parse(mailto:);
intent.setData(uri);
String[] addresses = {[email protected], [email protected]};
String[] cc = {[email protected]};
String[] bcc = {[email protected]};
String subject = 加班;
String content = 國慶正常上班~~;
//設置郵件的接收方
intent.putExtra(Intent.EXTRA_EMAIL, addresses);
//設置郵件的抄送方
intent.putExtra(Intent.EXTRA_CC, cc);
//設置郵件的密送方
intent.putExtra(Intent.EXTRA_BCC, bcc);
//設置郵件標題
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
//設置郵件內容
intent.putExtra(Intent.EXTRA_TEXT, content);
//設置郵件附件
//intent.putExtra(Intent.EXTRA_STREAM, Uri.parse(...));
ComponentName componentName = intent.resolveActivity(getPackageManager());
if(componentName != null){
startActivity(intent);
}
啟動郵件應用後的截圖如下所示:
我們分別通過key為Intent.EXTRA_EMAIL
、Intent.EXTRA_CC
和Intent.EXTRA_BCC
的extra依次設置郵件的接收方、抄送方、密送方,其值均為String數組。我們通過key為Intent.EXTRA_SUBJECT的extra設置郵件標題,通過key為Intent.EXTRA_TEXT
的extra設置郵件內容。如果想發送附件,那麼可以將附件封裝成Uri的形式,然後通過key為Intent.EXTRA_STREAM
的extra設置郵件附件。
需要注意的是,在執行了startActivity(intent)
之後,雖然郵件應用啟動打開了,但是郵件沒有直接發出去,需要我們再點擊一下右上角的發送按鈕才能將郵件發出去。
要想通過Intent打電話,我們有兩個可以使用的action:Intent.ACTION_DIAL
和Intent.ACTION_CALL
,二者有一定的區別。
如果使用Intent.ACTION_DIAL
作為intent對象的action,那麼當執行startActivity(intent)
之後,會啟動打電話應用,並且會自動輸入指定的手機號,但是不會自動撥打,需要我們手動按下撥打按鈕才能真正給對方打電話。
如果使用Intent.ACTION_CALL
作為intent對象的action,那麼當執行startActivity(intent)
之後,會啟動打電話應用,並且直接撥打我們指定的手機號,無需我們再手動按下撥打按鈕。但是需要注意的是,該action需要權限android.permission.CALL_PHONE
,如果在應用的AndroidManifest.xml文件中沒有添加該權限,那麼當指定到startActivity(intent)
這句代碼的時候,就會拋出異常,應用崩潰退出。
以下是示例代碼:
//Intent.ACTION_DIAL只撥號,不打電話
//Intent intent = new Intent(Intent.ACTION_DIAL);
//Intent.ACTION_CALL直接撥打指定電話,需要android.permission.CALL_PHONE權限
Intent intent = new Intent(Intent.ACTION_CALL);
Uri uri = Uri.parse(tel:10086);
intent.setData(uri);
ComponentName componentName = intent.resolveActivity(getPackageManager());
if(componentName != null){
startActivity(intent);
}
在該示例代碼中,我們使用了Intent.ACTION_CALL
作為intent對象的action,並且在AndroidManifest.xml中添加了如下權限:
我們使用tel:
協議的URI,在協議後面的是要撥打的號碼,將該Uri作為intent對象的data。
要想通過Intent啟動攝像機進行拍照,我們需要設置intent對象的action值為MediaStore.ACTION_IMAGE_CAPTURE
的action。然後我們用key為MediaStore.EXTRA_OUTPUT
的extra設置圖片的輸出路徑,最後調用startActivityForResult()
方法以啟動攝像機應用,並重寫我們的onActivityResult()
以便在該方法中得知拍照完成。
示例代碼如下:
//表示用於拍照的requestCode
private final int REQUEST_CODE_IMAGE_CAPTURE = 1;
//我們存儲照片的輸出路徑,以便後續使用
private Uri imageOutputUri = null;
//拍照
private void captureImage(){
PackageManager pm = getPackageManager();
//先判斷本機是否在硬件上有攝像能力
if(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)){
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
ComponentName componentName = intent.resolveActivity(pm);
//判斷手機上有無攝像機應用
if(componentName != null){
//創建圖片文件,以便於通過Uri.fromFile()生成對應的Uri
File imageFile = createImageFile();
if(imageFile != null){
//根據imageFile生成對應的Uri
imageOutputUri = Uri.fromFile(imageFile);
//利用該Uri作為拍照完成後照片的存儲路徑,注意,一旦設置了存儲路徑,我們就不能獲取縮略圖了
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageOutputUri);
//調用startActivityForResult()方法,以便在onActivityResult()方法中進行相應處理
startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE);
}else{
Toast.makeText(this, 無法創建圖像文件!, Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this, 未在本機找到Camera應用,無法拍照!, Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this, 本機沒有攝像頭,無法拍照!, Toast.LENGTH_LONG).show();
}
}
//創建圖片文件,以便於通過Uri.fromFile()生成對應的Uri
private File createImageFile(){
File image = null;
//用時間戳拼接文件名稱,防止文件重名
String timeStamp = new SimpleDateFormat(yyyyMMdd_HHmmss).format(new Date());
String imageFileName = JPEG_ + timeStamp + _;
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
try{
image = File.createTempFile(
imageFileName, //前綴
.jpg, //後綴
storageDir //文件夾
);
}catch (IOException e){
image = null;
e.printStackTrace();
Log.e(DemoLog, e.getMessage());
}
return image;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
//首先判斷是否正確完成
if(resultCode == RESULT_OK){
switch (requestCode){
case REQUEST_CODE_IMAGE_CAPTURE:
//此處,我們可以通過imageOutputUri獲取到我們想要的圖片
String imagePath = imageOutputUri.toString();
Log.i(DemoLog, 照片路徑是: + imagePath);
Toast.makeText(this, 照片路徑是: + imagePath, Toast.LENGTH_LONG).show();
//以下代碼嘗試獲取縮略圖
//如果設置MediaStore.EXTRA_OUTPUT作為extra的時候,那麼此處的intent為null,需要判斷
if(intent != null){
Bitmap thumbnail = intent.getParcelableExtra(data);
//有的手機並不會給拍照的圖片生成縮略圖,所以此處也要判斷
if(thumbnail != null){
Log.i(DemoLog, 得到縮略圖);
}
}
default:
break;
}
}
}
我們分析一下上面的代碼片段:
不是所有的Android設備都能拍照的,所以首先我們調用了PackageManager
的hasSystemFeature(PackageManager.FEATURE_CAMERA)
方法,判斷當前設備在硬件層級是否具有拍照的能力。
然後我們創建了一個action為MediaStore.ACTION_IMAGE_CAPTURE
的intent對象。
然後我們通過調用intent.resolveActivity(pm)
方法判斷當前設備有無攝像機應用以便我們啟動。如果沒有攝像機應用但是我們卻把intent對象傳遞給startActivity()
或startActivityForResult()
的話,就會拋出異常,應用崩潰退出。
我們自己寫了一個createImageFile
方法,通過該方法我們在自己的應用所對應的外設存儲卡上創建了一個圖片文件。需要注意的是,此步驟需要WRITE_EXTERNAL_STORAGE
權限,在AndroidManifest.xml中注冊如下:
關於該權限的更多信息,可參見WRITE_EXTERNAL_STORAGE。
我們利用上面生成的圖片文件生成了對應的Uri,將其存儲在Activity中類型為Uri的字段imageOutputUri中,之後我們執行了intent.putExtra(MediaStore.EXTRA_OUTPUT, imageOutputUri)
,利用該Uri作為拍照完成後照片的存儲路徑。
此處需要特別注意的是,一旦設置了存儲路徑,我們就不能在onActivityResult()中獲取縮略圖了。
最後我們需要調用方法startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE)
以啟動攝像機應用進行拍照,其中REQUEST_CODE_IMAGE_CAPTURE
是我們自定義指定的用於拍照的requestCode。
我們覆寫了onActivityResult
方法,拍照完成後觸發該方法的執行。首先我們要判斷resultCode
是否與RESULT_OK
相等,只有相等才表明拍照成功,然後我們判斷如果requestCode
是否等於REQUEST_CODE_IMAGE_CAPTURE
,若相等表明是拍照返回的結果。那麼此時,我們就可以通過我們之前存儲的imageOutputUri獲取剛剛拍完的照片了,其URI字符串如:
file:///storage/sdcard0/Android/data/com.ispring.commonintents/files/Pictures/JPEG_20150919_112704_533002075.jpg
需要注意的是,如果我們在第5步之中設置MediaStore.EXTRA_OUTPUT作為照片輸出路徑的話,那麼在onActivityResult
中無法獲取從攝像機應用換回的Intent,即為null,這樣也就無法獲取縮略圖。反之,如果在第5步沒有設置MediaStore.EXTRA_OUTPUT作為照片輸出路徑的話,intent不為空,可以嘗試執行Bitmap thumbnail = intent.getParcelableExtra(data)
獲取縮略圖,如果thumbnail不為空,表示能成功獲取縮略圖。但是有的手機並不會給拍照的圖片生成縮略圖,所以此處的thumbnail也有可能是null,所以在使用之前要先判斷。
通過Intent啟動攝像機進行攝像的步驟與上面剛提到的通過Intent啟動攝像機進行拍照的步驟非常相似,稍有區別。要啟動Camera進行攝像,我們需要給intent設置值為MediaStore.ACTION_VIDEO_CAPTURE
的action,然後我們用key為MediaStore.EXTRA_OUTPUT
的extra設置圖片的輸出路徑,最後調用startActivityForResult()
方法以啟動攝像機應用,並重寫我們的onActivityResult()
以便在該方法中得知攝像完成。
以下是示例代碼:
//表示用於錄視頻的requestCode
private final int REQUEST_CODE_VIDEO_CAPTURE = 2;
//我們存儲視頻的輸出路徑,以便後續使用
private Uri videoOutputUri = null;
//攝像
private void captureVideo(){
PackageManager pm = getPackageManager();
//先判斷本機是否在硬件上有攝像能力
if(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)){
//將intent的action設置為MediaStore.ACTION_VIDEO_CAPTURE
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
ComponentName componentName = intent.resolveActivity(pm);
//判斷手機上有無攝像機應用
if(componentName != null){
//創建視頻文件,以便於通過Uri.fromFile()生成對應的Uri
File videoFile = createVideoFile();
if(videoFile != null){
//根據videoFile生成對應的Uri
videoOutputUri = Uri.fromFile(videoFile);
//利用該Uri作為攝像完成後視頻的存儲路徑
intent.putExtra(MediaStore.EXTRA_OUTPUT, videoOutputUri);
//調用startActivityForResult()方法,以便在onActivityResult()方法中進行相應處理
startActivityForResult(intent, REQUEST_CODE_VIDEO_CAPTURE);
}else{
Toast.makeText(this, 無法創建視頻文件!, Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this, 未在本機找到Camera應用,無法攝像!, Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this, 本機沒有攝像頭,無法攝像!, Toast.LENGTH_LONG).show();
}
}
//創建視頻文件,以便於通過Uri.fromFile()生成對應的Uri
private File createVideoFile(){
File videoFile = null;
//用時間戳拼接文件名稱,防止文件重名
String timeStamp = new SimpleDateFormat(yyyyMMdd_HHmmss).format(new Date());
String imageFileName = MP4 + timeStamp + _;
File storageDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES);
try{
videoFile = File.createTempFile(
imageFileName, //前綴
.mp4, //後綴
storageDir //文件夾
);
}catch (IOException e){
videoFile = null;
e.printStackTrace();
Log.e(DemoLog, e.getMessage());
}
return videoFile;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
//首先判斷是否正確完成
if(resultCode == RESULT_OK){
switch (requestCode){
case REQUEST_CODE_VIDEO_CAPTURE:
//如果設置MediaStore.EXTRA_OUTPUT作為extra的時候,
//在有的手機上,此處的intent為不為null,但是在有的手機上卻為null,
//所以不建議從intent.getData()中獲取視頻路徑
//我們應該自己記錄videoOutputUri來得知視頻路徑,下面注釋的代碼不建議使用
/*if(intent != null){
Uri videoUri = intent.getData();
if(videoUri != null){
//路徑格式如content://media/external/video/media/130025
Log.i(DemoLog, 視頻路徑是: + videoUri.toString());
}
}*/
String videoPath = videoOutputUri.toString();
//1.如果沒有設置MediaStore.EXTRA_OUTPUT作為視頻文件存儲路徑,那麼路徑格式如下所示:
// 路徑格式如content://media/external/video/media/130025
//2.如果設置了MediaStore.EXTRA_OUTPUT作為視頻文件存儲路徑,那麼路徑格式如下所示:
// 路徑格式如file:///storage/sdcard0/Android/data/com.ispring.commonintents/files/Movies/MP420150919_184132_533002075.mp4
Log.i(DemoLog, 視頻路徑是: + videoPath);
Toast.makeText(this, 視頻路徑是: + videoPath, Toast.LENGTH_LONG).show();
break;
default:
break;
}
}
}
可以看到上面啟動Camera攝像的代碼與拍照的代碼幾乎完全一樣,具體解釋參見對拍照代碼的描述。在該示例代碼中,我們通過MediaStore.EXTRA_OUTPUT
設置了視頻的存放路徑,拍照的時候我們也通過它設置了照片的輸出路徑,但是二者稍有區別:
1. 對於拍照,設置了MediaStore.EXTRA_OUTPUT
之後,onActivityResult
中的Intent參數是null,不能從Intent中得知照片的存儲路徑。
2. 對於攝像,設置了MediaStore.EXTRA_OUTPUT
之後,onActivityResult
中的Intent參數在有的手機上是null,但是在有的手機上不是null,我的手機小米1s得到的intent對象就不是null,所以此處很奇怪。如果intent不是null,可以通過intent.getData()獲取到視頻文件的存儲路徑,但是由於intent是否為null不確定,所以盡量不要通過intent.getData()方法獲取其路徑,而應該自己在Activity中存儲一個字段保存我們之前設置的文件路徑,這樣就沒問題了。
方法一:Android的界面布局可以用兩種方法,一種是在xml中布局,一種是和JAVA中Swing一樣在JAVA代碼中實現Ui界面的布局,用xml的布局管理器布局是很方便
一、Android事件現代的用戶界面,都是以事件來驅動的來實現人機交換的。而Android上的一套UI控件,無非就是派發鼠標和鍵盤事件,然後每個控件收到相應的事件之後,做
吸引用戶的眼球,是我們至死不渝的追求;第一時間呈現最有價值的信息,簡明大方,告訴客戶,你的選擇是多麼的明智,這正是你尋覓已久的東西。分組的應用場合還是很多的,有數據集合的
TabWidget類似於Android 中查看電話薄的界面,通過多個標簽切換顯示不同內容。要實現這一效果,首先要了解TabHost,它是一個用來存放多個Tab標簽的容器。