700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android中拍照(相册中选择)并上传图片功能(包括动态获取权限)

Android中拍照(相册中选择)并上传图片功能(包括动态获取权限)

时间:2020-04-28 17:48:47

相关推荐

Android中拍照(相册中选择)并上传图片功能(包括动态获取权限)

作为新手小白,为了实现这个拍照和相册选取图片并上传功能,确实花费了很多时间,因为实现不容易,所以记录下来,一和大家分享,二为之后学习做个备忘。

一.实现效果

二. 整体思路

Android手机客户端,拍照(或从相册中选择图片),然后上传到服务器。服务器端接收到手机端上传上来的图片并处理后返回把从服务器获取到的图片展示在页面上

三. 实现步骤

第一步现在清单文件中把需要的权限写上

<!-- 相机--><uses-permission android:name="android.permission.CAMERA" /><!-- 存储权限--><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

创建popupWindow弹窗,给出图片选择方式通过点击拍摄或相册按钮进行图片拍摄和选择上传,在此处的话,会来个权限的检查和申请

(相机拍照要有拍照权限和读内存卡权限,相册选择只需读内存卡权限)对拍照、相册选择图片的返回结果进行处理(这里重点是数据类型转换的问题)

就不多说,先上代码

public class MyEvaluation<OkHttpClient, FormBody> extends AppCompatActivity implements View.OnClickListener {/*****控件定义省略。。**/private MyEvalImageAdapter myEvalImageAdapter;// private List<String> imageList=null;private List<Map<String,Object>> imageList = null;private List<ImageViewInfo> mImgList=null;private final int TAKE_PHOTO_PERMISSION_REQUEST_CODE = 0; //拍照的权限处理返回码private final int WRITE_SDCARD_PERMISSION_REQUEST_CODE = 1; // 读储存卡内容的权限处理返回码private final int REQUEST_CODE_FROM_PHOTO = 2; //相册选取返回的requestCodeprivate final int REQUEST_CODE_FROM_CAMERA = 1;//拍照返回的requestCodeprivate String imgString = ""; //要上传的图片路径private String mFilePath=""; 拍照得到的原图保存的图片路径String[] permissions = new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE};AlertDialog alertDialog;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initView();}/*** todo 初始化控件 initView*/private void initView() {/**省略**/}//使用相机拍摄功能的权限检查并设置private void checkPermission() {List<String> permissionList = new ArrayList<>();for (int i = 0; i < permissions.length; i++) {if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {permissionList.add(permissions[i]);}}if (permissionList.size() <= 0) {//说明权限都已经通过,可以做你想做的事情去(调起相机拍摄)openCamera();} else {//对存在的未允许的权限进行申请ActivityCompat.requestPermissions(this, permissions, TAKE_PHOTO_PERMISSION_REQUEST_CODE);}}/*** todo 对用户权限授予结果处理* @param requestCode 权限要求码,即我们申请权限时传入的常量 如: TAKE_PHOTO_PERMISSION_REQUEST_CODE* @param permissions 保存权限名称的 String 数组,可以同时申请一个以上的权限* @param grantResults 每一个申请的权限的用户处理结果数组(是否授权)*/@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode){case TAKE_PHOTO_PERMISSION_REQUEST_CODE:boolean haspermission = false;for(int i=0;i<grantResults.length;i++){if (grantResults[i] == -1){haspermission = true;}}if(haspermission){//跳转到系统设置权限页面,或者直接关闭页面,不让他继续访问permissionDialog();}else{//全部权限通过,可以进行下一步操作(调起相机拍摄)openCamera();}break;case WRITE_SDCARD_PERMISSION_REQUEST_CODE:if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {}else{ToolUtils.midToast(this,"读内存卡内容权限被拒绝",1000);}break;}}//手动打开设置应用权限private void permissionDialog() {if (alertDialog == null) {alertDialog = new AlertDialog.Builder(this).setTitle("提示信息").setMessage("当前应用缺少必要权限,该拍摄功能暂时无法使用。如若需要,请单击【设置】按钮前往设置中心进行权限授权。").setPositiveButton("设置", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {cancelPermissionDialog();Uri packageURI = Uri.parse("package:" + getPackageName());Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);startActivity(intent);}}).setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {cancelPermissionDialog();}}).create();}alertDialog.show();}//用户取消授权,关闭对话款private void cancelPermissionDialog() {alertDialog.cancel();}/*** todo 点击事件* @param v*/@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.uploadImg: //打开弹窗(上传图片方式选择)createPopupWindow(v);break;//拍照上传case R.id.camera_btn:popupWindow.dismiss();//6.0才用动态权限if (Build.VERSION.SDK_INT >= 23) {checkPermission();}break;//从相册中选择case R.id.pic_btn:popupWindow.dismiss();//6.0才用动态权限if (Build.VERSION.SDK_INT >= 23) {if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {// 申请读写内存卡内容的权限ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_SDCARD_PERMISSION_REQUEST_CODE);}else{intent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI);startActivityForResult(intent, REQUEST_CODE_FROM_PHOTO);}}break;case R.id.cancel_btn: //点击取消按钮,关闭弹窗popupWindow.dismiss();break;case R.id.eval_commit_btn:submitComment(); //提交break;}}//打开相机拍照private void openCamera() {// 获取SD卡路径mFilePath = Environment.getExternalStorageDirectory().getPath();// 保存图片的文件名mFilePath = mFilePath + "/" + "IMG"+ Calendar.getInstance().getTime() +".png";//android7.0以上版本if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){takePhotoBiggerThan7((new File(mFilePath)).getAbsolutePath());}else{Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Uri mUri = Uri.fromFile(new File(mFilePath));openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,mUri);startActivityForResult(openCameraIntent,REQUEST_CODE_FROM_CAMERA);}// intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// startActivityForResult(intent, REQUEST_CODE_FROM_CAMERA);}private void takePhotoBiggerThan7(String absolutePath) {Uri mCameraTempUri;try {ContentValues values = new ContentValues(1);values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");values.put(MediaStore.Images.Media.DATA, absolutePath);mCameraTempUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);if (mCameraTempUri != null) {intent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraTempUri);intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);}startActivityForResult(intent, REQUEST_CODE_FROM_CAMERA);} catch (Exception e) {e.printStackTrace();}}/*** todo 对拍照、相册选择图片的返回结果进行处理* @param requestCode 返回码,用于确定是哪个 Activity 返回的数据* @param resultCode 返回结果,一般如果操作成功返回的是 RESULT_OK* @param data 返回对应 activity 返回的数据*/@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode){// 表示 调用照相机拍照返回case REQUEST_CODE_FROM_CAMERA:if(resultCode == RESULT_OK){try {// 获取输入流FileInputStream is = new FileInputStream(mFilePath);// 把流解析成bitmap,此时就得到了清晰的原图Bitmap imageBitmap = BitmapFactory.decodeStream(is);Bitmap newImageBitmap = scaleBitmap(imageBitmap,(float)0.5); //压缩图片Uri imageUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(),newImageBitmap, "IMG"+ Calendar.getInstance().getTime(),null));imgString = UriToFile(imageUri);upLoadImg(); //调用接口把图片上传到服务器} catch (FileNotFoundException e) {e.printStackTrace();}}break;//从相册中选择图片返回case REQUEST_CODE_FROM_PHOTO:if(resultCode == RESULT_OK){try {Uri uri = data.getData();Bitmap imageBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),uri);Bitmap newImageBitmap = scaleBitmap(imageBitmap,(float)0.5); //压缩图片Uri newUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(),newImageBitmap,"IMG"+ Calendar.getInstance().getTime(),null));imgString = UriToFile(newUri);upLoadImg();} catch (IOException e) {e.printStackTrace();}}break;}}/*** todo uri 转 file* @param uri* @return*/public String UriToFile(Uri uri) {String[] filePc = {MediaStore.Images.Media.DATA};Cursor cursor = getContentResolver().query(uri, filePc, null, null, null);cursor.moveToFirst();Log.i(TAG, "UriToFile: 22"+cursor);int col = cursor.getColumnIndex(filePc[0]);String pic = cursor.getString(col);cursor.close();return pic;}/*** todo 压缩图片* @param origin* @param ratio* @return*/public Bitmap scaleBitmap(Bitmap origin, float ratio) {if (origin == null) {return null;}int width = origin.getWidth();int height = origin.getHeight();Matrix matrix = new Matrix();matrix.preScale(ratio, ratio);Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);return newBM;}/*** todo 创建弹窗(用于上传图片方式选择)* author wang* @param view*/private void createPopupWindow(View view) {if(popupView==null){popupView = getLayoutInflater().inflate(R.layout.popup_unload_image,null);}popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT,true);// popupWindow.showAsDropDown(view, view.getWidth(),view.getHeight());popupWindow.showAtLocation(findViewById(R.id.layout_parent), Gravity.BOTTOM,0,0); //底部显示弹窗popupWindow.setBackgroundDrawable(getResources().getDrawable(R.color.white));setAlpha(0.3f);//把背景还原popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {@Overridepublic void onDismiss() {setAlpha(1.0f);}});initPopupView();}/*** todo 初始化弹窗的控件*/private void initPopupView() {Button camera_btn = popupView.findViewById(R.id.camera_btn);Button pic_btn = popupView.findViewById(R.id.pic_btn);Button cancel_btn = popupView.findViewById(R.id.cancel_btn);camera_btn.setOnClickListener(this);pic_btn.setOnClickListener(this);cancel_btn.setOnClickListener(this);}/*** todo 自定义方法,遮罩层* @param f*/private void setAlpha(float f) {WindowManager.LayoutParams lp =getWindow().getAttributes();lp.alpha = f;getWindow().setAttributes(lp);}/*** todo handler*/@SuppressLint("HandlerLeak")Handler handler = new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what){case 1:try{JSONObject resObj = (JSONObject) msg.obj;if(resObj !=null && resObj.getInt("status")==1000){String imgUrl = Helper.fixImgUrl(resObj.getString("data"));Map<String,Object> imageMap = new HashMap<>();imageMap.put("url",imgUrl);imageList.add(imageMap);Log.i(TAG, "handleMessage:ist "+imageList);myEvalImageAdapter.setData(imageList);}}catch (JSONException je){je.printStackTrace();}break;case 2:break;}}};/*** todo 上传图片(api)*/private void upLoadImg() {try{new Thread(){@Overridepublic void run() {super.run();JSONObject retObj = Helper.imgUpload(imgString,userToken);msg = handler.obtainMessage();msg.what=1;msg.obj = retObj;handler.sendMessage(msg);}}.start();}catch (Exception e){e.printStackTrace();}}/*** todo 创建适配器*/private void createAdapter(){GridLayoutManager gridManager = new GridLayoutManager(MyEvaluation.this,3);eval_image_rv.setLayoutManager(gridManager);myEvalImageAdapter = new MyEvalImageAdapter(getApplicationContext());eval_image_rv.setAdapter(myEvalImageAdapter);itemClick();}/*** todo 点击图片进行放大预览*/private void itemClick() {/**省略**/}/*** todo 点击提交评价*/private void submitComment(){/**省略**/} }

上面给出的代码 基本是用到的,大家也去试试,如果不出意外,是可以运行的了

四. 重点

不过接下来我要提下的就是大家调用相机拍照获取的图片不清晰的问题,当然我上面写出的代码,获取到的图片是清晰,但还是要说下:

//打开相机拍照private void openCamera() {intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);startActivityForResult(intent, REQUEST_CODE_FROM_CAMERA);}

对返回结果进行处理

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode){// 表示 调用照相机拍照case REQUEST_CODE_FROM_CAMERA:if(resultCode == RESULT_OK){Bundle bundleData = data.getExtras();Bitmap imageBitmap = (Bitmap) bundleData.get("data");Log.i(TAG, "onActivityResult: ff"+imageBitmap);Bitmap newImageBitmap = scaleBitmap(imageBitmap,(float)0.5); //压缩图片Uri imageUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(),newImageBitmap, "IMG"+ Calendar.getInstance().getTime(),null));imgString = UriToFile(imageUri);upLoadImg();}break;

对于上面这种方法是不是很熟悉,因为我一开始来就是这样子写的,图片是可以获取到了,但是我这边的需求是要点击可预览大图,一看大图,被吓到了,根本看不清楚大图中的具体内容。

然后我就又去查了一下,原来调用系统相机去获取data时获取到的只是缩略图,如果想要查看大图,需要将拍照得到的原图则保存到手机中,然后再去读取。

//打开相机拍照private void openCamera() {// 获取SD卡路径mFilePath = Environment.getExternalStorageDirectory().getPath();// 保存图片的文件名mFilePath = mFilePath + "/" + "IMG"+ Calendar.getInstance().getTime() +".png";//android7.0以上版本if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){takePhotoBiggerThan7((new File(mFilePath)).getAbsolutePath());}else{Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Uri mUri = Uri.fromFile(new File(mFilePath));openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,mUri);startActivityForResult(openCameraIntent,REQUEST_CODE_FROM_CAMERA);}}private void takePhotoBiggerThan7(String absolutePath) {Uri mCameraTempUri;try {ContentValues values = new ContentValues(1);values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");values.put(MediaStore.Images.Media.DATA, absolutePath);mCameraTempUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);if (mCameraTempUri != null) {intent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraTempUri);intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);}startActivityForResult(intent, REQUEST_CODE_FROM_CAMERA);} catch (Exception e) {e.printStackTrace();}}

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode){// 表示 调用照相机拍照case REQUEST_CODE_FROM_CAMERA:if(resultCode == RESULT_OK){try {// 获取输入流FileInputStream is = new FileInputStream(mFilePath);// 把流解析成bitmap,此时就得到了清晰的原图Bitmap imageBitmap = BitmapFactory.decodeStream(is);Bitmap newImageBitmap = scaleBitmap(imageBitmap,(float)0.5); //压缩图片Uri imageUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(),newImageBitmap, "IMG"+ Calendar.getInstance().getTime(),null));imgString = UriToFile(imageUri);upLoadImg(); //调用接口把图片上传到服务器} catch (FileNotFoundException e) {e.printStackTrace();}break;

这样子就可以获取到清晰的图片啦

注意:

在Uri imageUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(),newImageBitmap, “IMG”+ Calendar.getInstance().getTime(),null))中使用MediaStore.Images.Media.insertImage方法会使相册中生成两张一模一样的图,所以有必要的话在用完后把它給删除

this.getContentResolver().delete(uri, null, null);

五. 出错排查

以下问题是我在开发过程中遇到的,然后我自己的一个解决方案,供大家参考下,具体如何还得根据自己的情况来

问题一

解决方法:在清单文件中添加 android:requestLegacyExternalStorage="true"就可以了

问题二

出错原因:

解决方案:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。