安卓开发:从系统相册选择照片

来源:互联网 发布:中铁四局网络采购平台 编辑:程序博客网 时间:2024/06/05 19:16

概述

与调用系统摄像头拍照类似的,从系统相册选择照片的核心代码也仅仅只是一句呼唤系统Intent:
Intent intent = new Intent("android.intent.action.GET_CONTENT");

不过我们同样也要围绕这句核心代码做很多准备工作。

第一步:获取权限

从系统相册获取照片,需要对存储器的读写操作权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

注意:

比较悲剧的是,这两个权限在Android6.x之后也被划分为高危系统权限,因此仅仅只是在AndroidManifest.xml文件中声明一下是没有用的,还需要转本编写代码进行动态运行时权限申请。
详细信息可以参考我的这篇文章:
http://blog.csdn.net/freezingxu/article/details/71409860

第二步:绘制或编写Activity界面

在我的应用场景中,我的界面是这个样子的:

我在界面上用TableLayout做了一个容器,每当从相册选择一张照片后,这张照片就会被放置在这个TableLayout中。如果选择了多张照片,那么就会像上图那样形成一个列表。
这样的界面结构其实非常简单,不过我还是把代码粘帖在这里:
<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.yumi.mibao.easyasset.activities.TakePhotoActivity"    android:background="@color/metro_white">    <Button        android:id="@+id/BUTTON_COMPLETE"        android:layout_width="0dp"        android:layout_height="48dp"        android:background="@color/yumi_red"        android:textColor="@color/metro_white"        android:text="@string/str_complete"        android:layout_marginRight="8dp"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintBottom_toBottomOf="parent"        android:layout_marginBottom="8dp"        app:layout_constraintLeft_toRightOf="@+id/BUTTON_TAKE_PHOTO"        android:layout_marginLeft="8dp"        android:layout_marginStart="8dp"        android:layout_marginEnd="8dp" />    <Button        android:id="@+id/BUTTON_TAKE_PHOTO"        android:layout_width="68dp"        android:layout_height="48dp"        android:layout_marginBottom="8dp"        android:layout_marginLeft="8dp"        android:background="@color/metro_grass"        android:text="@string/str_take_photo"        android:textColor="@color/metro_white"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toRightOf="@+id/BUTTON_ALBUM"        android:layout_marginStart="8dp"        android:onClick="takePohto"/>    <Button        android:id="@+id/BUTTON_ALBUM"        android:layout_width="68dp"        android:layout_height="48dp"        android:layout_marginBottom="8dp"        android:layout_marginLeft="8dp"        android:background="@color/metro_blue"        android:text="@string/str_album"        android:textColor="@color/metro_white"        app:layout_constraintBottom_toBottomOf="parent"        android:layout_marginStart="8dp"        android:onClick="pickPhotoFromAlbum"        app:layout_constraintLeft_toLeftOf="parent" />    <ScrollView        android:layout_width="0dp"        android:layout_height="495dp"        android:layout_marginRight="8dp"        app:layout_constraintRight_toRightOf="parent"        android:layout_marginLeft="8dp"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintTop_toTopOf="parent"        android:layout_marginTop="8dp"        app:layout_constraintHorizontal_bias="0.0"        android:layout_marginBottom="8dp"        app:layout_constraintBottom_toTopOf="@+id/BUTTON_COMPLETE">        <TableLayout            android:id="@+id/TABLE_LAYOUT_TAKE_PHOTO_LIST"            android:layout_width="match_parent"            android:layout_height="fill_parent"            android:layout_gravity="top"            android:layout_marginTop="8dp"            android:gravity="top" />    </ScrollView></android.support.constraint.ConstraintLayout>

在上面的代码中,可以看到id为“BUTTON_ALBUM”的按钮就对应了图片中的“相册”按钮,在这个按钮的onclick事件中,我触发了一个方法“pickPhotoFromAlbum”,就是这个方法会调起手机的拍照功能。具体实现往下看。

第三步:编写事件触发方法

3.1定义用于Intent回调的请求码

由于呼出系统拍照功能的Intent是采用startActivityForResult的模式来进行,所以需要明确的请求码来标记操作的类型,这样才能够在Intent的回调方法中进行相应的数据处理:
private final int PICK_FROM_ALBUN = 3;//从相册选择
实际上,我们还会用到一个裁剪图片的请求码,定义如下:
private final int CROP_PHOTO = 2;//切图操作
在后续的步骤中,我们会用到这个请求码

3.2定义被选中的照片的保存路径和文件名

/* *  从相册选择所得到的图像的保存路径 */private Uri imageUri;/* *  从相册选择的照片的文件名 */private String fileName;

3.3编写方法,呼出系统Intent,从相册选择照片

先来看一下选择相册的界面是什么样子的:


/** * 点击按钮从手机相册中获取图片 * @param view */public void pickPhotoFromAlbum(View view){    /*     *  用时间戳的方式来命名图片文件,这样可以避免文件名称重复     */    SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");    Date date = new Date(System.currentTimeMillis());    this.fileName = "easyassetFromAlbum"+format.format(date);    /*     *  创建一个File对象,用于存放选到的照片     */    File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);    File outputImage = new File(path,this.fileName+".jpg");    /*     *  以防万一,看一下这个文件是不是存在,如果存在的话,先删除掉     */    if(outputImage.exists()){        outputImage.delete();    }    try {        outputImage.createNewFile();    } catch (IOException e) {        e.printStackTrace();    }    /*     *  将File对象转换为Uri对象,以便拍照后保存     */    this.imageUri = Uri.fromFile(outputImage);    /*     *  启动系统的选择界面     */    Intent intent = new Intent("android.intent.action.GET_CONTENT");    intent.putExtra("scale", true);//设置可以缩放    intent.putExtra("crop", true);//设置可以裁剪    intent.setType("image/*");//设置需要从系统选择的内容:图片    intent.putExtra(MediaStore.EXTRA_OUTPUT, this.imageUri);//设置输出位置    startActivityForResult(intent, this.PICK_FROM_ALBUN);//开始选择}
可以看到,在代码的最后,采用startActivityForResult的形式呼出了系统Intent,并且将请求码“PICK_FROM_ALBUM”作为参数发送了过去。因此,我们必须编写onActivityResult方法,来处理回调业务。

3.4编写Intent回调方法

/** * 因为启动系统Intent使用的forResult模式,因此需要onActivityResult方法来接受回调参数 * @param requestCode * @param resultCode * @param data */@Overrideprotected void onActivityResult(int requestCode,int resultCode,Intent data){    super.onActivityResult(requestCode,resultCode,data);    if (resultCode != RESULT_OK) {        Toast.makeText(this, "获取图片出现错误", Toast.LENGTH_SHORT).show();    }    else{        switch(requestCode) {                        /*             *  case PICK_FROM_ALBUM 代表从选择相册的intent返回之后             *  完成从相册中选择照片后,就要将图片生成bitmap对象,然后显示在界面上了             */            case PICK_FROM_ALBUN:                this.cropPhoto(data.getData());                break;             default:                break;        }    }}
可以看到,在switch中我们捕获了请求码“PICK_FROM_ALBUM”,然后立即调用了 一个方法“cropPhoto”,实际上这是一个用来裁剪图片的方法。

3.5编写图片裁剪方法

/** * 打开裁剪图片的系统界面 */private void cropPhoto(Uri imageUri){    /*     *  准备打开系统自带的裁剪图片的intent     */    Intent intent = new Intent("com.android.camera.action.CROP"); //打开系统自带的裁剪图片的intent    intent.setDataAndType(imageUri, "image/*");    intent.putExtra("scale", true);    /*     *  设置裁剪区域的宽高比例     */    intent.putExtra("aspectX", 1);    intent.putExtra("aspectY", 1);    /*     *  设置裁剪区域的宽度和高度     */    intent.putExtra("outputX", 340);    intent.putExtra("outputY", 340);    /*     *  指定裁剪完成以后的图片所保存的位置     */    intent.putExtra(MediaStore.EXTRA_OUTPUT, this.imageUri);    Toast.makeText(this, "剪裁图片", Toast.LENGTH_SHORT).show();    /*     *  以广播方式刷新系统相册,以便能够在相册中找到刚刚所拍摄和裁剪的照片     */    Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);    intentBc.setData(this.imageUri);    this.sendBroadcast(intentBc);    /*     *  以forResult模式启动系统自带的裁剪图片的intent     */    startActivityForResult(intent, CROP_PHOTO); //设置裁剪参数显示图片至ImageView}
可以看到,在上述方法的最后,我们启动了系统自带图片裁剪Intent,并且将请求码“CROP_PHOTO”以startActivityForResult的形式传递过去。
手机打开的且图界面是这样的:


3.6在Intent的回调方法中,增加对切图回调的处理

/* *  case CROP_PHOTO 代表从裁剪照片的intent返回之后 *  完成裁剪照片后,就要将图片生成bitmap对象,然后显示在界面上面了 */case CROP_PHOTO:    try {        /*         *  将图片转换成Bitmap对象         */        Bitmap bitmap = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(this.imageUri));        Toast.makeText(this, this.imageUri.toString(), Toast.LENGTH_SHORT).show();        /*         *  在界面上显示图片         */        this.addPhotoToActivity(bitmap);    } catch(FileNotFoundException e) {        e.printStackTrace();    }    break;
所以,整个回调方法看起来是这样的:
@Overrideprotected void onActivityResult(int requestCode,int resultCode,Intent data){    super.onActivityResult(requestCode,resultCode,data);    if (resultCode != RESULT_OK) {        Toast.makeText(this, "获取图片出现错误", Toast.LENGTH_SHORT).show();    }    else{        switch(requestCode) {            /*             *  case CROP_PHOTO 代表从裁剪照片的intent返回之后             *  完成裁剪照片后,就要将图片生成bitmap对象,然后显示在界面上面了             */            case CROP_PHOTO:                try {                    /*                     *  将图片转换成Bitmap对象                     */                    Bitmap bitmap = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(this.imageUri));                    Toast.makeText(this, this.imageUri.toString(), Toast.LENGTH_SHORT).show();                    /*                     *  在界面上显示图片                     */                    this.addPhotoToActivity(bitmap);                } catch(FileNotFoundException e) {                    e.printStackTrace();                }                break;            /*             *  case PICK_FROM_ALBUM 代表从选择相册的intent返回之后             *  完成从相册中选择照片后,就要将图片生成bitmap对象,然后显示在界面上了             */            case PICK_FROM_ALBUN:                this.cropPhoto(data.getData());                break;            default:                break;        }    }}
可以看到,在完成切图后,直接调用了一个方法“addPhotoToActivity”,来将选择的照片显示在界面上。

3.7显示图片

/** * 将拍照和裁剪后所得到的照片,罗列在界面上 */private  void addPhotoToActivity(Bitmap bitMap){    /*     *  首先获取到用来显示照片的容器     *  该容易是一个TableLayout     */    TableLayout tableLayout = (TableLayout)this.findViewById(R.id.TABLE_LAYOUT_TAKE_PHOTO_LIST);    /*     *  创建一个TableRow对象     *  每一行TableRow对象都用来存放一张照片,以及该照片的上传情况信息     *  将这个TableRow放入TableLayout中     */    TableRow tableRow = new TableRow(this);    tableRow.setPadding(0,0,0,8);//设置每一行的下间距    tableLayout.addView(tableRow);    /*     *  创建一个ImageView对象     *  将这个对象放入TableRow中     *  并在这个对象上显示刚刚拍照所得到的照片     */    ImageView imageView = new ImageView(this);    imageView.setLayoutParams(this.photoParams);    imageView.setImageBitmap(bitMap);    tableRow.addView(imageView);    /*     *  创建一个TextView对象     *  为这个对象设置一段“图片正在上传”的提示文字     *  并将这个TextView对象放入TableRow中     */    TextView textView = new TextView(this);    textView.setLayoutParams(this.uploadStateMsgParam);    textView.setGravity(Gravity.CENTER_VERTICAL);    textView.setText("正在上传照片...");    textView.setTextColor(ContextCompat.getColor(this,R.color.metro_blue));    tableRow.addView(textView);    }
在上述代码中,会用到两个界面组件的样式定义,如下:
/* *  一组界面样式,分别是照片在TableRow中所占的宽度比重和照片上传状态的文字信息在TableRow中所占的宽度比重 */private TableRow.LayoutParams photoParams;private TableRow.LayoutParams uploadStateMsgParam;
/** * 初始化一些界面组件的样式 */private void initLayoutParams(){    /*     *  拍照所得到的图片被放置在界面上时,其在TableRow所占的宽度占比     */    this.photoParams = new TableRow.LayoutParams(        TableRow.LayoutParams.WRAP_CONTENT,        268,        0.1f    );    /*     *  照片上传状态的文字信息被放置在界面上时,其在TableRow所占的宽度占比     */    this.uploadStateMsgParam = new TableRow.LayoutParams(        TableRow.LayoutParams.WRAP_CONTENT,        268,        0.9f    );}
@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_easy_asset_take_photo);     /*     *  调用方法,初始化界面组件的样式     */    this.initLayoutParams();}

至此所有的逻辑就全部实现了。




原创粉丝点击