zoukankan      html  css  js  c++  java
  • Android 系统自带图片裁剪功能(适配7.0、8.0、对了还有小米手机)

    前段时间写了如何获取相册和拍照之后的照片并且进行显示和上传,这一次是如何进行圆形图像制作,经常看我写的笔记的人会知道,我很懒。那么我就懒的自定义了,目前需求就用原生的就好了,大神的轮子,我会在后面进行推荐。这篇笔记是依赖于:Android调用相册、相机(兼容6.0、7.0、8.0)
    文章撰写的,所以不会基本操作的,请先去看看那个。

    先上效果哈:
    系统自带的裁剪效果

    ##一、布局设置
    由于是圆形头像,所以,我们引用一个举世闻名的项目哈:CircleImageView

    <?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/btn_get_pic_form_photo_album"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_margin="10dp"
            android:text="调用相册" />

        <Button
            android:id="@+id/btn_get_Permission"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="10dp"
            android:text="动态权限申请" />

        <Button
            android:id="@+id/btn_get_pic_from_camera"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_margin="10dp"
            android:text="调用相机" />

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/iv_test"
            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>


    布局没有太大变化,就是把之前的ImageView修改为CircleImageView控件

    ##二、.java 文件
    这里增加了两个方法
    ######方法一

      private void photoClip(Uri uri) {
            // 调用系统中自带的图片剪裁
            Intent intent = new Intent("com.android.camera.action.CROP");
            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);
            startActivityForResult(intent, 3);
        }


    该方法,传入我们拿到的照片的 uri 进行激活 Android 系统的裁剪界面。我是在 onActivityResult 内进行调用该方法。

    ######方法二

      public String saveImage(String name, Bitmap bmp) {
            File appDir = new File(Environment.getExternalStorageDirectory().getPath());
            if (!appDir.exists()) {
                appDir.mkdir();
            }
            String fileName = name + ".jpg";
            File file = new File(appDir, fileName);
            try {
                FileOutputStream fos = new FileOutputStream(file);
                bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
                fos.flush();
                fos.close();
                return file.getAbsolutePath();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

    该方法 传入获取照片的 bitmap 和裁剪之后的照片名称,生成文件的保存路径,韩小呆将其保存在了本地的根目录了。

    完整代码

    package com.example.hxd.pictest;

    import android.Manifest;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.SystemClock;
    import android.provider.MediaStore;
    import android.support.annotation.NonNull;
    import android.support.v4.content.FileProvider;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.Toast;

    import com.bumptech.glide.Glide;

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.util.List;
    import java.util.Objects;
    import java.util.logging.Logger;

    import de.hdodenhof.circleimageview.CircleImageView;
    import pub.devrel.easypermissions.EasyPermissions;

    public class MainActivity extends AppCompatActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks {

        private CircleImageView ivTest;

        private File cameraSavePath;//拍照照片路径
        private Uri uri;
        private String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};

        private String photoName = System.currentTimeMillis() + ".jpg";


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

            Button btnGetPicFromCamera = findViewById(R.id.btn_get_pic_from_camera);
            Button btnGetPicFromPhotoAlbum = findViewById(R.id.btn_get_pic_form_photo_album);
            Button btnGetPermission = findViewById(R.id.btn_get_Permission);
            ivTest = findViewById(R.id.iv_test);

            btnGetPicFromCamera.setOnClickListener(this);
            btnGetPicFromPhotoAlbum.setOnClickListener(this);
            btnGetPermission.setOnClickListener(this);

            cameraSavePath = new File(Environment.getExternalStorageDirectory().getPath() + "/" + photoName);

        }

        @Override
        public void onClick(View v) {
            int id = v.getId();
            switch (id) {
                case R.id.btn_get_pic_from_camera:
                    goCamera();
                    break;
                case R.id.btn_get_pic_form_photo_album:
                    goPhotoAlbum();
                    break;
                case R.id.btn_get_Permission:
                    getPermission();
                    break;
            }
        }

        //获取权限
        private void getPermission() {
            if (EasyPermissions.hasPermissions(this, permissions)) {
                //已经打开权限
                Toast.makeText(this, "已经申请相关权限", Toast.LENGTH_SHORT).show();
            } else {
                //没有打开相关权限、申请权限
                EasyPermissions.requestPermissions(this, "需要获取您的相册、照相使用权限", 1, permissions);
            }

        }


        private void photoClip(Uri uri) {
            // 调用系统中自带的图片剪裁
            Intent intent = new Intent("com.android.camera.action.CROP");
            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);
            startActivityForResult(intent, 3);


        }


        //激活相册操作
        private void goPhotoAlbum() {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_PICK);
            intent.setType("image/*");
            startActivityForResult(intent, 2);
        }

        //激活相机操作
        private void goCamera() {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                uri = FileProvider.getUriForFile(MainActivity.this, "com.example.hxd.pictest.fileprovider", cameraSavePath);
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            } else {
                uri = Uri.fromFile(cameraSavePath);
            }
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            MainActivity.this.startActivityForResult(intent, 1);
        }


        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            //框架要求必须这么写
            EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
        }


        //成功打开权限
        @Override
        public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {

            Toast.makeText(this, "相关权限获取成功", Toast.LENGTH_SHORT).show();
        }

        //用户未同意权限
        @Override
        public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
            Toast.makeText(this, "请同意相关权限,否则功能无法使用", Toast.LENGTH_SHORT).show();
        }

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            String photoPath;
            if (requestCode == 1 && resultCode == RESULT_OK) {

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    photoPath = String.valueOf(cameraSavePath);
                    photoClip(Uri.fromFile(cameraSavePath));
                } else {
                    photoPath = uri.getEncodedPath();
                    photoClip(uri);
                }
                Log.d("拍照返回图片路径:", photoPath);
                Glide.with(MainActivity.this).load(photoPath).into(ivTest);
            } else if (requestCode == 2 && resultCode == RESULT_OK) {
                photoPath = getPhotoFromPhotoAlbum.getRealPathFromUri(this, data.getData());
                Log.d("相册返回图片路径:", photoPath);
                photoClip(data.getData());
                Glide.with(MainActivity.this).load(photoPath).into(ivTest);
            } else if (requestCode == 3 && resultCode == RESULT_OK) {
                Bundle bundle = data.getExtras();

                if (bundle != null) {
                    //在这里获得了剪裁后的Bitmap对象,可以用于上传
                    Bitmap image = bundle.getParcelable("data");
                    //设置到ImageView上
                    ivTest.setImageBitmap(image);
                    //也可以进行一些保存、压缩等操作后上传
                    String path = saveImage("头像", image);
                    Log.d("裁剪路径:", path);
                }


            }

            super.onActivityResult(requestCode, resultCode, data);
        }

        public String saveImage(String name, Bitmap bmp) {
            File appDir = new File(Environment.getExternalStorageDirectory().getPath());
            if (!appDir.exists()) {
                appDir.mkdir();
            }
            String fileName = name + ".jpg";
            File file = new File(appDir, fileName);
            try {
                FileOutputStream fos = new FileOutputStream(file);
                bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
                fos.flush();
                fos.close();
                return file.getAbsolutePath();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    当代码写成了这样,我以为就可以完事了,但是后来拿出来我的小米4和小米8se 测试的时候,我就懵逼了,出现了如下结果:
    bug
    描述一下:
    1、相册选择照片之后,只要进行裁剪就会出现“保存时发生错误,保存失败”;
    2、就是拍照之后进行裁剪无效果。

    #三、解决bug
    ###1、将 photoClip() 方法内添加如下代码,其他代码不进行变动

    uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + System.currentTimeMillis() + ".jpg");
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
            intent.putExtra("noFaceDetection", true);

    这里的 uritempFile 是个 Uri 。

    ###2、将裁剪的返回成功的返回方法内添加如下代码:

      Picasso.with(EditMyInfoActivity.this).load(uritempFile).into(ivHead);
                File file = null;
                try {
                    file = new File(new URI(uritempFile.toString()));
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                }
                //照片路径
                String path = Objects.requireNonNull(file).getPath();


    然后,就可以完美的运行了。
    ##三、推荐一篇自定义裁剪的博文
    Android 高仿微信头像截取 打造不一样的自定义控件----作者: 家喻户晓的 张鸿洋

    ##四、推荐几款裁剪框架
    1、uCrop—薄荷,哔哩哔哩 在使用

    uCrop

    2、android-crop—网易新闻,芒果TV 在使用
    android-crop

    3、PhotoCrop—快手 使用
    PhotoCrop

    4、CropBitmap—>出自一位牛x的群友

  • 相关阅读:
    整理SVN代码-->正式环境的代码
    业务(1)
    在一个项目中跨领域调用接口的的实现
    一个java文件编译之后会产生多个class文件
    shutil模块
    shevle模块
    confiparser模块
    sys模块
    subprocess模块
    【ADO.NET】3、从TXT中导入数据到数据库
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/11988462.html
Copyright © 2011-2022 走看看