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

一.实现效果

二. 整体思路

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

三. 实现步骤

  1. 第一步现在清单文件中把需要的权限写上
 <!--  相机--><uses-permission android:name="android.permission.CAMERA" /><!-- 存储权限--><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  1. 创建popupWindow弹窗,给出图片选择方式
  2. 通过点击拍摄或相册按钮进行图片拍摄和选择上传,在此处的话,会来个权限的检查和申请
    (相机拍照要有拍照权限和读内存卡权限,相册选择只需读内存卡权限)
  3. 对拍照、相册选择图片的返回结果进行处理 (这里重点是数据类型转换的问题)
    就不多说,先上代码
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);

五. 出错排查

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

  1. 问题一

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

  2. 问题二

    出错原因:

    解决方案:

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

  1. android 拍照申请权限,Android拍照6.0以上动态获取权限

    ## 前言=== 在Android系统6.0以上拍照需要动态获取权限,并且在获取照片处理上也有些不同: 直接上代码吧,就不再多说了..... 具体实现 在res下新建xml文件夹,新建file_pat ...

  2. 微生物生态学中的挑战:建立对于群落功能与动态的预测性认识

    本文转自"微生太笔记",已获授权. 标题 Challenges in microbial ecology: building predictive understanding of ...

  3. android 动态获取权限有哪些,Android 6.0+ 动态获取权限

    Android 6.0+ 动态获取权限 这里有一个现成的库,可以直接拿来用.方便简单 1.向app下的gradle添加依赖: dependencies{ // android 6.0+ 动态获取权限 ...

  4. android 从相册选择,Android开发从相册中选取照片

    最近项目在做一个功能:就是需要从用户选择头像跳转到相册选择图片,这应该是一个很简单的需求,但是在网上搜了一下有好多都讲的很乱,其实用几十行代码就可以实现的为什么要说的那么复杂呢,下面就简单说一下喽. ...

  5. Android相册中搜索功能,Android开发从相册中选取照片的示例代码

    最近项目在做一个功能:就是需要从用户选择头像跳转到相册选择图片,这应该是一个很简单的需求,但是在网上搜了一下有好多都讲的很乱,其实用几十行代码就可以实现的为什么要说的那么复杂呢,下面就简单说一下喽. ...

  6. oppo手机如何找android,OPPO手机相册中的图片不见了怎么找回?

    近期有些小伙伴反馈,相册的图片好端端的,怎么不见了?玩起躲猫猫了?别担心,小编给大家介绍一个相册图片丢失排除的方法,看仔细了哦! OPPO手机相册中的图片不见了怎么办? 手机相册图片丢失,有可能是文件 ...

  7. Android实现拍照相册图片上传功能

    更改头像功能不像修改信息一样直接提交参数就可以,需要上传图片文件 我就直接贴代码了首先给出布局文件 <ImageViewandroid:id="@+id/iv"android ...

  8. Android权限说明大全及动态获取权限框架——Easypermissions

    Android6.0把权限分成正常权限和危险权限,AndroidManifest中声明的正常权限系统会自动授予,而危险权限则需要在使用的时候用户明确授予. 换句话说,就是Android6.0以上的系统 ...

  9. android 动态获取全县_android 6.0之后动态获取权限

    1. 概述 Android 6.0 (API 23) 之前应用的权限在安装时全部授予,运行时应用不再需要询问用户.在 Android 6.0 或更高版本对权限进行了分类,对某些涉及到用户隐私的权限可在 ...

最新文章

  1. 微软牛津计划-语音转文本-文本转语音代码和实现
  2. h5 解决ios端输入框失去焦点后页面不回弹或者底部留白问题
  3. java吵醒线程_一文搞懂 Java 线程中断
  4. Leetcode155最小栈
  5. python-数据结构-栈
  6. svm多分类代码_跟我一起机器学习系列文章知识点与代码索引目录,持续更新…...
  7. php上传文件很慢的原因_PHP编码安全:上传文件安全
  8. 【转载】Apache Ranger剖析:Hadoop生态圈的安全管家
  9. Flask安装首页显示
  10. 苹果鼓励美国员工赴中国出差 一天500美元奖金
  11. OpenCV精进之路(十):直方图匹配——模板匹配
  12. 应用ruby打造个性化的有道单词本 (二)
  13. 论文助手 for word/wps
  14. 宇枫资本女性刚工作理财建议
  15. 树莓派33/100 - Pico控制直流小马达,为智能避障小车提供动力
  16. 【下载工具】哔哩哔哩视频下载器——(Downkyi)下载姬v1.3.3
  17. 插了T管引流,该注意点啥
  18. 虚拟现实大作业——太阳系
  19. 永不过时的优雅 KOREANO ESSENTIAL 2022秋冬系列全新上市
  20. 利用虚拟机实时迁移技术可以实现服务器的,VMware vMotion虚拟机的实时迁移技术概述...

热门文章

  1. 告诉你如何给视频配音
  2. 四种经典的拉格朗日函数(Augmented Lagrangian Function)
  3. 中国移动TD-SCDMA业务类型分析
  4. python 最快 因式分解_python – 快速素因子分解模块
  5. C语言实现猜数字游戏(数字炸弹)
  6. Excel制作柱形图
  7. 如何训练开发者记忆能力
  8. CDGA认证|一文浅析数据治理与数据管理的区别
  9. ASP.NET AJAX 在Web开发中的应用
  10. 30款 香水品牌logo设计灵感 - logo设计公司 - ci设计