Android 摄像头拍照显示 相册显示 图片裁剪绘制显示

Android 专栏收录该内容
9 篇文章 0 订阅

声明: 学习这个之前, 建议先学习下内容提供器.

使用的是Android系统自带的裁剪功能.

本人真机(华为荣耀9 Android8.0)测试, 先上效果图(拍照和从相册选, 一样的效果):

.1. build.grandle 代码如下:

    引用了circleimageview和butterknife.

    使用butterknife的话, 需要添加javaCompileOptions选项, 如下所示:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "cwx.camerastudy"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath = true
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'de.hdodenhof:circleimageview:3.0.0'
    implementation 'com.jakewharton:butterknife:7.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

2. AndroidManifest.xml代码如下:

    注册了两个Activity和一个内容提供器.

    内容提供器中, "android: authorities", 自己定义. 我定义的是"包名."+ fileprovider. 这个属性在MainActivity中会用到.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cwx.camerastudy">

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.Read_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".Main2Activity">

        </activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="cwx.camerastudy.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    </application>

</manifest>

3. 在res目录下新建Directory, 命名xml. 新建XML Resource File, 命名file_paths.内容代码如下:

    path值为空, 表示可以访问整个sdcard目录.

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="" />
</paths>

4. 辅助类HelpUtils, 代码如下:

public class HelpUtils {
    public static final int TAKE_PHOTO= 1;
    public static final int CHOSE_PHOTO= 2;
    public static final int CLIP_PHOTO= 3;

    /**
     * 返回文件的在本地的真实路径
     * @param data
     * @return
     */
    public static String handleImageonKitKat(Activity activity, Intent data) {
        String imagePath= null;
        Uri myuri= data.getData();
        if (DocumentsContract.isDocumentUri(activity, myuri)) {
            String docId= DocumentsContract.getDocumentId(myuri);
            if ("com.android.providers.media.documents".equals(myuri.getAuthority())) {
                String id= docId.split(":")[1];
                String selection= MediaStore.Images.Media._ID+ "="+ id;
                imagePath= getImagePath(activity, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
            } else if ("com.android.providers.downloads.documents".equals(myuri.getAuthority())) {
                Uri contentUri= ContentUris.withAppendedId(Uri.parse("content://downloads/public_download"), Long.valueOf(docId));
                imagePath= getImagePath(activity, contentUri, null);
            }
        } else if ("content".equalsIgnoreCase(myuri.getScheme())) {
            imagePath= getImagePath(activity, myuri, null);
        } else if ("file".equalsIgnoreCase(myuri.getScheme())) {
            imagePath= myuri.getPath();
        }
        return imagePath;
    }

    /**
     * 返回文件的在本地的真实路径
     * @param data
     * @return
     */
    public static String handleImageBeforeKitKat(Activity activity, Intent data) {
        Uri uri= data.getData();
        String imagePath= getImagePath(activity, uri, null);
        return imagePath;
    }

    public static String getImagePath(Activity activity, Uri uri, String selection) {
        String path= null;
        Cursor cursor= activity.getContentResolver().query(uri, null, selection, null, null);
        if (cursor!= null) {
            if (cursor.moveToFirst()) {
                path= cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
            cursor.close();
        }
        return path;
    }

    /**
     * 打开相册
     */
    public static void openAlbum(Activity activity) {
        Intent intent= new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.KITKAT) {
            activity.startActivityForResult(intent, CHOSE_PHOTO);
        } else {
            activity.startActivityForResult(intent, CHOSE_PHOTO);
        }
    }

    /**
     * 打开摄像头
     */
    public static void openCamera(Activity activity, Uri uri) {
        Intent intent= new Intent("android.media.action.IMAGE_CAPTURE");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        activity.startActivityForResult(intent, TAKE_PHOTO);
    }

    /**
     * 剪裁绘制
     * @param uri
     */
    public static void photoClip(Activity activity, Uri uri) {
        // 调用系统中自带的图片剪裁
        Intent intent = new Intent("com.android.camera.action.CROP");
        if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }
        intent.setDataAndType(uri, "image/*");
        // 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
        intent.putExtra("crop", "true");
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // outputX outputY 是裁剪图片宽高
        intent.putExtra("outputX", 150);
        intent.putExtra("outputY", 150);
        intent.putExtra("return-data", true);
        activity.startActivityForResult(intent, CLIP_PHOTO);
    }

    /**
     * 保存图片
     */
    public static String saveImage(Activity activity, String name, Bitmap bitmap) {
        File appDir= new File(String.valueOf(activity.getExternalCacheDir()));
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        String fileName= name+ ".jpg";
        File file= new File(appDir, fileName);
        try {
            FileOutputStream fos= new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
            fos.close();
            return file.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

5. MainActivity代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Uri uri;

    @Bind(R.id.take_photo)
    Button btn_take;
    @Bind(R.id.chose_photo)
    Button btn_chose;
    @Bind(R.id.picture)
    CircleImageView picture;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        btn_take.setOnClickListener(this);
        btn_chose.setOnClickListener(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ButterKnife.unbind(this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        switch (requestCode) {
            case HelpUtils.TAKE_PHOTO:
                if (resultCode== RESULT_OK) {
                    try {
                        //因为uri是在缓存数据目录 不需要运行时权限处理
                        HelpUtils.photoClip(this, uri);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                break;
            case HelpUtils.CHOSE_PHOTO:
                String path;
                if (resultCode== RESULT_OK) {
                    Uri newUri= null;
                    //获取真实路径
                    if (Build.VERSION.SDK_INT>= 19) {
                        path= HelpUtils.handleImageonKitKat(this, data);
                    } else {
                        path= HelpUtils.handleImageBeforeKitKat(this, data);
                    }
                    //申城新的uri实例
                    if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) {
                        newUri = FileProvider.getUriForFile(this, "cwx.camerastudy.fileprovider", new File(path));
                    } else {
                        newUri= Uri.fromFile(new File(path));
                    }
                    //剪裁绘制
                    HelpUtils.photoClip(this, newUri);
                }
                break;
            case HelpUtils.CLIP_PHOTO:
                Bundle bundle= data.getExtras();
                if (bundle!= null) {
                    Bitmap bitmap= bundle.getParcelable("data");
                    picture.setImageBitmap(bitmap);
                    //为操作简单 保存图片到当前app缓存目录下
                    String strPath= HelpUtils.saveImage(this,"temp", bitmap);
                    Log.d("剪裁后图片路径: ", strPath);
                }
                break;
            default:
                break;
        }
    }

    /**
     * 权限请求回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length> 0 && grantResults[0]== PackageManager.PERMISSION_GRANTED) {
                    HelpUtils.openCamera(this, uri);
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            case 2:
                if (grantResults.length> 0 && grantResults[0]== PackageManager.PERMISSION_GRANTED) {
                    HelpUtils.openAlbum(this);
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.take_photo:
                //使用getExternalCacheDir() 获取当前应用缓存数据的位置
                File outoutImage= new File(getExternalCacheDir(), "output_image.jpg");
                try {
                    if (outoutImage.exists()) {
                        outoutImage.delete();
                    }
                    outoutImage.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //从Android 7.0开始 直接使用本地真实路径的Uri被认为不安全 此处作特殊处理
                if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) {
                    uri= FileProvider.getUriForFile(MainActivity.this, "cwx.camerastudy.fileprovider", outoutImage);
                } else {
                    uri= Uri.fromFile(outoutImage);
                }
                //验证摄像头权限 并打开摄像头
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.CAMERA}, 1);
                } else {
                    HelpUtils.openCamera(this, uri);
                }
                break;
            case R.id.chose_photo:
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
                } else {
                    HelpUtils.openAlbum(this);
                }
                break;
            default:
                break;
        }
    }
}

6. activity_main.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button android:id="@+id/take_photo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_margin="10dp"
        android:text="Take Photo" />

    <Button android:id="@+id/chose_photo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="10dp"
        android:text="Chose Photo" />

    <Button android:id="@+id/chose_"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_margin="10dp"
        android:text="Test" />

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/picture"
        android:layout_width="96dp"
        android:layout_height="96dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_launcher_background"
        app:civ_border_color="#999999"
        app:civ_border_width="2dp" />

</RelativeLayout>

代码总结完毕, 如果有不足或错误的地方非常欢迎指出来. 我也在学习中.

其实在写这个功能之前, 本人在"第一行代码"书里, 看到也有调用摄像头拍照并显示, 调用相册图片并显示的例子,本人照着书敲出来的, 希望对大家有所帮助:

在之前的基础上添加:

1. Main2Activity代码如下

public class Main2Activity extends AppCompatActivity {

    public static final int TAKE_PHOTO= 1;
    public static final int CHOSE_PHOTO= 2;
    private ImageView picture;
    private Uri uri;

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

        Button btn_take= (Button) findViewById(R.id.take_photo);
        Button btn_chose= (Button) findViewById(R.id.chose_photo);
        picture = (ImageView) findViewById(R.id.picture);
        btn_take.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                File outoutImage= new File(getExternalCacheDir(), "output_image.jpd");
                try {
                    if (outoutImage.exists()) {
                        outoutImage.delete();
                    }
                    outoutImage.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT>= 24) {
                    uri= FileProvider.getUriForFile(Main2Activity.this, "cwx.camerastudy.fileprovider", outoutImage);
                } else {
                    uri= Uri.fromFile(outoutImage);
                }
                Intent intent= new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                startActivityForResult(intent, TAKE_PHOTO);
            }
        });
        btn_chose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (ContextCompat.checkSelfPermission(Main2Activity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(Main2Activity.this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                } else {
                    openAlbum();
                }
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode== RESULT_OK) {
                    try {
                        Bitmap bitmap= BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case CHOSE_PHOTO:
                if (resultCode== RESULT_OK) {
                    if (Build.VERSION.SDK_INT>= 19) {
                        handleImageonKitKat(data);
                    } else {
                        handleImageBeforeKitKat(data);
                    }
                }
                break;
            default:
                break;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length> 0 && grantResults[0]== PackageManager.PERMISSION_GRANTED) {
                    openAlbum();
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }

    private void openAlbum() {
        Intent intent= new Intent("android.intent.action.GET_CONTENT");
        intent.setType("image/*");
        startActivityForResult(intent, CHOSE_PHOTO);
    }

    private void handleImageonKitKat(Intent data) {
        String imagePath= null;
        Uri myuri= data.getData();
        if (DocumentsContract.isDocumentUri(this, myuri)) {
            String docId= DocumentsContract.getDocumentId(myuri);
            if ("com.android.providers.media.documents".equals(myuri.getAuthority())) {
                String id= docId.split(":")[1];
                String selection= MediaStore.Images.Media._ID+ "="+ id;
                imagePath= getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
            } else if ("com.android.providers.downloads.documents".equals(myuri.getAuthority())) {
                Uri contentUri= ContentUris.withAppendedId(Uri.parse("content://downloads/public_download"), Long.valueOf(docId));
                imagePath= getImagePath(contentUri, null);
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            imagePath= getImagePath(myuri, null);
        } else if ("file".equalsIgnoreCase(myuri.getScheme())) {
            imagePath= myuri.getPath();
        }
        displayImage(imagePath);
    }

    private void handleImageBeforeKitKat(Intent data) {
        Uri uri= data.getData();
        String imagePath= getImagePath(uri, null);
        displayImage(imagePath);
    }

    private String getImagePath(Uri uri, String selection) {
        String path= null;
        Cursor cursor= getContentResolver().query(uri, null, selection, null, null);
        if (cursor!= null) {
            if (cursor.moveToFirst()) {
                path= cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
            cursor.close();
        }
        return path;
    }

    private void displayImage(String imagePath) {
        if (imagePath!= null) {
            Bitmap bitmap= BitmapFactory.decodeFile(imagePath);
            picture.setImageBitmap(bitmap);
            /**
             * 质量压缩
             * 质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,
             * 那么bitmap所占内存大小是不会变的。 quality,可以调节你压缩的比例
             * 质量压缩对png格式图片无效, png是无损压缩的.
             */
//            ByteArrayOutputStream baos= new ByteArrayOutputStream();
//            int quality= 2;
//            Bitmap bitmap= BitmapFactory.decodeFile(imagePath);
//            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
//            byte[] bytes= baos.toByteArray();
//            Bitmap bm= BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//            picture.setImageBitmap(bm);
            /**
             * 采样率压缩
             * 采样率压缩其原理是缩放bitamp的尺寸,通过调节其inSampleSize参数
             * 比如调节为2,宽高会为原来的1/2,内存变回原来的1/4
             */
//            BitmapFactory.Options options= new BitmapFactory.Options();
//            options.inSampleSize= 2;
//            Bitmap bitmap= BitmapFactory.decodeFile(imagePath, options);
//            picture.setImageBitmap(bitmap);
            /**
             * 缩放法压缩
             * 放缩法压缩使用的是通过矩阵对图片进行裁剪,也是通过缩放图片尺寸,来达到压缩图片的效果,和采样率的原理一样
             */
//            Matrix matrix= new Matrix();
//            matrix.setScale(0.5f, 0.5f);
//            Bitmap bitmap= BitmapFactory.decodeFile(imagePath);
//            Bitmap bm= Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
//            picture.setImageBitmap(bm);
            /**
             * RGB_565压缩
             * RGB_565压缩是通过改用内存占用更小的编码格式来达到压缩的效果
             * Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高
             * 如果对透明度没有要求,建议可以改成RGB_565,相比ARGB_8888将节省一半的内存开销
             */
//            BitmapFactory.Options options= new BitmapFactory.Options();
//            options.inPreferredConfig= Bitmap.Config.RGB_565;
//            Bitmap bitmap1= BitmapFactory.decodeFile(imagePath, options);
//            picture.setImageBitmap(bitmap);
            /**
             * createScaledbitmap
             * 将图片压缩成用户所期望的长度和宽度
             * 如果用户期望的长度和宽度和原图长度宽度相差太多的话,图片会很不清晰。
             */
//            Bitmap bitmap= BitmapFactory.decodeFile(imagePath);
//            Bitmap bm= Bitmap.createScaledBitmap(bitmap, 150, 150, true);
//            picture.setImageBitmap(bm);
        } else {
            Toast.makeText(this, "Failed to get image", Toast.LENGTH_SHORT).show();
        }
    }
}

2. activity_main2.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo" />

    <Button android:id="@+id/chose_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Chose Photo" />

    <ImageView android:id="@+id/picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>

</LinearLayout>

结束!

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值