安卓不同机型和API版本中拍照和相册选取照片功能实现

该demo针对不同机型和不同API进行功能实现,满足大部分手机拍照和相册选取照片功能实现。
1使用知乎相册框架compile 'com.zhihu.android:matisse:0.4.3'进行图库相册选取
2使用compile 'com.github.bumptech.glide:glide:3.7.0'框架(Glide)进行图片加载


如下效果

1点击选择图片按钮弹出popuwindow


01.png

2拍照:


02.png

3相册选取:


03.png

4选择的结果:
04.png

SelectPicPopup的代码:

package study.lzl.photoselectdemo;

import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.PopupWindow;

/**
 * @author: Created by lzl on 2017/8/14.
 * @function:
 * @description:
 */

public class SelectPicPopup implements View.OnClickListener {
    /**
     * UI
     */
    private PopupWindow mPopupWindow;
    private Button mCameraBtn;//相册
    private Button mPicBtn;//图库
    private Button mCancelBtn;//取消
    private Context mContext;//上下文环境对象

    private OnBtnClickListener click;

    public PopupWindow getmPopupWindow() {
        return mPopupWindow;
    }

    /**
     * 单参构造
     * @param mContext
     */
    public SelectPicPopup(Context mContext) {
        this.mContext = mContext;
        mPopupWindow=new PopupWindow(mContext);
        mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
        mPopupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT);//宽度充满父窗体
        mPopupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);//高度包裹内容
        mPopupWindow.setTouchable(true);//设置可以被触摸
        mPopupWindow.setFocusable(true);//设置占据焦点
        mPopupWindow.setOutsideTouchable(true);//设置可以点击外部区域(点击外部区域将消失)
        mPopupWindow.setContentView(initView());
        mPopupWindow.setAnimationStyle(R.style.AnimBottom);//设置转场动画
        mPopupWindow.getContentView().setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                mPopupWindow.setFocusable(false);
                dismiss();
                return false;
            }
        });
    }

    private View initView() {
        View view= LayoutInflater.from(mContext).inflate(R.layout.layout_popup,null);
        mCameraBtn= (Button) view.findViewById(R.id.camera_btn);
        mCameraBtn.setOnClickListener(this);

        mPicBtn= (Button) view.findViewById(R.id.pic_btn);
        mPicBtn.setOnClickListener(this);
        mCancelBtn= (Button) view.findViewById(R.id.cancel_btn);
        mCancelBtn.setOnClickListener(this);
        return view;
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.camera_btn:
                click.getFlag(0);
                break;
            case R.id.pic_btn:
                click.getFlag(1);
                break;
            case R.id.cancel_btn:
                dismiss();
                break;
        }
    }

    protected void dismiss(){
        if (mPopupWindow!=null&&mPopupWindow.isShowing()){
            mPopupWindow.dismiss();
        }
    }

    /**
     *
     * @param rootView
     */
    protected void showPopup(View rootView){
        mPopupWindow.showAtLocation(rootView, Gravity.BOTTOM,0,0);
    }

    protected interface OnBtnClickListener{
        void getFlag(int flag);
    }

    protected void setOnBtnClick(OnBtnClickListener click){
        this.click=click;
    }
}

自定义的popuwindow使用的style如下:

<!--popuwindow style-->
    <style name="AnimBottom" parent="@android:style/Animation">
        <item name="android:windowEnterAnimation">@anim/popup_alpha_out</item>
        <item name="android:windowExitAnimation">@anim/popup_alpha_in</item>
    </style>

引用的两个anim文件:
1anim/popup_alpha_out:

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromAlpha="0.0"
    android:toAlpha="1.0"
    >

</alpha>

2anim/popup_alpha_in:

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromAlpha="1.0"
    android:toAlpha="0.0"
    >

</alpha>

布局文件:layout_popup.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/half_transparent_black">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_alignParentBottom="true"
        android:background="@color/page_bg_color">
        <Button
            android:id="@+id/camera_btn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#ffffff"
            android:text="拍照"
            android:textColor="#000"
            />
        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="#65000000"/>
        <Button
            android:id="@+id/pic_btn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="相册"
            android:textColor="#000"
            android:background="#ffffff"/>
        <Button
            android:id="@+id/cancel_btn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_marginTop="10dp"
            android:background="#ffffffff"
            android:textColor="#000"
            android:text="取消"/>
    </LinearLayout>
</RelativeLayout>

MainActivity的代码:

package study.lzl.photoselectdemo;

import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.zhihu.matisse.Matisse;
import com.zhihu.matisse.MimeType;
import com.zhihu.matisse.engine.impl.GlideEngine;

import java.io.File;
import java.util.List;

/**
 * 演示图片选择来源,并将图片正确显示在控件上,该控件可以对图片进行手势操作:包括大小缩放和平移拖拽
 */
public class MainActivity extends AppCompatActivity {
    private ImageView imageV;
    private Uri imageUrl;//照片地址uri对象
    private int destType = FileHelper.JPEG;
    private static final int REQUEST_CODE_TAKE_PIC_CAMERA = 100;//拍照请求码

    private static final int REQUEST_CODE_CHOOSE = 233;//图库选取照片请求码

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        imageV= (ImageView) findViewById(R.id.image);
        findViewById(R.id.select_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final SelectPicPopup popup=new SelectPicPopup(getApplicationContext());
                popup.showPopup(findViewById(R.id.activity_main));
                popup.setOnBtnClick(new SelectPicPopup.OnBtnClickListener() {
                    @Override
                    public void getFlag(int flag) {
                        if (flag==1){//相册
                            popup.dismiss();
                            selectPicFromGallery();
                        }else if (flag==0){//拍照
                            popup.dismiss();
                            if (hasPermission(PerMissionContants.HARDWEAR_CAMERA_PERMISSION)){
                                openCamera();
                            }else {
                                requestPermission(PerMissionContants.HARDWEAR_CAMERA_CODE,PerMissionContants.HARDWEAR_CAMERA_PERMISSION);
                                Toast.makeText(getApplicationContext(),"权限:"+hasPermission(PerMissionContants.HARDWEAR_CAMERA_PERMISSION),Toast.LENGTH_SHORT).show();
                            }
                        }
                    }
                });
            }
        });
    }

    /**
     * 打开相册进行拍照
     */
    private void openCamera() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        //创建一个临时文件夹存储拍摄的照片
        File file = FileHelper.createFileByType(getApplicationContext(), destType, System.currentTimeMillis()+"");
        imageUrl=Uri.fromFile(file);//解析出uri对象
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//版本过高的拍照路径
            Log.e("进入1","进入1");
            file = new File(getApplicationContext().getCacheDir(), System.currentTimeMillis()+".jpg");
            imageUrl = Uri.fromFile(file);

            Toast.makeText(getApplicationContext(), "TODO", Toast.LENGTH_SHORT).show();
            // 将文件转换成content://Uri的形式
            Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", file);
            // 申请临时访问权限
            takePictureIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);

        } else {
            Log.e("进入2","进入2");
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUrl);
        }


        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            Log.e("进入3","进入3");
            startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PIC_CAMERA);
        }
    }

    /**
     * 打开相册选取照片
     */
    private void selectPicFromGallery() {
        Matisse.from(MainActivity.this)
                .choose(MimeType.allOf())
                .countable(true)
                .spanCount(4)
                .maxSelectable(1)
                .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
                .thumbnailScale(0.85f)
                .imageEngine(new GlideEngine())
                .forResult(REQUEST_CODE_CHOOSE);
    }

    /**
     * 发起权限请求
     */
    private void requestPermission(int code,String...permissions){
        if (Build.VERSION.SDK_INT>=23){//判断是否大于6.0版本(当前手机的安卓系统是否是6.0或以上)
            requestPermissions(permissions,code);
        }
    }

    /**
     * 校验权限是否通过(安卓6.0以上的动态权限需要调用该方法)
     * @param permissions
     * @return
     */
    private boolean hasPermission(String...permissions){
        for (String permission:permissions) {
            if (ContextCompat.checkSelfPermission(getApplicationContext(),permission)!= PackageManager.PERMISSION_GRANTED){
                return false;
            }
        }
        return true;
    }

    /**
     * 权限请求回调结果处理
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode==PerMissionContants.HARDWEAR_CAMERA_CODE){//拍照权限
            if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                Toast.makeText(getApplicationContext(),"权限请求结果",Toast.LENGTH_SHORT).show();
                openCamera();
            }

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==REQUEST_CODE_TAKE_PIC_CAMERA&&resultCode==RESULT_OK){//拍照请求回调成功
            if (imageUrl!=null){
                //根据uri获取图片的真实路径
                String path=getRealFilePath(getApplicationContext(),imageUrl);
                Bitmap bitmap=BitmapFactory.decodeFile(path);//根据图片的路径字符串解析成bitmap
                imageV.setImageBitmap(bitmap);
            }
        }else if (requestCode==REQUEST_CODE_CHOOSE&&resultCode==RESULT_OK){
            List<Uri> uriList=Matisse.obtainResult(data);
            if (uriList.size()==1){
                String path=getRealFilePath(getApplicationContext(),uriList.get(0));
                Bitmap bitmap=BitmapFactory.decodeFile(path);//根据图片的路径字符串解析成bitmap
                imageV.setImageBitmap(bitmap);
            }
        }
    }

    /**
     * 该方法根据图片uri得出其真实路径
     * @param context 上下文
     * @param uri 图片对应的uri对象
     * @return 图片的真实路径字符串
     */
    public String getRealFilePath(final Context context, final Uri uri) {
        if (null == uri) return null;
        final String scheme = uri.getScheme();
        String data = null;
        if (scheme == null)
            data = uri.getPath();
        else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
            data = uri.getPath();
        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
            Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
            if (null != cursor) {
                if (cursor.moveToFirst()) {
                    int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                    if (index > -1) {
                        data = cursor.getString(index);
                    }
                }
                cursor.close();
            }
        }
        return data;
    }
}

布局文件:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="study.lzl.photoselectdemo.MainActivity">

    <Button
        android:id="@+id/select_btn"
        android:background="@color/colorAccent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="选择照片"
        android:textColor="@color/colorPrimary"/>

    <ImageView
        android:id="@+id/image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"/>

</RelativeLayout>


动态权限常量类:PerMissionContants

package study.lzl.photoselectdemo;

import android.Manifest;

/**
 * @author: Created by lzl on 2017/8/15.
 * @function:
 * @description:
 */

public class PerMissionContants {
    /**
     * 拍照权限码
     */
    public static final int HARDWEAR_CAMERA_CODE=0x01;
    /**
     * 拍照用到的权限字符串数组:1打开数组的权限  2文件的写入权限(拍照要缓存) 3所拍照片的获取(从本地缓存或者相册中获取)
     */
    public static final String[] HARDWEAR_CAMERA_PERMISSION=new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};
}


文件操作助手类:FileHelper

package study.lzl.photoselectdemo;

import android.content.Context;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by rookie on 2017/2/28.
 */

public class FileHelper {
    public static final int JPEG = 1;
    static final int PNG = 0;


    /**
     * 返回系统缓存路径
     *
     * @param mContext
     * @return
     */
    public static String getTempDirectoryPath(Context mContext) {
        File cachePath;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            cachePath = mContext.getExternalCacheDir();
        } else {
            cachePath = mContext.getCacheDir();
        }
        cachePath.mkdirs();
        return cachePath.getAbsolutePath();
    }

    /**
     * 在系统缓存中创建一个文件
     *
     * @param mContext
     * @param type
     * @param name
     * @return
     */
    public static File createFileByType(Context mContext, int type, String name) {
        if (TextUtils.isEmpty(name)) {
            name = ".pic";
        }

        switch (type) {
            case JPEG:
                name = name + ".jpg";
                break;
            case PNG:
                name = name + ".png";
                break;
            default:
                break;

        }
        return new File(getTempDirectoryPath(mContext), name);

    }

    /**
     * Removes the "file://" prefix from the given URI string, if applicable.
     * If the given URI string doesn't have a "file://" prefix, it is returned unchanged.
     *
     * @param uriString the URI string to operate on
     * @return a path without the "file://" prefix
     */
    public static String stripFileProtocol(String uriString) {
        if (uriString.startsWith("file://")) {
            uriString = uriString.substring(7);
        }
        return uriString;
    }

    public static String getPicutresPath(int encodingType) {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "IMG_" + timeStamp + (encodingType == JPEG ? ".jpg" : ".png");
        File storageDir = Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM);
        String galleryPath = storageDir.getAbsolutePath() + File.separator + "Camera" + File.separator + imageFileName;
        return galleryPath;
    }

    public static boolean copyResultToGalley(Context context, Uri originUri, Uri galleryUri) {
        boolean result = false;
        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            inputStream = context.getContentResolver().openInputStream(originUri);
            outputStream = context.getContentResolver().openOutputStream(galleryUri);

            byte[] buffer = new byte[2048];

            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
            outputStream.flush();
            result = true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
}

***

注意事项:
1在app.gradle配置文件中配置使用到的两个框架:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    testCompile 'junit:junit:4.12'
    //项目中需要用到的第三方框架
    compile 'com.zhihu.android:matisse:0.4.3'
    compile 'com.github.bumptech.glide:glide:3.7.0'
}

2安卓6.0以上版本使用的是动态权限管理:因此增加一个类进行权限的申请处理:PerMissionContants

推荐阅读更多精彩内容