一款针对Android平台下的图片选择器,支持从相册获取图片、视频、音频&拍照,支持裁剪(单图or多图裁剪)、压缩、主题自定义配置等功能,支持动态获取权限&适配Android 5.0+系统的开源图片选择框架。
废话不多。开干!!
添加依赖:
//图片选择器 api 'io.github.lucksiege:pictureselector:v3.11.1' //图片压缩 api 'io.github.lucksiege:compress:v3.11.1' //图片裁剪 api 'io.github.lucksiege:ucrop:v3.11.1' //自定义相机 api 'io.github.lucksiege:camerax:v3.11.1'
实现工具类如下 PictureUtils :
import android.Manifest;import android.content.Context;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.drawable.Drawable;import android.net.Uri;import android.provider.MediaStore;import android.widget.ImageView;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import androidx.fragment.app.Fragment;import com.bumptech.glide.Glide;import com.bumptech.glide.request.target.CustomTarget;import com.bumptech.glide.request.transition.Transition;import com.luck.lib.camerax.SimpleCameraX;import com.luck.picture.lib.basic.PictureSelector;import com.luck.picture.lib.config.PictureMimeType;import com.luck.picture.lib.config.SelectMimeType;import com.luck.picture.lib.engine.CompressFileEngine;import com.luck.picture.lib.engine.CropFileEngine;import com.luck.picture.lib.engine.VideoPlayerEngine;import com.luck.picture.lib.entity.LocalMedia;import com.luck.picture.lib.entity.MediaExtraInfo;import com.luck.picture.lib.interfaces.OnCameraInterceptListener;import com.luck.picture.lib.interfaces.OnExternalPreviewEventListener;import com.luck.picture.lib.interfaces.OnRecordAudioInterceptListener;import com.luck.picture.lib.interfaces.OnResultCallbackListener;import com.luck.picture.lib.permissions.PermissionChecker;import com.luck.picture.lib.permissions.PermissionResultCallback;import com.luck.picture.lib.utils.MediaUtils;import com.luck.picture.lib.utils.ToastUtils;import com.yalantis.ucrop.UCrop;import com.yalantis.ucrop.UCropImageEngine;import java.io.File;import java.util.ArrayList;import top.zibin.luban.Luban;import top.zibin.luban.OnNewCompressListener;public class PictureUtils { public static void openCamera(Context context, boolean isRotateImage, OnPictureSelectorResultListener onPictureSelectorResultListener) { openCamera(context, SelectMimeType.ofImage(), isRotateImage, onPictureSelectorResultListener); } public static void openVideo(Context context, boolean isRotateImage, OnPictureSelectorResultListener onPictureSelectorResultListener) { openCamera(context, SelectMimeType.ofVideo(), isRotateImage, onPictureSelectorResultListener); } public static void openCamera(Context context, int openCamera, boolean isRotateImage, OnPictureSelectorResultListener onPictureSelectorResultListener) { PictureSelector.create(context) .openCamera(openCamera) .isCameraAroundState(isRotateImage) .setVideoThumbnailListener(new VideoThumbListener(context)) .setCompressEngine((CompressFileEngine) (context1, source, call) -> Luban.with(context1).load(source).ignoreBy(100) .setCompressListener(new OnNewCompressListener() {@Overridepublic void onStart() {}@Overridepublic void onSuccess(String source, File compressFile) { if (call != null) { call.onCallback(source, compressFile.getAbsolutePath()); }}@Overridepublic void onError(String source, Throwable e) { if (call != null) { call.onCallback(source, null); }} }).launch()) .forResult(new OnResultCallbackListener<LocalMedia>() { @Override public void onResult(ArrayList<LocalMedia> result) { onPictureSelectorResultListener.onResult(result); } @Override public void onCancel() { } }); } public static void createAvatar(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) { create(mContext, SelectMimeType.ofImage(), selectResult, 1, 1, true, onPictureSelectorResultListener); } public static void createImageMin(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) { create(mContext, SelectMimeType.ofImage(), selectResult, 1, 1, false, onPictureSelectorResultListener); } public static void createImageMax(Context mContext, int selectMax, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) { create(mContext, SelectMimeType.ofImage(), selectResult, 1, selectMax, false, onPictureSelectorResultListener); } public static void createVideo(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) { create(mContext, SelectMimeType.ofVideo(), selectResult, 1, 1, false, onPictureSelectorResultListener); } public static void createAudio(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) { create(mContext, SelectMimeType.ofAudio(), selectResult, 1, 1, false, onPictureSelectorResultListener); } public static void createPicture(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) { create(mContext, SelectMimeType.ofAll(), selectResult, 1, 1, false, onPictureSelectorResultListener); } public static void create(Context mContext, int selectMimeType, ArrayList<LocalMedia> selectResult, int selectMin, int selectMax, boolean isCrop, OnPictureSelectorResultListener onPictureSelectorResultListener) { PictureSelector.create(mContext) .openGallery(selectMimeType) .setMaxSelectNum(selectMax) .setCropEngine(getCropFileEngine(isCrop)) .setMinSelectNum(selectMin) .setFilterVideoMaxSecond(selectMimeType == SelectMimeType.ofVideo() ? 60 : 60 * 10) .setFilterVideoMinSecond(5) .setRecordVideoMaxSecond(selectMimeType == SelectMimeType.ofVideo() ? 60 : 60 * 10) .setRecordVideoMinSecond(5) .setFilterMaxFileSize(100 * 1024 * 1024) .setCameraInterceptListener(new MeOnCameraInterceptListener()) .isFilterSizeDuration(true) .setSelectedData(selectResult) .setRecordAudioInterceptListener(new MeOnRecordAudioInterceptListener()) .setCompressEngine((CompressFileEngine) (context, source, call) -> Luban.with(context).load(source).ignoreBy(100) .setCompressListener(new OnNewCompressListener() {@Overridepublic void onStart() {}@Overridepublic void onSuccess(String source, File compressFile) { if (call != null) { call.onCallback(source, compressFile.getAbsolutePath()); }}@Overridepublic void onError(String source, Throwable e) { if (call != null) { call.onCallback(source, null); }} }).launch()).setImageEngine(GlideUtils.createGlideEngine()) .forResult(new OnResultCallbackListener<LocalMedia>() { @Override public void onResult(ArrayList<LocalMedia> result) { for (LocalMedia media : result) {if (media.getWidth() == 0 || media.getHeight() == 0) { if (PictureMimeType.isHasImage(media.getMimeType())) { MediaExtraInfo imageExtraInfo = MediaUtils.getImageSize(mContext, media.getPath()); media.setWidth(imageExtraInfo.getWidth()); media.setHeight(imageExtraInfo.getHeight()); } else if (PictureMimeType.isHasVideo(media.getMimeType())) { MediaExtraInfo videoExtraInfo = MediaUtils.getVideoSize(mContext, media.getPath()); media.setWidth(videoExtraInfo.getWidth()); media.setHeight(videoExtraInfo.getHeight()); }}//LogUtils.e("文件名: " + media.getFileName() + "\n" +// "是否压缩:" + media.isCompressed() + "\n" +// "压缩路径:" + media.getCompressPath() + "\n" +// "初始路径:" + media.getPath() + "\n" +// "绝对路径:" + media.getRealPath() + "\n" +// "是否裁剪:" + media.isCut() + "\n" +// "裁剪路径:" + media.getCutPath() + "\n" +// "是否开启原图:" + media.isOriginal() + "\n" +// "原图路径:" + media.getOriginalPath() + "\n" +// "沙盒路径:" + media.getSandboxPath() + "\n" +// "水印路径:" + media.getWatermarkPath() + "\n" +// "视频缩略图:" + media.getVideoThumbnailPath() + "\n" +// "原始宽高: " + media.getWidth() + "x" + media.getHeight() + "\n" +// "裁剪宽高: " + media.getCropImageWidth() + "x" + media.getCropImageHeight() + "\n" +// "文件大小: " + PictureFileUtils.formatAccurateUnitFileSize(media.getSize()) + "\n" +// "文件大小: " + media.getSize() + "\n" +// "文件时长: " + media.getDuration()//); } onPictureSelectorResultListener.onResult(result); } @Override public void onCancel() { } }); } public static void openImage(Context mContext, int position, ArrayList<LocalMedia> localMedia) { PictureSelector.create(mContext) .openPreview() .isHidePreviewDownload(true) .setImageEngine(GlideUtils.createGlideEngine()) .setExternalPreviewEventListener(new OnExternalPreviewEventListener() { @Override public void onPreviewDelete(int position) { } @Override public boolean onLongPressDownload(Context context, LocalMedia media) { return false; } }).startActivityPreview(position, false, localMedia); } public static void openImage(Context mContext, int position, String imageUrl) { ArrayList<LocalMedia> localMedia = new ArrayList<>(); for (String url : imageUrl.split(",")) { localMedia.add(LocalMedia.generateHttpAsLocalMedia(url)); } PictureSelector.create(mContext) .openPreview() .setImageEngine(GlideUtils.createGlideEngine()) .setExternalPreviewEventListener(new OnExternalPreviewEventListener() { @Override public void onPreviewDelete(int position) { } @Override public boolean onLongPressDownload(Context context, LocalMedia media) { return false; } }).startActivityPreview(position, false, localMedia); } public static void openVideo(Context mContext, int position, ArrayList<LocalMedia> localMedia) { openVideo(mContext, position, localMedia, null); } public static void openVideo(Context mContext, int position, ArrayList<LocalMedia> localMedia, VideoPlayerEngine videoPlayerEngine) { PictureSelector.create(mContext) .openPreview() .setImageEngine(GlideUtils.createGlideEngine()) .setVideoPlayerEngine(videoPlayerEngine) .isAutoVideoPlay(true) .setExternalPreviewEventListener(new OnExternalPreviewEventListener() { @Override public void onPreviewDelete(int position) { } @Override public boolean onLongPressDownload(Context context, LocalMedia media) { return false; } }).startActivityPreview(position, false, localMedia); } private static ImageFileCropEngine getCropFileEngine(boolean isCrop) { return isCrop ? new ImageFileCropEngine() : null; } private static class ImageFileCropEngine implements CropFileEngine { @Override public void onStartCrop(Fragment fragment, Uri srcUri, Uri destinationUri, ArrayList<String> dataSource, int requestCode) { UCrop.Options options = buildOptions(); UCrop uCrop = UCrop.of(srcUri, destinationUri, dataSource); uCrop.withOptions(options); uCrop.setImageEngine(new UCropImageEngine() { @Override public void loadImage(Context context, String url, ImageView imageView) { Glide.with(context).load(url).override(180, 180).into(imageView); } @Override public void loadImage(Context context, Uri url, int maxWidth, int maxHeight, OnCallbackListener<Bitmap> call) { Glide.with(context).asBitmap().load(url).override(maxWidth, maxHeight).into(new CustomTarget<Bitmap>() { @Override public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { if (call != null) { call.onCall(resource); } } @Override public void onLoadCleared(@Nullable Drawable placeholder) { if (call != null) { call.onCall(null); } }}); } }); uCrop.start(fragment.requireActivity(), fragment, requestCode); } } private static UCrop.Options buildOptions() { UCrop.Options options = new UCrop.Options(); options.setHideBottomControls(true); options.setFreeStyleCropEnabled(true); options.setShowCropFrame(true); options.setShowCropGrid(false); options.setCircleDimmedLayer(false); options.withAspectRatio(1, 1); options.isCropDragSmoothToCenter(false); options.setMaxScaleMultiplier(100); return options; } public interface OnPictureSelectorResultListener { void onResult(ArrayList<LocalMedia> result); } private static class MeOnCameraInterceptListener implements OnCameraInterceptListener { @Override public void openCamera(Fragment fragment, int cameraMode, int requestCode) { SimpleCameraX camera = SimpleCameraX.of(); camera.isAutoRotation(true); camera.setCameraMode(cameraMode); camera.setVideoFrameRate(50); camera.setVideoBitRate(5 * 1024 * 1024); camera.isDisplayRecordChangeTime(true); camera.isManualFocusCameraPreview(true); camera.isZoomCameraPreview(true); camera.setImageEngine((context, url, imageView) -> Glide.with(context).load(url).into(imageView)); camera.start(fragment.requireActivity(), fragment, requestCode); } } private static class MeOnRecordAudioInterceptListener implements OnRecordAudioInterceptListener { @Override public void onRecordAudio(Fragment fragment, int requestCode) { String[] recordAudio = {Manifest.permission.RECORD_AUDIO}; if (PermissionChecker.isCheckSelfPermission(fragment.getContext(), recordAudio)) { startRecordSoundAction(fragment, requestCode); } else { PermissionChecker.getInstance().requestPermissions(fragment, new String[]{Manifest.permission.RECORD_AUDIO}, new PermissionResultCallback() {@Overridepublic void onGranted() { startRecordSoundAction(fragment, requestCode);}@Overridepublic void onDenied() {} }); } } } private static void startRecordSoundAction(Fragment fragment, int requestCode) { Intent recordAudioIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); if (recordAudioIntent.resolveActivity(fragment.requireActivity().getPackageManager()) != null) { fragment.startActivityForResult(recordAudioIntent, requestCode); } else { ToastUtils.showToast(fragment.getContext(), "The system is missing a recording component"); } }}
上文监听 VideoThumbListener:
import android.content.Context;import android.graphics.Bitmap;import android.graphics.drawable.Drawable;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import com.bumptech.glide.Glide;import com.bumptech.glide.request.target.CustomTarget;import com.bumptech.glide.request.transition.Transition;import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener;import com.luck.picture.lib.interfaces.OnVideoThumbnailEventListener;import com.luck.picture.lib.utils.PictureFileUtils;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;public class VideoThumbListener implements OnVideoThumbnailEventListener { private Context context; public VideoThumbListener(Context context) { this.context = context; } @Override public void onVideoThumbnail(Context context, String videoPath, OnKeyValueResultCallbackListener call) { Glide.with(context).asBitmap().sizeMultiplier(0.6F).load(videoPath).into(new CustomTarget<Bitmap>() { @Override public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); resource.compress(Bitmap.CompressFormat.JPEG, 60, stream); FileOutputStream fos = null; String result = null; try { File targetFile = new File(getVideoThumbnailDir(), "thumbnails_" + System.currentTimeMillis() + ".jpg"); fos = new FileOutputStream(targetFile); fos.write(stream.toByteArray()); fos.flush(); result = targetFile.getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } finally { PictureFileUtils.close(fos); PictureFileUtils.close(stream); } if (call != null) { call.onCallback(videoPath, result); } } @Override public void onLoadCleared(@Nullable Drawable placeholder) { if (call != null) { call.onCallback(videoPath, ""); } } }); } private String getVideoThumbnailDir() { File externalFilesDir = context.getExternalFilesDir(""); File customFile = new File(externalFilesDir.getAbsolutePath(), "Thumbnail"); if (!customFile.exists()) { customFile.mkdirs(); } return customFile.getAbsolutePath() + File.separator; }}
使用:
使用摄像头拍照:
PictureUtils.openCamera(requireActivity(), false) {val localMedia = it[0] }
选择单张图片
PictureUtils.createImageMin(this, ArrayList()) {val localMedia = it[0] }
选择多张图片
PictureUtils.createImageMax(this, 9, arrayListOf()) { result: ArrayList<LocalMedia> -> ... }
简单使用如上。
END
来源地址:https://blog.csdn.net/lixinxiaos/article/details/132899394