Android 系統自帶圖片裁剪功能(適配7.0、8.0、對了還有小米手機)
前段時間寫了如何獲取相簿和拍照之後的照片並且進行顯示和上傳,這一次是如何進行圓形影象製作,經常看我寫的筆記的人會知道,我很懶。那麼我就懶的自定義了,目前需求就用原生的就好了,大神的輪子,我會在後面進行推薦。這篇筆記是依賴於:Android呼叫相簿、相機(相容6.0、7.0、8.0)
文章撰寫的,所以不會基本操作的,請先去看看那個。
先上效果哈:
##一、佈局設定
由於是圓形頭像,所以,我們引用一個舉世聞名的專案哈:CircleImageView
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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=".MainActivity"> <Button android:id="@+id/btn_get_pic_form_photo_album" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_margin="10dp" android:text="呼叫相簿" /> <Button android:id="@+id/btn_get_Permission" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_margin="10dp" android:text="動態許可權申請" /> <Button android:id="@+id/btn_get_pic_from_camera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_margin="10dp" android:text="呼叫相機" /> <de.hdodenhof.circleimageview.CircleImageView android:id="@+id/iv_test" android:layout_width="96dp" android:layout_height="96dp" android:layout_centerInParent="true" android:src="@drawable/ic_launcher_background" app:civ_border_color="#999999" app:civ_border_width="2dp" /> </RelativeLayout>
佈局沒有太大變化,就是把之前的ImageView
修改為CircleImageView
控制元件
##二、.java 檔案
這裡增加了兩個方法
######方法一
private void photoClip(Uri uri) { // 呼叫系統中自帶的圖片剪裁 Intent intent = new Intent("com.android.camera.action.CROP"); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.setDataAndType(uri, "image/*"); // 下面這個crop=true是設定在開啟的Intent中設定顯示的VIEW可裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是寬高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪圖片寬高 intent.putExtra("outputX", 150); intent.putExtra("outputY", 150); intent.putExtra("return-data", true); startActivityForResult(intent, 3); }
該方法,傳入我們拿到的照片的 uri 進行啟用 Android 系統的裁剪介面。我是在 onActivityResult 內進行呼叫該方法。
######方法二
public String saveImage(String name, Bitmap bmp) { File appDir = new File(Environment.getExternalStorageDirectory().getPath()); if (!appDir.exists()) { appDir.mkdir(); } String fileName = name + ".jpg"; File file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); bmp.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); return file.getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } return null; }
該方法 傳入獲取照片的 bitmap 和裁剪之後的照片名稱,生成檔案的儲存路徑,韓小呆將其儲存在了本地的根目錄了。
完整程式碼
package com.example.hxd.pictest;
import android.Manifest;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
import de.hdodenhof.circleimageview.CircleImageView;
import pub.devrel.easypermissions.EasyPermissions;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks {
private CircleImageView ivTest;
private File cameraSavePath;//拍照照片路徑
private Uri uri;
private String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
private String photoName = System.currentTimeMillis() + ".jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnGetPicFromCamera = findViewById(R.id.btn_get_pic_from_camera);
Button btnGetPicFromPhotoAlbum = findViewById(R.id.btn_get_pic_form_photo_album);
Button btnGetPermission = findViewById(R.id.btn_get_Permission);
ivTest = findViewById(R.id.iv_test);
btnGetPicFromCamera.setOnClickListener(this);
btnGetPicFromPhotoAlbum.setOnClickListener(this);
btnGetPermission.setOnClickListener(this);
cameraSavePath = new File(Environment.getExternalStorageDirectory().getPath() + "/" + photoName);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn_get_pic_from_camera:
goCamera();
break;
case R.id.btn_get_pic_form_photo_album:
goPhotoAlbum();
break;
case R.id.btn_get_Permission:
getPermission();
break;
}
}
//獲取許可權
private void getPermission() {
if (EasyPermissions.hasPermissions(this, permissions)) {
//已經開啟許可權
Toast.makeText(this, "已經申請相關許可權", Toast.LENGTH_SHORT).show();
} else {
//沒有開啟相關許可權、申請許可權
EasyPermissions.requestPermissions(this, "需要獲取您的相簿、照相使用許可權", 1, permissions);
}
}
private void photoClip(Uri uri) {
// 呼叫系統中自帶的圖片剪裁
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// 下面這個crop=true是設定在開啟的Intent中設定顯示的VIEW可裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是寬高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪圖片寬高
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
intent.putExtra("return-data", true);
startActivityForResult(intent, 3);
}
//啟用相簿操作
private void goPhotoAlbum() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, 2);
}
//啟用相機操作
private void goCamera() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(MainActivity.this, "com.example.hxd.pictest.fileprovider", cameraSavePath);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
uri = Uri.fromFile(cameraSavePath);
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
MainActivity.this.startActivityForResult(intent, 1);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//框架要求必須這麼寫
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
//成功開啟許可權
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "相關許可權獲取成功", Toast.LENGTH_SHORT).show();
}
//使用者未同意權限
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "請同意相關許可權,否則功能無法使用", Toast.LENGTH_SHORT).show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
String photoPath;
if (requestCode == 1 && resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
photoPath = String.valueOf(cameraSavePath);
photoClip(Uri.fromFile(cameraSavePath));
} else {
photoPath = uri.getEncodedPath();
photoClip(uri);
}
Log.d("拍照返回圖片路徑:", photoPath);
Glide.with(MainActivity.this).load(photoPath).into(ivTest);
} else if (requestCode == 2 && resultCode == RESULT_OK) {
photoPath = getPhotoFromPhotoAlbum.getRealPathFromUri(this, data.getData());
Log.d("相簿返回圖片路徑:", photoPath);
photoClip(data.getData());
Glide.with(MainActivity.this).load(photoPath).into(ivTest);
} else if (requestCode == 3 && resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
if (bundle != null) {
//在這裡獲得了剪裁後的Bitmap物件,可以用於上傳
Bitmap image = bundle.getParcelable("data");
//設定到ImageView上
ivTest.setImageBitmap(image);
//也可以進行一些儲存、壓縮等操作後上傳
String path = saveImage("頭像", image);
Log.d("裁剪路徑:", path);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public String saveImage(String name, Bitmap bmp) {
File appDir = new File(Environment.getExternalStorageDirectory().getPath());
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = name + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
當代碼寫成了這樣,我以為就可以完事了,但是後來拿出來我的小米4和小米8se 測試的時候,我就懵逼了,出現瞭如下結果:
描述一下:
1、相簿選擇照片之後,只要進行裁剪就會出現“儲存時發生錯誤,儲存失敗”;
2、就是拍照之後進行裁剪無效果。
#三、解決bug
###1、將 photoClip() 方法內新增如下程式碼,其他程式碼不進行變動
uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + System.currentTimeMillis() + ".jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
這裡的 uritempFile
是個 Uri 。
###2、將裁剪的返回成功的返回方法內新增如下程式碼:
Picasso.with(EditMyInfoActivity.this).load(uritempFile).into(ivHead);
File file = null;
try {
file = new File(new URI(uritempFile.toString()));
} catch (URISyntaxException e) {
e.printStackTrace();
}
//照片路徑
String path = Objects.requireNonNull(file).getPath();
然後,就可以完美的運行了。
##三、推薦一篇自定義裁剪的博文
Android 高仿微信頭像擷取 打造不一樣的自定義控制元件----作者: 家喻戶曉的 張鴻洋
##四、推薦幾款裁剪框架
1、uCrop—薄荷,嗶哩嗶哩 在使用
2、android-crop—網易新聞,芒果TV 在使用
3、PhotoCrop—快手 使用
4、CropBitmap—>出自一位牛x的群友