android 拍照 相册 剪切以及显示功能
一.概述
android的 图片拍照 ,相册选图,以及图片剪切功能可以说非常常用. 尤其是图片上传功能,必然用到此功能. 而公司最近的一个项目中正好用到该功能. 记录下来以便以后再次用到,直接拿来使用.
在此之前,我也参考了网上很多代码示例, 写得都不错, 但是有一个问题可能大家都没发现, 当我参考网上示例写完后,发现 小米手机竟然不能使用该功能, 最后查了很多资料依然不能解决,最后猜测要么是 小米手机bug,要么就是 小米把android底层修改的过火了.
但是不管怎么样,必须要解决这个问题,毕竟用小米手机的人不在少数. 最后经人指点,终于搞定.
二.运行效果图点击圆形头像弹出自定义Dialog
代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv= (CircleImageView) findViewById(R.id.iv); iv.setBorderWidth(5); iv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new ActionSheetDialog(MainActivity.this).Builder() .addSheetItem("拍照", ActionSheetDialog.SheetItemColor.BULE, new ActionSheetDialog.OnSheetItemClickListener() { @Override public void onClick(int witch) { cameraorpic = 1; openCamera(); } }).addSheetItem("打开相册",ActionSheetDialog.SheetItemColor.BULE, new ActionSheetDialog.OnSheetItemClickListener() { @Override public void onClick(int witch) { cameraorpic = 0; openPic(); } }).show(); } }); } /** * 打开相册 */ private void openPic() { Intent pickIntent = new Intent(Intent.ACTION_PICK, null); pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*"); startActivityForResult(pickIntent, REQUESTCODE_PICK); } /** * 打开相机 */ private void openCamera() { String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); if (!outDir.exists()) { outDir.mkdirs(); } outFile = new File(outDir, System.currentTimeMillis() + ".jpg"); Log.e("outFile",outFile+""); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outFile)); intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(intent, PHOTO_REQUEST_TAKEPHOTO); } else { Log.e("CAMERA", "请确认已经插入SD卡"); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 进行判断是那个操作跳转回来的,如果是裁剪跳转回来的这块就要把图片现实到View上,其他两种的话都把数据带入裁剪界面 switch (requestCode) { //相册 case REQUESTCODE_PICK: if (data == null || data.getData() == null) { return; } startPhotoZoom(data.getData()); break; //裁剪 case REQUESTCODE_CUTTING: if (data != null) { setPicToView(data); } break; //拍照 case PHOTO_REQUEST_TAKEPHOTO: Log.e("outFile1",outFile+""); startPhotoZoom(Uri.fromFile(outFile)); break; } super.onActivityResult(requestCode, resultCode, data); } /** * 把裁剪好的图片设置到View上或者上传到网络 * @param data */ private void setPicToView(Intent data) { Bundle extras = data.getExtras(); if (extras != null) { /** 可用于图像上传 */ currentBitmap = extras.getParcelable("data"); iv.setImageBitmap(currentBitmap); } } /** * 调用系统的图片裁剪 * @param data */ private void startPhotoZoom(Uri data) { Log.e("outFile2",outFile+""); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(data, "image/*"); intent.putExtra("crop", true); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("outputX", 300); intent.putExtra("outputY", 300); intent.putExtra("scale", true);//黑边 intent.putExtra("scaleUpIfNeeded", true);//黑边 intent.putExtra("return-data", true); intent.putExtra("noFaceDetection", true); startActivityForResult(intent, REQUESTCODE_CUTTING); }
上面的CircleImageView 是一个 圆形头像, 可以自定义实现也可以用 第三方库,这里用的是第三方的库
de.hdodenhof.circleimageview.CircleImageView --- github上面有,使用方法非常简单 配置如下所示
<de.hdodenhof.circleimageview.CircleImageView android:id="@+id/img_head" android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="center_horizontal" android:layout_marginLeft="@dimen/space_xl" android:layout_marginTop="@dimen/space_xl" android:src="@mipmap/default_head" app:civ_border_color="#FFFFFF" app:civ_border_width="2dp" />
这里的难点是这个自定义的 Dialog如下
public class ActionSheetDialog { private Context context; private Dialog dialog; private TextView txt_title; private TextView txt_cancel; private LinearLayout lLayout_content; private ScrollView sLayout_content; private boolean showTitle = false; private List<SheetItem> sheetItemList; private Display display; public ActionSheetDialog(Context context){ this.context = context; WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); display = windowManager.getDefaultDisplay(); } public ActionSheetDialog Builder(){ // 获取Dialog布局 View view = LayoutInflater.from(context).inflate(R.layout.view_actionsheet,null); // dialog的最小宽,设置屏幕宽度为 view.setMinimumWidth(display.getWidth()); //获取xml文件中的控件 sLayout_content = (ScrollView) view.findViewById(R.id.sLayout_content); lLayout_content = (LinearLayout) view.findViewById(R.id.lLayout_content); txt_title = (TextView) view.findViewById(R.id.txt_title); txt_cancel = (TextView) view.findViewById(R.id.txt_cancel); txt_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); //定义 dialog的布局和参数 dialog = new Dialog(context, R.style.ActionSheetDialogStyle); dialog.setContentView(view); Window dialogWindow = dialog.getWindow(); dialogWindow.setGravity(Gravity.LEFT | Gravity.BOTTOM); WindowManager.LayoutParams lp = dialogWindow.getAttributes(); lp.x = 0; lp.y = 0; dialogWindow.setAttributes(lp); return this; } public ActionSheetDialog setTitle(String title) { showTitle = true; txt_title.setVisibility(View.VISIBLE); txt_title.setText(title); return this; } public ActionSheetDialog setCancelable(boolean cancel) { dialog.setCancelable(cancel); return this; } public ActionSheetDialog setCanceledOnTouchOutside(boolean cancel) { dialog.setCanceledOnTouchOutside(cancel); return this; } public ActionSheetDialog addSheetItem(String itemName,SheetItemColor itemTextColor,OnSheetItemClickListener listener){ if(null == sheetItemList){ sheetItemList = new ArrayList<SheetItem>(); } sheetItemList.add(new SheetItem(itemName, itemTextColor, listener)); return this; } public void show(){ setSheetItems(); dialog.show(); } private void setSheetItems() { if (sheetItemList == null || sheetItemList.size() <= 0){ return; } int size = sheetItemList.size(); // 控制高度 if (size > 5){ LayoutParams params = sLayout_content.getLayoutParams(); params.height = display.getHeight()/2; sLayout_content.setLayoutParams(params); } for (int i = 1; i <= size; i++) { final int index = i; SheetItem sheetItem = sheetItemList.get(i-1); String itemName = sheetItem.name; SheetItemColor itemTextcolor = sheetItem.color; final OnSheetItemClickListener listener = sheetItem.listener; TextView textView = new TextView(context); textView.setText(itemName); textView.setTextSize(18); textView.setGravity(Gravity.CENTER); if (size == 1){ if(showTitle){ textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector); }else{ textView.setBackgroundResource(R.drawable.actionsheet_top_selector); } }else { if (showTitle){ if (i >= 1 && i < size) { textView.setBackgroundResource(R.drawable.actionsheet_middle_selector); } else { textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector); } }else { if (i == 1) { textView.setBackgroundResource(R.drawable.actionsheet_top_selector); } else if (i < size) { textView.setBackgroundResource(R.drawable.actionsheet_middle_selector); } else { textView.setBackgroundResource(R.drawable.actionsheet_bottom_selector); } } } //字体颜色 if (null != itemTextcolor){ textView.setTextColor(Color.parseColor(itemTextcolor.getName())); }else{ textView.setTextColor(Color.parseColor(SheetItemColor.BULE.getName())); } //高度 float scale = context.getResources().getDisplayMetrics().density; int height = (int) (45 * scale + 0.5f); textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height)); //点击事件 textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { listener.onClick(index); dialog.dismiss(); } }); lLayout_content.addView(textView); } } public interface OnSheetItemClickListener{ void onClick(int witch); } private class SheetItem{ String name; OnSheetItemClickListener listener; SheetItemColor color; public SheetItem(String name,SheetItemColor color,OnSheetItemClickListener listener) { this.name = name; this.listener = listener; this.color = color; } } public enum SheetItemColor{ BULE("#037BFF"),RED("#FD4A2E"); String name ; private SheetItemColor(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } }
自定义Dialog对应布局如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="8dp" > <TextView android:id="@+id/txt_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/actionsheet_top_normal" android:gravity="center" android:minHeight="45dp" android:paddingTop="10dp" android:paddingBottom="10dp" android:paddingLeft="15dp" android:paddingRight="15dp" android:textColor="@color/actionsheet_gray" android:textSize="13sp" android:visibility="gone" /> <ScrollView android:id="@+id/sLayout_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:fadingEdge="none" > <LinearLayout android:id="@+id/lLayout_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > </LinearLayout> </ScrollView> <TextView android:id="@+id/txt_cancel" android:layout_width="match_parent" android:layout_height="45dp" android:layout_marginTop="8dp" android:background="@drawable/actionsheet_single_selector" android:gravity="center" android:text="取消" android:textColor="@color/actionsheet_blue" android:textSize="18sp" /> </LinearLayout>
自定义Dialog 弹出和收回动画
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="200" android:fromYDelta="100%" android:toYDelta="0" />
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="200" android:fromYDelta="0" android:toYDelta="100%" />
自定义Dialog 条目的 点击背景selector就不给出了, 很简单点击一下背景变成浅灰色
以上就是所有代码了, 当然如果觉得自定义dialog麻烦, 完全可以用 popupwindow来代替, 事实上网上很多 类似代码都是用的 popupwindow做的.
需要注意的一点是: 如果要做图片上传操作, 需要加入sd卡 的读写权限.
顺便说2句文件上传吧 . 我在项目中采用的是 按照图片路径方式进行上传的, 因为接口写不出来 以流的方式上传,所以 安卓端 也只能按照路径上传了.但是由于我们最终剪切的图片转成了一个Bitmap对象,所以要想获取这个Bitmap对象所在的路径是不容易获取到的, 因为拍照路径我们存到了outFile中, 而从相册选择的路径位于 content://media/external/images/media "媒体库"中, 他们路径是不同的, 这就为上传带来麻烦了. 为什么相册选择图片位于这里呢, 这就是前面我说到的为了适应小米手机.
我的最终解决办法是: 把这个最终的 Bitmap对象 转成流存入到 sd卡中比如: Environment.getxxxsdpath+"/temp","temp.jpg" 这样无论是 拍照还是 相册选择的 ,最终他们的路径都变成了sd路径下的temp.jpg, 那么拿着这个路径就可以上传图片了.
尽管有点麻烦,但是解决了问题.