Android

Java安卓学习总结(二十)

第十六章 使用Intent拍照

有关FileProvider

FileProvider可以实现从其它应用接受一个文件。需要在androidManifest中定义,例如:

<provider
android:authorities="com.example.a73421.criminalintentchallenge.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/files"/>
</provider>

其中android:name是使用的FileProvider的名称,android:authorities是你定义是一个权限,android:exported表示别的应用是否可以使用这个FileProvider,android:grantUriPermissions表示是否可以授权。

meta-data用于关联路径描述资源,是FileProvider使用的文件地址,其中android:name是这个资源的类型,android:resource是要关联的路径描述资源。xml/files定义如下:

<paths>
<files-path
name="crime_photo"
path="."/>
</paths>

有关相机Intent

依旧是创建一个隐式的intent,操作是

MediaStore.ACTION_IMAGE_CAPTURE

之后因为需要将捕获的照片存到对应路径,还要将文件路径转化为相机应用可识别的uri形式,需要用到FileProvider.getUriForFile()。FileProvider.getUriForFile()中的参数有本地路径,还有使用这个路径所需要的权限(之前在androidManifest中定义的),如:

Uri uri= FileProvider.getUriForFile(getActivity(),"com.example.a73421.criminalintentchallenge.fileprovider",mPhotoFile);

将上面的uri传给相机应用的操作是

MediaStore.EXTRA_OUTPUT
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);
}

从相机应用返回后需要再撤销那个权限:

Uri uri=FileProvider.getUriForFile(getActivity(),"com.example.a73421.criminalintentchallenge.fileprovider",mPhotoFile);

getActivity().revokeUriPermission(uri,Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

有关位图的压缩

需要用到BitmapFactory.options:

public class PictureUtils {
public static Bitmap getScaledBitmap(String path, Activity activity)
{
Point size=new Point();
activity.getWindowManager().getDefaultDisplay().getSize(size);

return getScaledBitmap(path,size.x,size.y);
}
public static Bitmap getScaledBitmap(String path,int destWidth,int destHeight)
{
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(path,options);

float srcWidth=options.outWidth;
float srcHeight=options.outHeight;

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;

return BitmapFactory.decodeFile(path,options);
}
}

一些参数说明:

injustDecodeBounds设为true,那么BitmapFactory并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM。设置为false,BitmapFactory返回bitmap。

outWidth&outHeight是bitmap图像的宽和高;

inSampleSize;获取采样率
inSampleSize大于1时,图像高、宽分别以2的inSampleSize次方分之一缩小
inSampleSize小于等于1时,图像高、宽不变;

所以getScaledBitmap方法干了这么一件事:先把图像的宽和高取回来,分别除一下应用显示区域的宽和高,得到压缩比例,再按照缩放比例新建一个bitmap返回。

有关挑战练习 优化照片显示

做法就和TimePicker过程一样,需要新建一个DialogFragment和相关的.xml文件。

给显示缩略图的mPhotoView增加有关监听器,被按下时创建一个dialog:

mPhotoView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(getActivity(),mPhotoFile.toString(),Toast.LENGTH_SHORT).show();
        FragmentManager manager=getFragmentManager();
        ShowPictureFragment dialog=ShowPictureFragment.newInstance(mPhotoFile);
        dialog.show(manager,DIALOG_SHOW_PICTURE);
    }
});

其中创建ShowPictureFragment时传入的是要显示的图片的位置。在ShowPictureFragment的onCreateDialog方法中要获取这个图片的位置将其转化为ImageVIew可识别的uri,并且绑定到ImageView中:

public Dialog onCreateDialog(Bundle savedInstanceState) {
    View v= LayoutInflater.from(getActivity()).inflate(R.layout.dialog_picture_show,null);

    File file=(File)getArguments().getSerializable(ARG_PICTURE);
    Uri uri = Uri.fromFile(file);

    mPictureImageView=(ImageView)v.findViewById(R.id.show_picture);
    mPictureImageView.setImageURI(uri);
    
return new AlertDialog.Builder(getActivity()).
            setView(v).
            setPositiveButton(android.R.string.ok,null).
            setTitle(R.string.show_picture).
            create();
}

ImageView有四种指定图片的方法,这里我用的是uri,查看其它用法

有关挑战练习 优化缩略图加载

这个题目我原以为是优化缩略图的生成效率,实际上是优化缩略图的加载时机,即只有当其布局文件发生变化之后再进行加载。要用到的ViewTreeObserver就可以监听布局的传递。

ViewTreeObserver observer = mPhotoView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mPhotoView.setEnabled(mPhotoFile.exists());
updatePhotoView();
}
});

同样的也可以在里面设置mPhotoView是否可用,当路径不存在照片时就禁用mPhotoView。

ViewTreeObserver的六个接口类的参考

发表评论