推薦一個安卓圖片/視頻/文件選擇器。簡單好用。
不多廢話。直接上代碼:
首先,添加依賴:
//圖片選擇器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'api 'com.github.bumptech.glide:glide:4.12.0'
這些依賴是必須的。其次是封裝一下,方便后續使用
首先,創建 VideoThumbListener.java:
public class VideoThumbListener implements OnVideoThumbnailEventListener {private Context context;public VideoThumbListener(Context context) {this.context = context;}@Overridepublic void onVideoThumbnail(Context context, String videoPath, OnKeyValueResultCallbackListener call) {Glide.with(context).asBitmap().sizeMultiplier(0.6F).load(videoPath).into(new CustomTarget<Bitmap>() {@Overridepublic 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);}}@Overridepublic 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;}
}
再次就是圖片加載器的封裝:GlideUtils:
public class GlideUtils implements ImageEngine {/*** 加載圖片** @param context 上下文* @param url 資源url* @param imageView 圖片承載控件*/@Overridepublic void loadImage(Context context, String url, ImageView imageView) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).load(url)// .error(R.mipmap.icon_photo_error).into(imageView);}/*** 加載圖片** @param context 上下文* @param url 資源url* @param imageView 圖片承載控件* @param errorImage 圖片加載失敗資源文件*/public void loadImage(Context context, String url, ImageView imageView, int errorImage) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).load(url).error(errorImage).into(imageView);}@Overridepublic void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).load(url)
// .error(R.mipmap.ic_launcher).override(maxWidth, maxHeight).into(imageView);}/*** 加載相冊目錄封面** @param context 上下文* @param url 圖片路徑* @param imageView 承載圖片ImageView*/@Overridepublic void loadAlbumCover(Context context, String url, ImageView imageView) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).asBitmap().load(url)
// .error(R.mipmap.ic_launcher).override(180, 180).sizeMultiplier(0.5f).transform(new CenterCrop(), new RoundedCorners(8))
// .placeholder(R.mipmap.ic_launcher).into(imageView);}/*** 默認圖片** @param context* @param url* @param imageView*/public void loadVideoThumb(Context context, String url, ImageView imageView) {loadVideoThumb(context, url, 0, imageView);}public void loadVideoThumb(Context context, String url, int errorImage, ImageView imageView) {Glide.with(context).setDefaultRequestOptions(new RequestOptions().frame(1000000).centerCrop().error(errorImage).placeholder(errorImage)).load(url).into(imageView);}/*** 獲取視頻第一證* @param path 文件路徑* @return*/public Bitmap loadVideoThumb(String path) {MediaMetadataRetriever media = new MediaMetadataRetriever();media.setDataSource(path);return media.getFrameAtTime();}/*** 加載圖片列表圖片** @param context 上下文* @param url 圖片路徑* @param imageView 承載圖片ImageView*/@Overridepublic void loadGridImage(Context context, String url, ImageView imageView) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).load(url).override(200, 200).centerCrop()
// .error(R.mipmap.ic_launcher)
// .placeholder(R.mipmap.ic_launcher).into(imageView);}@Overridepublic void pauseRequests(Context context) {Glide.with(context).pauseRequests();}@Overridepublic void resumeRequests(Context context) {Glide.with(context).resumeRequests();}private GlideUtils() {}private static final class InstanceHolder {static final GlideUtils instance = new GlideUtils();}public static GlideUtils createGlideEngine() {return InstanceHolder.instance;}
}
最后。就是正題了,PictureUtils.java:
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() {@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>() {@Overridepublic void onResult(ArrayList<LocalMedia> result) {onPictureSelectorResultListener.onResult(result);}@Overridepublic 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() {@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>() {@Overridepublic 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);}@Overridepublic 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() {@Overridepublic void onPreviewDelete(int position) {}@Overridepublic 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() {@Overridepublic void onPreviewDelete(int position) {}@Overridepublic 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() {@Overridepublic void onPreviewDelete(int position) {}@Overridepublic 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 {@Overridepublic 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() {@Overridepublic void loadImage(Context context, String url, ImageView imageView) {Glide.with(context).load(url).override(180, 180).into(imageView);}@Overridepublic 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>() {@Overridepublic void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {if (call != null) {call.onCall(resource);}}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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() {}});}}}/*** 啟動錄音意圖** @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");}}
}
至于封裝,到此就結束了。最后看一下用法,舉例::
打開相機:
PictureUtils.openCamera(this,false){val localMedia = it[0]
}
選擇X張照片:
PictureUtils.createImageMax(this, 9, arrayListOf<LocalMedia>()){it.forEach { section ->// section.realPath:獲取圖片路徑}
}
就簡單列舉兩個。剩下的都差不多。
使用的時候別忘記添加權限喲。