一.概述

android的 图片拍照 ,相册选图,以及图片剪切功能可以说非常常用. 尤其是图片上传功能,必然用到此功能. 而公司最近的一个项目中正好用到该功能. 记录下来以便以后再次用到,直接拿来使用.

在此之前,我也参考了网上很多代码示例, 写得都不错, 但是有一个问题可能大家都没发现, 当我参考网上示例写完后,发现 小米手机竟然不能使用该功能, 最后查了很多资料依然不能解决,最后猜测要么是 小米手机bug,要么就是 小米把android底层修改的过火了.

但是不管怎么样,必须要解决这个问题,毕竟用小米手机的人不在少数. 最后经人指点,终于搞定.

二.运行效果图

android 手机相册路径是哪里_ide

点击圆形头像弹出自定义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, 那么拿着这个路径就可以上传图片了.

尽管有点麻烦,但是解决了问题.