Android开发:相册读取、拍照、图片裁剪和图片上传服务器等功能的实现
2016-05-03 16:30
1461 查看
修改日志[b]
2016.05.12
之前的程序存在两个问题:
1)从相册选择的图片如果比较大,会失败;
2)无法拍照上传照片。
修改了这两个bug,之前的代码已经被覆盖掉了,留着太误人子弟了。同时修改了一下标题和文章的文字描述[/b]
拍照示意
相册示意
一 整体功能描述
二 功能实现
1 获得图片
2上传到服务器并保存
3从服务器中获得图片并显示
4辅助工具
1)获得相册图片
2)通过拍照获得图片
3)裁剪图片
4)将图片上传至服务器
5)从服务器获得图片
**从相册中选择图片
这种方式原理比较简单,就是从SDK中获得照片,转成字节再生产Bitmap对象用于显示即可。
**拍照获得图片
拍照获取的图片原理就是先拍照存储,然后再读取,就和从相册中选择图片的原理一样了。
(2)实现:
关于图片的选择和处理,推荐一叶飘舟的文章。
首先是点击头像弹出一个dialog供选择读入图片的方式:
然后就是对图片数据进行处理了
ok,按照上面的代码,从相册选择照片和拍照 裁剪就没有问题了,至于对图片的其他一些处理,可以参考一叶飘舟的博文。
客户端
现在接着写1.1中代码使用到的UserController.updateHeadImage(userId, headImage, handler)。
服务器端
客户端将图片加密成了String,那么服务端就需要解码就可以获得图片。
由此就完美的将客户端的图像和数据都上传到服务器上了。下面部分就是从服务器上获得图片和数据再返回给客户端,有了前面部分的思路,那这部分就很容易实现,反其道而行之就可以了。
服务器端要做的事情就是将Bitmap转成String,然后和其他数据打包成json,返回给客户端。
客户端
这部分我就不贴代码了,因为自己把很多功能都封装了,要贴出来有点麻烦,总之此时的图片数据已经转成了String,只需要在显示的时候,再转成Bitmap。String->Bitmap的实现请参考我的Common类。
2016.05.12
之前的程序存在两个问题:
1)从相册选择的图片如果比较大,会失败;
2)无法拍照上传照片。
修改了这两个bug,之前的代码已经被覆盖掉了,留着太误人子弟了。同时修改了一下标题和文章的文字描述[/b]
拍照示意
相册示意
一 整体功能描述
二 功能实现
1 获得图片
2上传到服务器并保存
3从服务器中获得图片并显示
4辅助工具
一. 整体功能描述
整理了一下主要有以下几点功能:1)获得相册图片
2)通过拍照获得图片
3)裁剪图片
4)将图片上传至服务器
5)从服务器获得图片
二. 功能实现
1.1 获得图片
(1)通常情况下,有以下两种方式:**从相册中选择图片
这种方式原理比较简单,就是从SDK中获得照片,转成字节再生产Bitmap对象用于显示即可。
**拍照获得图片
拍照获取的图片原理就是先拍照存储,然后再读取,就和从相册中选择图片的原理一样了。
(2)实现:
关于图片的选择和处理,推荐一叶飘舟的文章。
首先是点击头像弹出一个dialog供选择读入图片的方式:
private CharSequence []its = {"拍照","从相册选择"}; public static final int TAKE_PHOTO = 1;//拍照 public static final int CROP_PHOTO = 2;//裁剪 public static final int SELECT_PIC = 0;//从相册选择 private Uri imageUri; //图片路径 private String filename; //图片名称 //上传头像 headImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new AlertDialog.Builder(ModiUserInfoActivity.this) .setTitle("更换头像") .setItems(its, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(); //最好自己封装一下,方便复用 switch (which) { case 0://拍照 //图片名称 时间命名 SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); Date date = new Date(System.currentTimeMillis()); filename = format.format(date); File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); File outputImage = new File(path,filename+".jpg"); try { if(outputImage.exists()) { outputImage.delete(); } outputImage.createNewFile(); } catch(IOException e) { e.printStackTrace(); } //将File对象转换为Uri并启动照相程序 imageUri = Uri.fromFile(outputImage); Intent tTntent = new Intent("android.media.action.IMAGE_CAPTURE"); //照相 tTntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //指定图片输出地址 startActivityForResult(tTntent,TAKE_PHOTO); //启动照相 break; case 1://从相册选择 intent.setAction(Intent.ACTION_GET_CONTENT);//ACTION_OPEN_DOCUMENT intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); intent.putExtra("return-data", true); intent.putExtra("crop", "true"); //设置宽高比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); //设置裁剪图片宽高、 intent.putExtra("outputX", 450); intent.putExtra("outputY", 450); startActivityForResult(intent,SELECT_PIC); break; } } }) .create() .show(); } }); }
然后就是对图片数据进行处理了
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != RESULT_OK) return; switch (requestCode) { case SELECT_PIC://相册 photo = data.getParcelableExtra("data"); if(photo!=null) { headImage = ImageDeal.toRoundBitmap(photo);//裁剪成圆形 //上传到服务器... UserController.updateHeadImage(userId, headImage, handler); photo.recycle(); } break; case SELECT_CAMERA://相机 try { Intent intent = new Intent("com.android.camera.action.CROP"); //剪裁 intent.setDataAndType(imageUri, "image/*"); intent.putExtra("scale", true); //设置宽高比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); //设置裁剪图片宽高 intent.putExtra("outputX", 450); intent.putExtra("outputY", 450); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); Toast.makeText(ModiUserInfoActivity.this, "剪裁图片", Toast.LENGTH_SHORT).show(); //广播刷新相册 Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intentBc.setData(imageUri); this.sendBroadcast(intentBc); startActivityForResult(intent, CROP_PHOTO); //设置裁剪参数显示图片至ImageView break; } catch (Exception e) { e.printStackTrace(); } break; case CROP_PHOTO: try { //图片解析成Bitmap对象 Bitmap bitmap = BitmapFactory.decodeStream( getContentResolver().openInputStream(photoFile)); headImage = ImageDeal.toRoundBitmap(bitmap); UserController.updateHeadImage(userId, headImage, handler); } catch(FileNotFoundException e) { e.printStackTrace(); } break; default: break; } }
ok,按照上面的代码,从相册选择照片和拍照 裁剪就没有问题了,至于对图片的其他一些处理,可以参考一叶飘舟的博文。
1.2上传到服务器并保存
查阅了很多资料,使用的方式要么都是那种上传文件那类看起来就特别复杂的方式,看的我真的头大了。后来找了好几个版本自己都没能成功实现把1.1中获取的图片上传到服务器。后来回想了一下http请求不就是传送的字节嘛,那我把图片转成字节不就能传过去了么,另一个问题又来了,有时候我们并不是单单传送一张图片过去,可能还有其他信息,比如一个表单或者一个id之类的,总之要传送的数据还有不仅仅是图片。之前传送数据都是通过json或者map转成String再转成byte[]实现的,那么现在传送的是图片+其他数据,思路便是:图片->String,然后和其他数据一起打包成json->String->byte[]->Bitmap。客户端
现在接着写1.1中代码使用到的UserController.updateHeadImage(userId, headImage, handler)。
/** * 保存头像 * @param userId * @param handler * @param image */ public static void updateHeadImage(final String userId,final Bitmap image, final Handler handler) { new Thread() { @Override public void run() { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("userId",userId); //将Bitmap转成String,其实这是一个加密过程。后面会有Common.Bitmap2String()的代码。 jsonObject.put("userImageContent", Common.Bitmap2String(image)); String content = String.valueOf(jsonObject); /** * 请求地址 */ String url = ConfigModel.getServerURL()+"user/updateImage"; String result = Common.httpPost(url,content);//post请求服务器:数据content,请求路径url。这个函数也是自己写在Common类里,其实就是稍微封装了一下post请求的过程,方便复用。 Log.i("result",result); /** * 服务器返回结果 * 继续干什么事情.... */ Message message = new Message(); Bundle bundle = new Bundle(); bundle.putString("result",result); message.setData(bundle); message.what = R.id.save_user_image_result; handler.sendMessage(message); }catch (Exception e){} } }.start(); }
服务器端
客户端将图片加密成了String,那么服务端就需要解码就可以获得图片。
//第一步,将数据流转String,自己封装成了一个read函数,方便复用; String streamIn = ReadStream.read(new BufferedInputStream(request.getInputStream())); //JSONObject object = JSONObject.fromObject(streamIn).getJSONObject("user"); JSONObject object = JSONObject.fromObject(streamIn);//String转JSON String userId = object.getString("userId"); String userImageContent = object.getString("userImageContent");//获得图像的数据 //..其他步骤省略 //..比如判断是否是新图像,比如生成图像ID imgId = Tool.getUUID(); //第二步将图像数据String转成Bitmap byte[] bitmapArray= Base64.decode(imageContent); try { File imageFile = new File(headImagePath); if(!imageFile.exists()) imageFile.mkdirs(); //创建输出流 FileOutputStream outStream = new FileOutputStream(imageFile.getPath()+"\\"+imgId+".png"); //写入数据 outStream.write(bitmapArray); //关闭输出流 outStream.close(); } catch(IOException e) { System.out.println(e); }
由此就完美的将客户端的图像和数据都上传到服务器上了。下面部分就是从服务器上获得图片和数据再返回给客户端,有了前面部分的思路,那这部分就很容易实现,反其道而行之就可以了。
1.3从服务器中获得图片并显示
服务器端服务器端要做的事情就是将Bitmap转成String,然后和其他数据打包成json,返回给客户端。
//读取图片,转成String public static String readImage(String imgId) { //byte []data = imageContent.getBytes(); File file = new File(headImagePath+imgId+".png"); try { FileInputStream inputStream = new FileInputStream(file); byte[] bitmapArray = new byte[(int) file.length()]; inputStream.read(bitmapArray); inputStream.close(); return Base64.encode(bitmapArray); } catch(Exception e) { return ""; } } //***************************************** //代码片.....和上面的代码不在同一个java文件中 //***************************************** /** * 查询一个user * * @param userId * 主键 * @return user */ @RequestMapping(value = "/user/get/{id}") @ResponseBody public JSONObject get(@PathVariable("id") String userId, HttpServletResponse response) { response.setHeader("Access-Control-Allow-Origin", "*"); User user = userDAO.getUser(userId); JSONObject jsonObject = new JSONObject(); jsonObject.put("user", user); if(user.getUserImage()!=null&&!"".equals(user.getUserImage())) { jsonObject.put("userImageContent", Tool.readImage(user.getUserImage())); } return jsonObject; }
客户端
这部分我就不贴代码了,因为自己把很多功能都封装了,要贴出来有点麻烦,总之此时的图片数据已经转成了String,只需要在显示的时候,再转成Bitmap。String->Bitmap的实现请参考我的Common类。
1.4辅助工具
/** * 图像转字节 * @param bm * @return */ public static byte[] Bitmap2Bytes(Bitmap bm) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); } /** * 图像转String * @param bitmap * @return */ public static String Bitmap2String(Bitmap bitmap) { return Base64.encodeToString(Bitmap2Bytes(bitmap), Base64.DEFAULT); } /** * string转成bitmap * * @param st */ public static Bitmap String2Bitmap(String st) { Bitmap bitmap = null; try { byte[] bitmapArray; bitmapArray = Base64.decode(st, Base64.DEFAULT); bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length); return bitmap; } catch (Exception e) { return null; } } /** * 把bitmap转成圆形 * */ public static Bitmap toRoundBitmap(Bitmap bitmap){ int width=bitmap.getWidth(); int height=bitmap.getHeight(); int r=0; //取最短边做边长 if(width<height){ r=width; }else{ r=height; } //构建一个bitmap Bitmap backgroundBm= Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); //new一个Canvas,在backgroundBmp上画图 Canvas canvas=new Canvas(backgroundBm); Paint p=new Paint(); //设置边缘光滑,去掉锯齿 p.setAntiAlias(true); RectF rect=new RectF(0, 0, r, r); //通过制定的rect画一个圆角矩形,当圆角X轴方向的半径等于Y轴方向的半径时, //且都等于r/2时,画出来的圆角矩形就是圆形 canvas.drawRoundRect(rect, r/2, r/2, p); //设置当两个图形相交时的模式,SRC_IN为取SRC图形相交的部分,多余的将被去掉 p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); //canvas将bitmap画在backgroundBmp上 canvas.drawBitmap(bitmap, null, rect, p); return backgroundBm; }
相关文章推荐
- Android之自定义View的实现
- android开发步步为营之101:动态调整view的宽度和高度以及在页面的坐标位置
- Android Studio 生成APK实践
- Android Studio如何用release签名进行debug调试
- android Button防止多次点击
- Android线程中设置控件
- android view用动画实现遮罩效果
- Android 注解框架----Butter knife
- Fragment 中的onConfigurationChanged 在切换语言的情况下不被回调的解决办法
- Android5.0查看应用使用情况的权限
- android数据库存储
- 【转】如何分析解决Android ANR
- android 属性文件default.prop与adb
- Android json数据的解析
- Android 浅谈一个全适配方案
- Android--FragmentTabHost+Fragment
- android--------关于创建模拟器不能移动的问题
- AndroidStudiod如何打包aar文件包和引用小记
- android圆形头像:相机相册加载图片到圆形头像
- android zxing 扫描二维码 横屏转竖屏