在上一篇学习安卓开发[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"表示不强制拍照功能,由于若是设备没有相机,会禁掉拍照按钮。