一款针对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;
/**
* @author: zzw
* @Description:
* @Date: 2022/9/21 21:37
*/
public class PictureUtils {
/**
* 打开摄像头 拍照
*
* @param context
* @param isRotateImage true 前置 false 后置
* @param onPictureSelectorResultListener 回调
*/
public static void openCamera(Context context, boolean isRotateImage, OnPictureSelectorResultListener onPictureSelectorResultListener) {
openCamera(context, SelectMimeType.ofImage(), isRotateImage, onPictureSelectorResultListener);
}
/**
* 打开摄像头 录制视频
*
* @param context
* @param isRotateImage true 前置 false 后置
* @param onPictureSelectorResultListener 回调
*/
public static void openVideo(Context context, boolean isRotateImage, OnPictureSelectorResultListener onPictureSelectorResultListener) {
openCamera(context, SelectMimeType.ofVideo(), isRotateImage, onPictureSelectorResultListener);
}
/**
* 打开摄像头
*
* @param context 上下文
* @param 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() {
@Override
public void onStart() {
}
@Override
public void onSuccess(String source, File compressFile) {
if (call != null) {
call.onCallback(source, compressFile.getAbsolutePath());
}
}
@Override
public 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() {
}
});
}
/**
* 设置头像
*
* @param mContext
* @param selectResult 结果
* @param onPictureSelectorResultListener 结果回调
*/
public static void createAvatar(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) {
create(mContext, SelectMimeType.ofImage(), selectResult, 1, 1, true, onPictureSelectorResultListener);
}
/**
* 选择单张图片
*
* @param mContext
* @param selectResult 结果
* @param onPictureSelectorResultListener 结果回调
*/
public static void createImageMin(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) {
create(mContext, SelectMimeType.ofImage(), selectResult, 1, 1, false, onPictureSelectorResultListener);
}
/**
* 选择多张图片
*
* @param mContext
* @param selectResult 结果
* @param selectMax 最多选择
* @param onPictureSelectorResultListener 结果回调
*/
public static void createImageMax(Context mContext, int selectMax, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) {
create(mContext, SelectMimeType.ofImage(), selectResult, 1, selectMax, false, onPictureSelectorResultListener);
}
/**
* 选择单个视频
*
* @param mContext
* @param selectResult 结果
* @param onPictureSelectorResultListener 结果回调
*/
public static void createVideo(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) {
create(mContext, SelectMimeType.ofVideo(), selectResult, 1, 1, false, onPictureSelectorResultListener);
}
/**
* 选择单个音频
*
* @param mContext
* @param selectResult 结果
* @param onPictureSelectorResultListener 结果回调
*/
public static void createAudio(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) {
create(mContext, SelectMimeType.ofAudio(), selectResult, 1, 1, false, onPictureSelectorResultListener);
}
/**
* 选择 媒体 图片 视频 音频
*
* @param mContext
* @param selectResult 结果
* @param onPictureSelectorResultListener 结果回调
*/
public static void createPicture(Context mContext, ArrayList<LocalMedia> selectResult, OnPictureSelectorResultListener onPictureSelectorResultListener) {
create(mContext, SelectMimeType.ofAll(), selectResult, 1, 1, false, onPictureSelectorResultListener);
}
/**
* 默认设置
*
* @param mContext
* @param selectMimeType 结果
* @param selectResult 结果
* @param selectMin 最少选择
* @param selectMax 最多选择
* @param isCrop 是否剪裁
* @param 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() {
@Override
public void onStart() {
}
@Override
public void onSuccess(String source, File compressFile) {
if (call != null) {
call.onCallback(source, compressFile.getAbsolutePath());
}
}
@Override
public 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() {
}
});
}
/**
* 查看图片大图
*
* @param mContext
* @param position
* @param localMedia
*/
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);
}
/**
* 预览视频
*
* @param mContext
* @param position
* @param localMedia
*/
public static void openVideo(Context mContext, int position, ArrayList<LocalMedia> localMedia) {
openVideo(mContext, position, localMedia, null);
}
/**
* 预览视频
*
* @param mContext
* @param position
* @param localMedia
*/
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);
}
/**
* 裁剪引擎
*
* @return
*/
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);
}
}
/**
* 配制UCrop,可根据需求自我扩展
*
* @return
*/
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() {
@Override
public void onGranted() {
startRecordSoundAction(fragment, requestCode);
}
@Override
public void onDenied() {
}
});
}
}
}
/**
* 启动录音意图
*
* @param fragment
* @param requestCode
*/
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;
/**
*
* @Description: 视频缩略图
*
*/
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]
}
选择多张图片文章来源:https://www.toymoban.com/news/detail-742465.html
PictureUtils.createImageMax(this, 9, arrayListOf()) { result: ArrayList<LocalMedia> ->
...
}
简单使用如上。
END文章来源地址https://www.toymoban.com/news/detail-742465.html
到了这里,关于Android图像选择之 PictureSelector的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!