背景
从手机图库选择一张图片并裁剪是我原来自己实现过的功能,但是当我在我自己的开发的个人项目再次实现这个功能的时候,发现该功能失效了。通过看日志分析,发现原因是:当Android版本高的时候,有些方法不适用或者是有些权限需要动态申请了。所以现在重新在自己的小米手机上(Android 7.0)实现该功能,下面来一起看一下我的实现过程。
添加依赖
项目中我用到了一些第三方的库,方便代码的实现:分别是ButterKnife和Glide3.7。
ButterKnife是一个Android系统的View注入框架;Glide是图片加载框架,这个图片库非常的强大,这里不做过多的说明,这里给大家推荐一下郭霖的博客,里面有非常详细的使用方法和Glide源码分析。
- 添加butterKnife依赖:
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
- 添加Glide依赖:
implementation 'com.github.bumptech.glide:glide:3.7.0'
在app下的gradle里添加完上面两个依赖后,再次同步gradle,等待工程自己重新构建成功就可以使用上面两个框架了。
清单配置文件
这里需要用到存储权限,所以需要在清单配置文件中申明该组权限,同时需要开发者自己动态申请,具体申请权限的代码,稍后会在项目中详细给出,这里大家先知道有这么一个配置:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.gallerydemo">
<!--存储权限, 需要开发者动态申请该权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
demo的界面xml布局
demo的显示界面是一个简单的按钮和ImageView控件,通过点击按钮,实现从图库选择图片并裁剪,然后ImageView显示该图片。xml如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:text="图库"
android:id="@+id/btn_gallery"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher_round"
android:layout_gravity="center"
android:id="@+id/iv_pic"
android:layout_width="300dp"
android:layout_height="300dp" />
</LinearLayout>
预览图如下所示:
代码实现
我的实现思路:首先动态申请存储权限,然后点击按钮,开始选择图库的图片,裁剪完成后,设置显示图片即可。
package com.example.administrator.gallerydemo;
import android.Manifest;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import java.io.File;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_GALLERY = 0x10;// 图库选取图片标识请求码
private static final int CROP_PHOTO = 0x12;// 裁剪图片标识请求码
private static final int STORAGE_PERMISSION = 0x20;// 动态申请存储权限标识
@BindView(R.id.iv_pic)ImageView iv_pic;// imageView控件
private File imageFile = null;// 声明File对象
private Uri imageUri = null;// 裁剪后的图片uri
private String path = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);// 控件绑定
// 动态申请存储权限,后面读取文件有用
requestStoragePermission();
}
/**
* 单击事件绑定
*/
@OnClick({R.id.btn_gallery})
public void doClick(View view){
switch (view.getId()){
case R.id.btn_gallery:// 图库选择
gallery();
break;
}
}
/**
* 图库选择图片
*/
private void gallery() {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// 以startActivityForResult的方式启动一个activity用来获取返回的结果
startActivityForResult(intent, REQUEST_CODE_GALLERY);
}
/**
* 接收#startActivityForResult(Intent, int)调用的结果
* @param requestCode 请求码 识别这个结果来自谁
* @param resultCode 结果码
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK){// 操作成功了
switch (requestCode){
case REQUEST_CODE_GALLERY:// 图库选择图片
Uri uri = data.getData();// 获取图片的uri
Intent intent_gallery_crop = new Intent("com.android.camera.action.CROP");
intent_gallery_crop.setDataAndType(uri, "image/*");
// 设置裁剪
intent_gallery_crop.putExtra("crop", "true");
intent_gallery_crop.putExtra("scale", true);
// aspectX aspectY 是宽高的比例
intent_gallery_crop.putExtra("aspectX", 1);
intent_gallery_crop.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent_gallery_crop.putExtra("outputX", 400);
intent_gallery_crop.putExtra("outputY", 400);
intent_gallery_crop.putExtra("return-data", false);
// 创建文件保存裁剪的图片
createImageFile();
imageUri = Uri.fromFile(imageFile);
if (imageUri != null){
intent_gallery_crop.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
intent_gallery_crop.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
}
startActivityForResult(intent_gallery_crop, CROP_PHOTO);
break;
case CROP_PHOTO:// 裁剪图片
try{
if (imageUri != null){
displayImage(imageUri);
}
}catch (Exception e){
e.printStackTrace();
}
break;
}
}
}
/**
* Android6.0后需要动态申请危险权限
* 动态申请存储权限
*/
private void requestStoragePermission() {
int hasCameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
Log.e("TAG","开始" + hasCameraPermission);
if (hasCameraPermission == PackageManager.PERMISSION_GRANTED){
// 拥有权限,可以执行涉及到存储权限的操作
Log.e("TAG", "你已经授权了该组权限");
}else {
// 没有权限,向用户申请该权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.e("TAG", "向用户申请该组权限");
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION);
}
}
}
/**
* 动态申请权限的结果回调
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == STORAGE_PERMISSION){
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
// 用户同意,执行相应操作
Log.e("TAG","用户已经同意了存储权限");
}else {
// 用户不同意,向用户展示该权限作用
}
}
}
/**
* 创建File保存图片
*/
private void createImageFile() {
try{
if (imageFile != null && imageFile.exists()){
imageFile.delete();
}
// 新建文件
imageFile = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + "galleryDemo.jpg");
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 显示图片
* @param imageUri 图片的uri
*/
private void displayImage(Uri imageUri) {
try{
// glide根据图片的uri加载图片
Glide.with(this)
.load(imageUri)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.placeholder(R.mipmap.ic_launcher_round)// 占位图设置:加载过程中显示的图片
.error(R.mipmap.ic_launcher_round)// 异常占位图
.transform(new CenterCrop(this))
.into(iv_pic);
}catch (Exception e){
e.printStackTrace();
}
}
}
上面的代码中注释很详细并给出了实现的流程,所以大家根据思路自己去看,这里不做过多的介绍,代码就是最好的老师。
效果图示
下面给出在我的手机上的运行效果图示,如下所示:
上面的效果图示很好的说明结果。
哈哈!结果当然是成功了啦!
好了,就到这了,下次见!
由于最近在用spring boot开发后台接口,所以以后更新的内容会更加的多元化,敬请期待!
A little bit of progress every day!Come on!