在上一篇學習安卓開發[3] - 使用RecyclerView顯示列表中瞭解了在進行列表展現時RecyclerView的使用,本次記錄的是在應用中如何經過隱式Intent調用其它應用的功能,好比發短信、打電話、拍照等html
Intent對象用來向操做系統說明須要處理的任務。使用顯式Intent時,要指定操做系統須要啓動的activity,但使用隱式intent,只需告知操做系統想要進行的操做,系統就會啓動能完成該操做的activity,若是有多個符合條件的activity,會提供用戶一個應用列表供選擇 Android是如何經過隱式intent找到並啓動合適應用的呢?緣由在於配置文件中的itent過濾器設置,好比咱們也想開發一款短信應用,那麼能夠在AndroidMainfest的activity聲明中這樣設置:android
<activity android:name=".CrimeListActivity"> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
隱式Intent的組成部分有 1)要執行的操做,一般以Intent類中的常量來表示,好比訪問URL可使用Intent.ACTION_VIEW,發送郵件使用Intent.ACTION_SEND 2)待訪問數據的位置,這多是設備之外的資源,如某個網頁的URL,某個文件的URI 3)操做涉及的數據類型,如text/html, audio/mpeg3等 4)可選類別,用來描述對activity的使用方式安全
那麼要啓動短信的隱式intent的方法爲:ide
mReportButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent i = new Intent(Intent.ACTION_SEND); i.setType("text/plain"); i.putExtra(Intent.EXTRA_TEXT, getCrimeReport()); i.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.crime_report_suspect)); i = Intent.createChooser(i, getString(R.string.send_report)); startActivity(i); } });
首先指定發送消息的操做名爲ACTION_SEND,而後消息內容爲文本,因此設置數據類型爲text/plain,要發送的文本經過Extra的形式提供佈局
使用隱式intent時,若是系統沒有安裝對應的軟件,應用就會奔潰,因此有必要在使用隱式intent時,檢查一下可以找到對應的軟件,若是沒找到,就避免再去發生相關的隱式intent學習
final Intent pickContact = new Intent(Intent.ACTION_SEND); PackageManager packageManager = getActivity().getPackageManager(); if (packageManager.resolveActivity(pickContact, PackageManager.MATCH_DEFAULT_ONLY) == null) { mReportButton.setEnabled(false); }
經過PackageManager能夠搜索須要的activity的信息,flag標誌MATCH_DEFAULT_ONLY限定只搜索帶CATEGORY_DEFAULT的activity,若是沒有找到,就禁用發短信按鈕。ui
若是所開發的APP有拍照功能,就可使用系統相機了。拍攝的照片要保存在設備文件系統,但這就涉及到私有存儲空間的問題。出於安全考慮,沒法使用公共外部存儲轉存,那麼若是想共享文件給其餘應用,或者接收其餘應用的文件(如相機拍攝的照片),可使用ContentProvider把要共享的文件臨時暴露出來。對於接受相機拍攝的照片這樣的場景,系統提供的現成的FileProvider類。操作系統
要使用FileProvider類,須要在AndroidMainfest中添加聲明。 首先添加files.xml文件code
<paths> <files-path name="crime_photos" path="."/> </paths>
這個描述性文件把私有存儲空間的根路徑映射爲crime_photos,這個名字僅供FileProvider本身使用。 而後添加FileProvider聲明:xml
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.zhixin.crimeintent.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/files" /> </provider>
經過這段聲明,提供了一個文件保存地,相機拍攝的照片就能夠放在這裏了。exported="false"表示除了應用本身和給予受權的應用,其它的不容許使用這個FileProvider,grantUriPermissions="true"表示容許其餘應用向指定文職的URI寫入文件。
接下來就能夠實現拍照功能了
mPhotoButton = (ImageButton) v.findViewById(R.id.crime_camera); final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); boolean canTakePhoto = mPhotoFile != null && captureImage.resolveActivity(packageManager) != null; mPhotoButton.setEnabled(canTakePhoto); mPhotoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Uri uri = FileProvider.getUriForFile(getActivity(), "com.example.zhixin.crimeintent.fileprovider", mPhotoFile); captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri); List<ResolveInfo> cameraActivities = getActivity().getPackageManager().queryIntentActivities(captureImage, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo activity : cameraActivities) { getActivity().grantUriPermission(activity.activityInfo.packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } startActivityForResult(captureImage,REQUEST_PHOTO); } }); mPhotoView = (ImageView) v.findViewById(R.id.crime_photo);
經過給全部目標activity授予Intent.FLAG_GRANT_WRITE_URI_PERMISSION權限,容許它們在URI指定的位置寫入文件。mPhotoFile表示拍攝生成照片的名稱。
在相機拍攝完成後的回調方法中,取消以前的Intent.FLAG_GRANT_WRITE_URI_PERMISSION受權,並加載顯示照片。
Uri uri=FileProvider.getUriForFile(getActivity(), "com.example.zhixin.crimeintent.fileprovider", mPhotoFile); getActivity().revokeUriPermission(uri,Intent.FLAG_GRANT_WRITE_URI_PERMISSION); updatePhotoView();
在顯示照片時還有一些工做要作。顯示照片要用到Bitmap,而Bitmap只存儲實際像素數據,即便是已經壓縮過的照片,存入Bitmap後,文件並不會一樣壓縮,好比一張1600萬像素24位的相機照片存爲JPG格式約爲5MB,但載入Bitmap後就會達到48MB左右。 要解決這個問題,須要手動縮放位圖照片。首先確認文件大小,而後根據要顯示照片的區域大小合理縮放文件,最後從新讀取縮放後的文件,再建立Bitmap對象。
public class PictureUtils { public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) { // Read in the dimensions of the image on disk BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); float srcWidth = options.outWidth; float srcHeight = options.outHeight; // Figure out how much to scale down by int inSampleSize = 1; if (srcHeight > destHeight || srcWidth > destWidth) { float heightScale = srcHeight / destHeight; float widthScale = srcWidth / destWidth; inSampleSize = Math.round(heightScale > widthScale ? heightScale : widthScale); } options = new BitmapFactory.Options(); options.inSampleSize = inSampleSize; // Read in and create final bitmap return BitmapFactory.decodeFile(path, options); } }
還有一個問題是在Fragment.OnCreateView裏面加載照片的時候,沒法知道要顯示照片的尺寸,只有onCreate, onStart, onResume方法執行事後,纔會有首個實例化佈局出現。對於這種狀況,能夠根據Fragment所在的Activity尺寸肯定屏幕的尺寸,按照屏幕尺寸縮放圖像。因此再添加一個getScaledBitmap的重載:
public static Bitmap getScaledBitmap(String path, Activity activity) { Point size = new Point(); activity.getWindowManager().getDefaultDisplay() .getSize(size); return getScaledBitmap(path, size.x, size.y); }
最後在OnCreateView和相機的回調方法更新照片。
既然APP須要用到拍照功能,但像拍照、NFC、紅外等並非每一個設備都有,因此進行功能聲明,從而能夠在應用前讓用戶知道,若是設備缺乏某項必須功能,應用商店會拒絕安裝應用。 在AndroidMainfest中添加:
<uses-feature android:name="android.hardware.camera" android:required="false"> </uses-feature>
android:required="false"表示不強制拍照功能,由於若是設備沒有相機,會禁掉拍照按鈕。