______ Introduction ______前言
上一篇文章刚给大家总结完,关于上传头像的功能。此文章所述 主要是关于上传头像的具体流程以及如何对照片做裁剪处理,回调给控件显示;当然重中之重适配了Android高版本,有兴趣的大家可以去看一下:
Android 拍照以及相册中选择(适配高版本)————上传头像并裁剪(一)
本篇将从另一个开发场景,给大家带来关于上传多张照片的需求。
记得之前拿华为的测试机拍出来的一张照片,就要十几M。在图片量过大的情况下,必须要对图片做压缩处理,减少内存损耗,也尽量避免占用内存;每当图片过大,或是多张图片一起上传的时候总会造成oom。
接下来,通过本篇让我们一起来实现,多张图片上传并做压缩处理之后,可以实现在线预览以及伸缩放大等功能。
______ Animation Effect ______效果
VIVO android 13 华为 鸿蒙系统2.0.1______ Function ______功能实测android 8、android 9、android 11、android 13、鸿蒙系统均有效;
手机机型分别为OPPO、华为、VIVO手机、小米、小米平板。
动态申请拍照,读,写权限自定义弹出框调用系统相机拍照
3.1 调用系统相机申请拍照权限回调
3.2 拍照完成回调
3.3 对图片做压缩处理自动获取sdk权限
4.1 访问相册完成回调
4.2 对图片做压缩处理可在线预览照片______ Concrete Realization ______具体实现
(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);@Overridepublic 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() {@Overridepublic void onGranted() {Log.e("tb", "所有权限授权成功");}@Overridepublic void onGranted(List<String> grantedPermission) {Log.e("tb", "授权成功集合权限---" + grantedPermission);}@Overridepublic 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类型的Uriif (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*/@Overrideprotected 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 = pressImageUpload(img);Log.e("tb", "paths----------" + imgPic);urlimg.add(0, imgPic);if (null != imgPic) {showPic(imgPic);}break;default:}}}
(3.3)对图片做压缩处理
pressImageUpload(img);
/*** @author 拉莫帅* @date /9/9* @address* @Desc BitmapUtil*/public class BitmapUtil {/*** 质量压缩方法** @param image* @return*/private static Bitmap compressImage(Bitmap image) {ByteArrayOutputStream baos = new ByteArrayOutputStream();press(pressFormat.JPEG, 100, baos);int options = 100;while (baos.toByteArray().length / 1024 > 100) {baos.reset();press(pressFormat.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;// 这里设置高度为800ffloat 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);press(pressFormat.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*/@Overrideprotected 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 = pressImageUpload(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)
pressImageUpload(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的方式,依赖方式至上。
______ Full Implementation ______完整实现
/*** @author 拉莫帅* @date /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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setStatusBg(1);AppUtils.requestPermission(permission);initView();initData();}@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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();}}});}@Overridepublic 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类型的Uriif (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卡!");}}}@Overridepublic 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*/@Overrideprotected 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 = pressImageUpload(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 = pressImageUpload(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 /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@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_photo, parent, false));return holder;}@Overridepublic 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() {@Overridepublic void onClick(View view) {int position = holder.getLayoutPosition();onItemClickListener.onItemClick(holder.iv_image, position);}});}if(onClickDelete!=null){holder.iv_delete.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Log.e("tb","身份证删除-----------------------------------");int position = holder.getLayoutPosition();onClickDelete.myClickDelete(position);}});}}@Overridepublic 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="/apk/res/android"android:layout_width="match_parent"android:layout_height="100dp"><ImageViewandroid:scaleType="centerCrop"android:id="@+id/iv_image"android:src="@drawable/add_pic_icon"android:layout_width="match_parent"android:layout_height="match_parent" /><ImageViewandroid: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 /4/01* @address* @Desc 预览照片*/public class ShowImgActivity extends BaseActivity {private View view;private TextView title;private PhotoView iv_show;private String image_path;@Overrideprotected 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);}@Overrideprotected 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="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"><uk.co.senab.photoview.PhotoViewandroid:id="@+id/iv_show"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true" /></RelativeLayout>
______ Ending ______结尾
关于Camera系列文章到这里就已经结束了!
节后赶紧来给大家更新了,本来五一之前就应该把连载篇,分享给大家。
基本上,关于相机也就这些东西;这里把它分享给大家,欢迎大家共同学习、留言探讨!
最后的最后,大家想要完整版的源代码,欢迎来start !
完整版源码下载地址:Android + <调用相机拍照 & 选择相册> + 数码相机
Android 拍照以及相册中选择(适配高版本)————上传多张照片之压缩处理 在线预览可伸缩放大(二)