追加一句話:
本文章已授權秦子帥微信公眾號轉載:Android 系統自帶圖片裁剪功能(適配7.0、8.0、小米)
前段時間寫了如何獲取相冊和拍照之后的照片并且進行顯示和上傳,這一次是如何進行圓形圖像制作,經??次覍懙墓P記的人會知道,我很懶。那么我就懶的自定義了,目前需求就用原生的就好了,大神的輪子,我會在后面進行推薦。這篇筆記是依賴于: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的大佬搞出來的