简述
PictureSelector相机和相册选择分开,全部代码,拿去可用。
流程,Android 通过webView的loadUrl一个H5页面。H5页面点击file,webview通过setWebChromeClient重写onShowFileChooser【当H5点击file时候会自动执行Android中onShowFileChooser方法】。然后后面再对点击的不同执行其他操作,是拍照、选择相册还是其他的。废话不多说,直接看gif动图和代码。
动图
这就是简单实现的内容,细节方面因为前端只能接受一张照片,所以还没有做处理。两张以及两张以上可以在前端做一个循环,因为Android中回调的是一个数组形式的。大致实现了,细节方面可以再自己优化优化。
gif动图。。。。。。
核心代码
重写onShowFileChooser
webView.setWebChromeClient(new WebChromeClient() { /** * API >= 21(Android 5.0.1)回调此方法 */ @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, WebChromeClient.FileChooserParams fileChooserParams) { Log.e("file回调", "运行方法 onShowFileChooser"); // (1)该方法回调时说明版本API >= 21,此时将结果赋值给 mUploadCallbackAboveL,使之 != null mUploadCallbackAboveL = valueCallback; showPopwindow(); return true; } });
PictureSelector点击直接激活相机
private void SelectorPhoto() { PictureSelector.create(this) .openCamera(SelectMimeType.ofImage()) .imageEngine(GlideEngine.createGlideEngine()) .forResult(new OnResultCallbackListener<LocalMedia>() { @Override public void onResult(List<LocalMedia> result) { mUploadCallbackAboveL.onReceiveValue(new Uri[]{Uri.parse(result.get(0).getPath())}); } @Override public void onCancel() { } }); }
弹窗代码
private void showPopwindow() { View parent = ((ViewGroup) this.findViewById(android.R.id.content)).getChildAt(0); View popView = inflate(this, R.layout.camera_pop_menu, null); Button btnCamera = (Button) popView.findViewById(R.id.btn_camera_pop_camera); Button btnAlbum = (Button) popView.findViewById(R.id.btn_camera_pop_album); Button btnCancel = (Button) popView.findViewById(R.id.btn_camera_pop_cancel); int width = getResources().getDisplayMetrics().widthPixels; int height = getResources().getDisplayMetrics().heightPixels; final PopupWindow popWindow = new PopupWindow(popView,width,height); popWindow.setFocusable(true); popWindow.setOutsideTouchable(false);// 设置同意在外点击消失 View.OnClickListener listener = new View.OnClickListener() { public void onClick(View v) { switch (v.getId()) { case R.id.btn_camera_pop_camera: // takePhoto(); SelectorPhoto(); break; case R.id.btn_camera_pop_album: InitSelectorMethold(); break; case R.id.btn_camera_pop_cancel: break; } popWindow.dismiss(); } }; btnCamera.setOnClickListener(listener); btnAlbum.setOnClickListener(listener); btnCancel.setOnClickListener(listener); ColorDrawable dw = new ColorDrawable(0x30000000); popWindow.setBackgroundDrawable(dw); popWindow.showAtLocation(parent, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); }
private String url = “http://10.1.192.88:8080/pictures.html”;
因为想要更接近项目环境,为了更好的体现出webview,所以这个是使用http-server【小型服务器(本地)】,手机和电脑要在同一个局域网下。当然你也可以不用。使用本地文件。
全部代码
MainActivity.java
package com.example.app7; import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.widget.Button; import android.widget.PopupWindow; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.entity.LocalMedia; import com.luck.picture.lib.listener.OnResultCallbackListener; import java.util.List; import static android.view.View.inflate; public class MainActivity extends AppCompatActivity { private Uri imageUri; private int REQUEST_CODE = 1234; private android.webkit.ValueCallback<Uri[]> mUploadCallbackAboveL; private android.webkit.ValueCallback<Uri> mUploadCallbackBelow; private WebView webView; private String url = "http://10.1.192.88:8080/pictures.html"; // todo private AlertDialog.Builder PictureSelector; private Object PictureMimeType; private com.luck.picture.lib.config.PictureMimeType SelectMimeType; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWebView(); } //初始化webView private void initWebView() { //从布局文件中扩展webView webView = (WebView) this.findViewById(R.id.webview); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { webView.setWebContentsDebuggingEnabled(true); } initWebViewSetting(); } //初始化webViewSetting @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) private void initWebViewSetting() { WebSettings settings = webView.getSettings(); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); settings.setDomStorageEnabled(true); settings.setDefaultTextEncodingName("UTF-8"); settings.setAllowContentAccess(true); // 是否可访问Content Provider的资源,默认值 true settings.setAllowFileAccess(true); // 是否可访问本地文件,默认值 true // 是否允许通过file url加载的Javascript读取本地文件,默认值 false settings.setAllowFileAccessFromFileURLs(false); // 是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false settings.setAllowUniversalAccessFromFileURLs(false); //开启JavaScript支持 settings.setJavaScriptEnabled(true); // 支持缩放 settings.setSupportZoom(true); //加载地址 webView.loadUrl(url); webView.setWebChromeClient(new WebChromeClient() { /** * API >= 21(Android 5.0.1)回调此方法 */ @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, WebChromeClient.FileChooserParams fileChooserParams) { Log.e("file回调", "运行方法 onShowFileChooser"); // (1)该方法回调时说明版本API >= 21,此时将结果赋值给 mUploadCallbackAboveL,使之 != null mUploadCallbackAboveL = valueCallback; // InitSelectorMethold(); // takePhoto(); // AlertDialog.Builder dialog=new AlertDialog.Builder(MainActivity.this); // dialog.setTitle("这是一个弹框提示"); // dialog.setMessage("嗯,这是消息内容"); // dialog.setCancelable(false); // // dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { // @Override // public void onClick(DialogInterface dialogInterface, int i) { // Toast.makeText(MainActivity.this, "确定,成功了", Toast.LENGTH_SHORT).show(); // InitSelectorMethold(); // } // // // }); // dialog.setNegativeButton("cancel", new DialogInterface.OnClickListener() { // @Override // public void onClick(DialogInterface dialogInterface, int i) { // Toast.makeText(MainActivity.this,"取消,失败了",Toast.LENGTH_SHORT).show(); // takePhoto(); // // } // // // }); // // dialog.show(); showPopwindow(); return true; } }); } private void showPopwindow() { View parent = ((ViewGroup) this.findViewById(android.R.id.content)).getChildAt(0); View popView = inflate(this, R.layout.camera_pop_menu, null); Button btnCamera = (Button) popView.findViewById(R.id.btn_camera_pop_camera); Button btnAlbum = (Button) popView.findViewById(R.id.btn_camera_pop_album); Button btnCancel = (Button) popView.findViewById(R.id.btn_camera_pop_cancel); int width = getResources().getDisplayMetrics().widthPixels; int height = getResources().getDisplayMetrics().heightPixels; final PopupWindow popWindow = new PopupWindow(popView,width,height); popWindow.setFocusable(true); popWindow.setOutsideTouchable(false);// 设置同意在外点击消失 View.OnClickListener listener = new View.OnClickListener() { public void onClick(View v) { switch (v.getId()) { case R.id.btn_camera_pop_camera: // takePhoto(); SelectorPhoto(); break; case R.id.btn_camera_pop_album: InitSelectorMethold(); break; case R.id.btn_camera_pop_cancel: break; } popWindow.dismiss(); } }; btnCamera.setOnClickListener(listener); btnAlbum.setOnClickListener(listener); btnCancel.setOnClickListener(listener); ColorDrawable dw = new ColorDrawable(0x30000000); popWindow.setBackgroundDrawable(dw); popWindow.showAtLocation(parent, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); } private void SelectorPhoto() { PictureSelector.create(this) .openCamera(SelectMimeType.ofImage()) .imageEngine(GlideEngine.createGlideEngine()) .forResult(new OnResultCallbackListener<LocalMedia>() { @Override public void onResult(List<LocalMedia> result) { mUploadCallbackAboveL.onReceiveValue(new Uri[]{Uri.parse(result.get(0).getPath())}); } @Override public void onCancel() { } }); } private void InitSelectorMethold() { // 进入相册 以下是例子:用不到的api可以不写 PictureSelector.create(this) .openGallery(PictureConfig.TYPE_IMAGE)//全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio() // .theme()//主题样式(不设置为默认样式) 也可参考demo values/styles下 例如:R.style.picture.white.style .maxSelectNum(4)// 最大图片选择数量 int .minSelectNum(2)// 最小选择数量 int // .imageSpanCount(4)// 每行显示个数 int .selectionMode(PictureConfig.MULTIPLE)// 多选 or 单选 PictureConfig.MULTIPLE or PictureConfig.SINGLE .previewImage(true)// 是否可预览图片 true or false .previewVideo(true)// 是否可预览视频 true or false .enablePreviewAudio(true) // 是否可播放音频 true or false // .isCamera(true)// 是否显示拍照按钮 true or false // .imageFormat(PictureMimeType.PNG)// 拍照保存图片格式后缀,默认jpeg .isZoomAnim(true)// 图片列表点击 缩放效果 默认true .sizeMultiplier(0.5f)// glide 加载图片大小 0~1之间 如设置 .glideOverride()无效 .setOutputCameraPath("/CustomPath")// 自定义拍照保存路径,可不填 .enableCrop(true)// 是否裁剪 true or false .compress(true)// 是否压缩 true or false .glideOverride(100, 100)// int glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度 .withAspectRatio(1, 1)// int 裁剪比例 如16:9 3:2 3:4 1:1 可自定义 .hideBottomControls(true)// 是否显示uCrop工具栏,默认不显示 true or false .isGif(true)// 是否显示gif图片 true or false .freeStyleCropEnabled(true)// 裁剪框是否可拖拽 true or false // .circleDimmedLayer()// 是否圆形裁剪 true or false // .showCropFrame()// 是否显示裁剪矩形边框 圆形裁剪时建议设为false true or false // .showCropGrid()// 是否显示裁剪矩形网格 圆形裁剪时建议设为false true or false // .openClickSound()// 是否开启点击声音 true or false // .selectionMedia()// 是否传入已选图片 List<LocalMedia> list // .previewEggs()// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中) true or false // .cropCompressQuality()// 裁剪压缩质量 默认90 int .minimumCompressSize(10)// 小于100kb的图片不压缩 .synOrAsy(true)//同步true或异步false 压缩 默认同步 // .cropWH()// 裁剪宽高比,设置如果大于图片本身宽高则无效 int // .rotateEnabled() // 裁剪是否可旋转图片 true or false // .scaleEnabled()// 裁剪是否可放大缩小图片 true or false // .videoQuality()// 视频录制质量 0 or 1 int .videoMaxSecond(15)// 显示多少秒以内的视频or音频也可适用 int .videoMinSecond(10)// 显示多少秒以内的视频or音频也可适用 int // .recordVideoSecond()//视频秒数录制 默认60s int .isDragFrame(false)// 是否可拖动裁剪框(固定) .imageEngine(GlideEngine.createGlideEngine()) .forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case PictureConfig.CHOOSE_REQUEST: // 结果回调 List<LocalMedia> selectList = PictureSelector.obtainMultipleResult(data); // // for (int i = 0; i < 2; i++) { // Uri urt = Uri.parse(selectList.get(i).getPath()); // // Log.e("这是一个循环测试,看看数据什么样的", "onActivityResult: "+urt); // } mUploadCallbackAboveL.onReceiveValue(new Uri[]{Uri.parse(selectList.get(0).getPath())}); break; default: break; } } } }
GlideEngine.java
package com.example.app7; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.view.View; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.BitmapImageViewTarget; import com.bumptech.glide.request.target.ImageViewTarget; import com.luck.picture.lib.engine.ImageEngine; import com.luck.picture.lib.listener.OnImageCompleteCallback; import com.luck.picture.lib.tools.MediaUtils; import com.luck.picture.lib.widget.longimage.ImageSource; import com.luck.picture.lib.widget.longimage.ImageViewState; import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView; public class GlideEngine implements ImageEngine { /** * 加载图片 * * @param context * @param url * @param imageView */ @Override public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) { Glide.with(context) .load(url) .into(imageView); } /** * 加载网络图片适配长图方案 * # 注意:此方法只有加载网络图片才会回调 * * @param context * @param url * @param imageView * @param longImageView * @param callback 网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#} */ @Override public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView, SubsamplingScaleImageView longImageView, OnImageCompleteCallback callback) { Glide.with(context) .asBitmap() .load(url) .into(new ImageViewTarget<Bitmap>(imageView) { @Override public void onLoadStarted(@Nullable Drawable placeholder) { super.onLoadStarted(placeholder); if (callback != null) { callback.onShowLoading(); } } @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { super.onLoadFailed(errorDrawable); if (callback != null) { callback.onHideLoading(); } } @Override protected void setResource(@Nullable Bitmap resource) { if (callback != null) { callback.onHideLoading(); } if (resource != null) { boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(), resource.getHeight()); longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE); imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE); if (eqLongImage) { // 加载长图 longImageView.setQuickScaleEnabled(true); longImageView.setZoomEnabled(true); longImageView.setPanEnabled(true); longImageView.setDoubleTapZoomDuration(100); longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP); longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER); longImageView.setImage(ImageSource.bitmap(resource), new ImageViewState(0, new PointF(0, 0), 0)); } else { // 普通图片 imageView.setImageBitmap(resource); } } } }); } /** * 加载网络图片适配长图方案 * # 注意:此方法只有加载网络图片才会回调 * * @param context * @param url * @param imageView * @param longImageView * @ 已废弃 */ @Override public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView, SubsamplingScaleImageView longImageView) { Glide.with(context) .asBitmap() .load(url) .into(new ImageViewTarget<Bitmap>(imageView) { @Override protected void setResource(@Nullable Bitmap resource) { if (resource != null) { boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(), resource.getHeight()); longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE); imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE); if (eqLongImage) { // 加载长图 longImageView.setQuickScaleEnabled(true); longImageView.setZoomEnabled(true); longImageView.setPanEnabled(true); longImageView.setDoubleTapZoomDuration(100); longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP); longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER); longImageView.setImage(ImageSource.bitmap(resource), new ImageViewState(0, new PointF(0, 0), 0)); } else { // 普通图片 imageView.setImageBitmap(resource); } } } }); } /** * 加载相册目录 * * @param context 上下文 * @param url 图片路径 * @param imageView 承载图片ImageView */ @Override public void loadFolderImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) { Glide.with(context) .asBitmap() .load(url) .override(180, 180) .centerCrop() .sizeMultiplier(0.5f) .apply(new RequestOptions().placeholder(R.drawable.picture_image_placeholder)) .into(new BitmapImageViewTarget(imageView) { @Override protected void setResource(Bitmap resource) { RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory. create(context.getResources(), resource); circularBitmapDrawable.setCornerRadius(8); imageView.setImageDrawable(circularBitmapDrawable); } }); } /** * 加载gif * * @param context 上下文 * @param url 图片路径 * @param imageView 承载图片ImageView */ @Override public void loadAsGifImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) { Glide.with(context) .asGif() .load(url) .into(imageView); } /** * 加载图片列表图片 * * @param context 上下文 * @param url 图片路径 * @param imageView 承载图片ImageView */ @Override public void loadGridImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) { Glide.with(context) .load(url) .override(200, 200) .centerCrop() .apply(new RequestOptions().placeholder(R.drawable.picture_image_placeholder)) .into(imageView); } private GlideEngine() { } private static GlideEngine instance; public static GlideEngine createGlideEngine() { if (null == instance) { synchronized (GlideEngine.class) { if (null == instance) { instance = new GlideEngine(); } } } return instance; } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical"> <ImageView android:id="@+id/mPictre" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent"> </WebView> </LinearLayout>
camera_pop_menu.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_margin="10dp" android:orientation="vertical" > <Button android:text="拍照" android:id="@+id/btn_camera_pop_camera" android:layout_width="match_parent" android:layout_height="45dp" android:textSize="18sp" /> <Button android:text="选相册" android:id="@+id/btn_camera_pop_album" android:layout_width="match_parent" android:layout_height="45dp" android:textSize="18sp" /> <Button android:text="选文件" android:id="@+id/btn_camera_pop_cancel" android:layout_width="match_parent" android:layout_height="45dp" android:layout_marginTop="10dp" android:textSize="18sp" /> </LinearLayout> </RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.app7"> <!-- 联网权限开启 --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" tools:ignore="ProtectedPermissions" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <queries package="${applicationId}"> <intent> <action android:name="android.media.action.IMAGE_CAPTURE"></action> </intent> <intent> <action android:name="android.media.action.ACTION_VIDEO_CAPTURE"></action> </intent> </queries> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.WebViewClient" android:usesCleartextTraffic="true"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
除了上面的之外,PictureSelector还有很多方法,例如下面的部分