上一篇文章刚给大家总结完,关于上传头像的功能。此文章所述 主要是关于上传头像的具体流程以及如何对照片做裁剪处理,回调给控件显示;当然重中之重适配了Android高版本,有兴趣的大家可以去看一下:
Android 拍照以及相册中选择(适配高版本)————上传头像并裁剪(一)
本篇将从另一个开发场景,给大家带来关于上传多张照片的需求。
记得之前拿华为的测试机拍出来的一张照片,就要十几M。在图片量过大的情况下,必须要对图片做压缩处理,减少内存损耗,也尽量避免占用内存;每当图片过大,或是多张图片一起上传的时候总会造成oom。
接下来,通过本篇让我们一起来实现,多张图片上传并做压缩处理之后,可以实现在线预览以及伸缩放大等功能。
实测android 8、android 9、android 11、android 13、鸿蒙系统均有效;
手机机型分别为OPPO、华为、VIVO手机、小米、小米平板。
- VIVO android 13
- 华为 鸿蒙系统2.0.1
- 动态申请拍照,读,写权限
- 自定义弹出框
- 调用系统相机拍照
3.1 调用系统相机申请拍照权限回调
3.2 拍照完成回调
3.3 对图片做压缩处理 - 自动获取sdk权限
4.1 访问相册完成回调
4.2 对图片做压缩处理 - 可在线预览照片
(1)动态申请权限
public static String[] permission = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA};
AppUtils.requestPermission(permission);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
//调用系统相机申请拍照权限回调
case CAMERA_PERMISSIONS_REQUEST_CODE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (AppUtils.hasSdcard()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.harry.takepicture.provider", cardImage);
} else {
imageUri = Uri.fromFile(cardImage);
}
PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_CARD_REQUEST);
} else {
ToastUtils.showShort(this, "设备没有SD卡!");
}
} else {
ToastUtils.showShort(this, "请允许打开相机!!");
}
break;
}
}
}
public class AppUtils {
/**
* 申请权限
*
* @param permission
*/
public static void requestPermission(String[] permission) {
BaseActivity.requestRunTimePermission(permission, new PermissionListener() {
@Override
public void onGranted() {
Log.e("tb", "所有权限授权成功");
}
@Override
public void onGranted(List<String> grantedPermission) {
Log.e("tb", "授权成功集合权限---" + grantedPermission);
}
@Override
public void onDenied(List<String> deniedPermission) {
Log.e("tb", "授权失败集合权限---" + deniedPermission);
}
});
}
}
(2)自定义弹出框
本篇主要内容为Camera相关,忽略…
(3.1)调用系统相机拍照
/**
* 拍照
*
* @param
*/
private void takePhoto() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
ToastUtils.showShort(this, "您已经拒绝过一次");
}
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_PERMISSIONS_REQUEST_CODE);
} else {//有权限直接调用系统相机拍照
if (AppUtils.hasSdcard()) {
cardImage = new File(Environment.getExternalStorageDirectory().getPath(), System.currentTimeMillis() + ".jpg");
//通过FileProvider创建一个content类型的Uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(this, "com.harry.takepicture.provider", cardImage);
} else {
imageUri = Uri.fromFile(cardImage);
}
PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_CARD_REQUEST);
} else {
ToastUtils.showShort(this, "设备没有SD卡!");
}
}
}
(3.2)处理拍照回调
/**
* 执行回调
*
* @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) {
fileCropUri = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg");
switch (requestCode) {
//拍照完成回调
case CODE_CAMERA_CARD_REQUEST:
cropImageUri = Uri.fromFile(fileCropUri);
String img = cardImage.getAbsolutePath();
//img就是压缩后的图片
String imgPic = BitmapUtil.compressImageUpload(img);
Log.e("tb", "paths----------" + imgPic);
urlimg.add(0, imgPic);
if (null != imgPic) {
showPic(imgPic);
}
break;
default:
}
}
}
(3.3)对图片做压缩处理
BitmapUtil.compressImageUpload(img);
/**
* @author 拉莫帅
* @date 2021/9/9
* @address
* @Desc BitmapUtil
*/
public class BitmapUtil {
/**
* 质量压缩方法
*
* @param image
* @return
*/
private static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
while (baos.toByteArray().length / 1024 > 100) {
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
return bitmap;
}
/**
* 图片按比例大小压缩方法(根据路径获取图片并压缩)
*
* @param srcPath
* @return
*/
private static Bitmap getImage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
float hh = 800f;// 这里设置高度为800f
float ww = 480f;// 这里设置宽度为480f
// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;// be=1表示不缩放
if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
}
/**
* 将压缩的bitmap保存到SDCard卡临时文件夹,用于上传
*
* @param filename
* @param bit
* @return
*/
private static String saveMyBitmap(String filename, Bitmap bit) {
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/laopai/";
String filePath = baseDir + filename;
File dir = new File(baseDir);
if (!dir.exists()) {
dir.mkdir();
}
File f = new File(filePath);
try {
f.createNewFile();
FileOutputStream fOut = null;
fOut = new FileOutputStream(f);
bit.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return filePath;
}
/**
* 压缩上传路径
*
* @param path
* @return
*/
public static String compressImageUpload(String path) {
String filename = path.substring(path.lastIndexOf("/") + 1);
Bitmap image = getImage(path);
return saveMyBitmap(filename, image);
}
/**
* 清除缓存文件
*/
public static void deleteCacheFile() {
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/laopai/");
RecursionDeleteFile(file);
}
/**
* 递归删除
*/
private static void RecursionDeleteFile(File file) {
if (file.isFile()) {
file.delete();
return;
}
if (file.isDirectory()) {
File[] childFile = file.listFiles();
if (childFile == null || childFile.length == 0) {
file.delete();
return;
}
for (File f : childFile) {
RecursionDeleteFile(f);
}
file.delete();
}
}
}
(4)自动获取SDK权限
/**
* 自动获取sdk权限
*/
private void autoObtainStoragePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSIONS_REQUEST_CODE);
} else {
PhotoUtils.openPic(this, CODE_GALLERY_CARD_REQUEST);
}
}
(4.1)访问相册完成回调
/**
* 执行回调
*
* @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) {
fileCropUri = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg");
switch (requestCode) {
//访问相册完成回调
case CODE_GALLERY_CARD_REQUEST:
if (AppUtils.hasSdcard()) {
cropImageUri = Uri.fromFile(fileCropUri);
String path = GetPathFromUri.getPath(this, data.getData());
//imgPath就是压缩后的图片
String imgPath = BitmapUtil.compressImageUpload(path);
Log.e("tb", "paths----------" + imgPath);
urlimg.add(0, imgPath);
if (null != imgPath) {
showPic(imgPath);
}
} else {
ToastUtils.showShort(this, "设备没有SD卡!");
}
break;
default:
}
}
}
(4.2)对图片做压缩处理 —> (3.3)
BitmapUtil.compressImageUpload(path);
(5)可在线预览照片
// 显示图片
List<String> pic = new ArrayList<>();
/**
* 展示
*
* @param imgPic
*/
private void showPic(String imgPic) {
pic.add(0, imgPic);
picAdapter.notifyDataSetChanged();
}
// 进入预览界面
Intent intent = new Intent(MainActivity.this, ShowImgActivity.class);
intent.putExtra("image_path", pic.get(position));
startActivity(intent);
对于预览图片,单点/多点触摸来进行图片缩放
采用的是开源PhotoView控件
implementation 'com.github.chrisbanes:PhotoView:1.2.6'
此项目个人采用的是jar的方式,依赖方式至上。
/**
* @author 拉莫帅
* @date 2023/4/01
* @address
* @Desc TakePicture 上传多张照片并压缩处理
*/
public class MainActivity extends BaseActivity implements View.OnClickListener {
public static String[] permission = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA};
private RecyclerView recycle;
private Intent intent;
private View view;
private List<String> pic; // 显示图片
private List<String> urlimg; // 上传图片路径
private Uri cropImageUri;
private File cardImage;
private File fileCropUri;//裁剪的照片
private Uri imageUri;//拍照所得到的图像的保存路径
private static final int CAMERA_PERMISSIONS_REQUEST_CODE = 0x03;
private static final int STORAGE_PERMISSIONS_REQUEST_CODE = 0x04;
private static final int CODE_GALLERY_CARD_REQUEST = 0xa5;
private static final int CODE_CAMERA_CARD_REQUEST = 0xa6;
private PicAdapter picAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStatusBg(1);
AppUtils.requestPermission(permission);
initView();
initData();
}
@Override
protected View addContentLayout() {
view = getLayoutInflater().inflate(R.layout.activity_main, contentLayout, false);
return view;
}
private void initView() {
recycle = findViewById(R.id.recycle);
// 初始化数据
urlimg = new ArrayList<>();
pic = new ArrayList<>();
pic.add(this.getResources().getResourceName(R.drawable.add_pic_icon));
}
private void initData() {
// 初始化适配器
picAdapter = new PicAdapter(this, pic);
recycle.setLayoutManager(new GridLayoutManager(this, 3));
recycle.setAdapter(picAdapter);
//添加ItemDecoration,item之间的间隔
int leftRight = AppUtils.dip2px(15, this);
int topBottom = AppUtils.dip2px(15, this);
recycle.addItemDecoration(new SpacesItemDecoration(leftRight, topBottom));
picAdapter.setOnItemClickListener(new PicAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
if (pic.size() > 2 && pic.get(position).equals(MainActivity.this.getResources().getResourceName(R.drawable.add_pic_icon))) {
ToastUtils.showShort(MainActivity.this, "最多只能上传2张图片");
} else {
if (pic.get(position).equals(MainActivity.this.getResources().getResourceName(R.drawable.add_pic_icon))) {
select();
} else {
intent = new Intent(MainActivity.this, ShowImgActivity.class);
intent.putExtra("image_path", pic.get(position));
startActivity(intent);
}
}
}
});
picAdapter.setOnClickDelete(new PicAdapter.OnClickDelete() {
@Override
public void myClickDelete(int pos) {
if (pic.get(pos).equals(MainActivity.this.getResources().getResourceName(R.drawable.add_pic_icon))) {
return;
}
if (pic.size() > 0) {
pic.remove(pos);
picAdapter.notifyDataSetChanged();
}
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.userinfo_iv_head:
select();
break;
case R.id.rl_head_camera:
takePhoto();
AppUtils.dismiss();
break;
case R.id.rl_head_photo:
autoObtainStoragePermission();
AppUtils.dismiss();
break;
case R.id.rl_head_cancel:
AppUtils.dismiss();
break;
}
}
private void select() {
AppUtils.selectPhoto(MainActivity.this, R.layout.dialog_head, R.layout.activity_main, this);
}
/**
* 拍照
*
* @param
*/
private void takePhoto() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
ToastUtils.showShort(this, "您已经拒绝过一次");
}
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_PERMISSIONS_REQUEST_CODE);
} else {//有权限直接调用系统相机拍照
if (AppUtils.hasSdcard()) {
cardImage = new File(Environment.getExternalStorageDirectory().getPath(), System.currentTimeMillis() + ".jpg");
//通过FileProvider创建一个content类型的Uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(this, "com.harry.takepicture.provider", cardImage);
} else {
imageUri = Uri.fromFile(cardImage);
}
PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_CARD_REQUEST);
} else {
ToastUtils.showShort(this, "设备没有SD卡!");
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
//调用系统相机申请拍照权限回调
case CAMERA_PERMISSIONS_REQUEST_CODE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (AppUtils.hasSdcard()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.harry.takepicture.provider", cardImage);
} else {
imageUri = Uri.fromFile(cardImage);
}
PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_CARD_REQUEST);
} else {
ToastUtils.showShort(this, "设备没有SD卡!");
}
} else {
ToastUtils.showShort(this, "请允许打开相机!!");
}
break;
}
}
}
/**
* 自动获取sdk权限
*/
private void autoObtainStoragePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSIONS_REQUEST_CODE);
} else {
PhotoUtils.openPic(this, CODE_GALLERY_CARD_REQUEST);
}
}
/**
* 执行回调
*
* @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) {
fileCropUri = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg");
switch (requestCode) {
//拍照完成回调
case CODE_CAMERA_CARD_REQUEST:
cropImageUri = Uri.fromFile(fileCropUri);
String img = cardImage.getAbsolutePath();
//img就是压缩后的图片
String imgPic = BitmapUtil.compressImageUpload(img);
Log.e("tb", "paths----------" + imgPic);
urlimg.add(0, imgPic);
if (null != imgPic) {
showPic(imgPic);
}
break;
//访问相册完成回调
case CODE_GALLERY_CARD_REQUEST:
if (AppUtils.hasSdcard()) {
cropImageUri = Uri.fromFile(fileCropUri);
String path = GetPathFromUri.getPath(this, data.getData());
//imgPath就是压缩后的图片
String imgPath = BitmapUtil.compressImageUpload(path);
Log.e("tb", "paths----------" + imgPath);
urlimg.add(0, imgPath);
if (null != imgPath) {
showPic(imgPath);
}
} else {
ToastUtils.showShort(this, "设备没有SD卡!");
}
break;
default:
}
}
}
/**
* 展示
*
* @param imgPic
*/
private void showPic(String imgPic) {
pic.add(0, imgPic);
picAdapter.notifyDataSetChanged();
}
}
/**
* @author 拉莫帅
* @date 2023/4/01
* @address
* @Desc PicAdapter适配器
*/
public class PicAdapter extends RecyclerView.Adapter<PicAdapter.MyViewHolder> {
private Context context;
private OnItemClickListener onItemClickListener;
private OnClickDelete onClickDelete;
private List<String> ml;
public PicAdapter(Context ctx, List<String> ml) {
this.context = ctx;
this.ml = ml;
}
public interface OnItemClickListener{
void onItemClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
public interface OnClickDelete{
void myClickDelete(int pos);
}
public void setOnClickDelete(OnClickDelete onClickDelete) {
this.onClickDelete = onClickDelete;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_photo, parent, false));
return holder;
}
@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {
if(ml.get(position).equals(context.getResources().getResourceName(R.drawable.add_pic_icon))){
ImageUtils.ImageIntDefault(context,context.getResources().getDrawable(R.drawable.add_pic_icon),holder.iv_image);
}else{
ImageUtils.ImageDefault(context,ml.get(position),holder.iv_image);
}
if(onItemClickListener!=null){
holder.iv_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.iv_image, position);
}
});
}
if(onClickDelete!=null){
holder.iv_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e("tb","身份证删除-----------------------------------");
int position = holder.getLayoutPosition();
onClickDelete.myClickDelete(position);
}
});
}
}
@Override
public int getItemCount() {
return (ml.size() == 0 || ml == null) ? 0 : ml.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView iv_image,iv_delete;
public MyViewHolder(View itemView) {
super(itemView);
iv_image = itemView.findViewById(R.id.iv_image);
iv_delete = itemView.findViewById(R.id.iv_delete);
}
}
}
item_photo.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp">
<ImageView
android:scaleType="centerCrop"
android:id="@+id/iv_image"
android:src="@drawable/add_pic_icon"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:scaleType="fitXY"
android:layout_gravity="right"
android:id="@+id/iv_delete"
android:layout_width="30dp"
android:layout_height="28dp"
android:src="@drawable/delete_icon" />
</FrameLayout>
/**
* @author 拉莫帅
* @date 2023/4/01
* @address
* @Desc 预览照片
*/
public class ShowImgActivity extends BaseActivity {
private View view;
private TextView title;
private PhotoView iv_show;
private String image_path;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStatusBg(1);
title = findViewById(R.id.title);
title.setText("预览照片");
iv_show = findViewById(R.id.iv_show);
image_path = getIntent().getStringExtra("image_path");
ImageUtils.ImageDefault(this, image_path, iv_show);
}
@Override
protected View addContentLayout() {
view = getLayoutInflater().inflate(R.layout.activity_show_img, contentLayout, false);
return view;
}
}
activity_show_img.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<uk.co.senab.photoview.PhotoView
android:id="@+id/iv_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
关于Camera系列文章到这里就已经结束了!
节后赶紧来给大家更新了,本来五一之前就应该把连载篇,分享给大家。
基本上,关于相机也就这些东西;这里把它分享给大家,欢迎大家共同学习、留言探讨!
最后的最后,大家想要完整版的源代码,欢迎来start !
完整版源码下载地址:Android + <调用相机拍照 & 选择相册> + 数码相机