Android

[Android] 카메라(Camera) 호출 후 이미지 크롭(Crop) 하기

poppy 2020. 12. 26. 19:30
반응형

1. 크롭 라이브러리 추가 - build.gradle의 dependencies

implementation 'com.soundcloud.android:android-crop:1.0.1@aar'

2. 권한 추가 - AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAG"/>
<uses-feature android:name="android.hardware.camera" android:required="true"/>

3. activity_main.xml 에 버튼과 이미지뷰 추가

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

    <ImageView
        android:id="@+id/iv_takepicture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"/>

    <Button
        android:id="@+id/btn_takepicture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dp"
        android:text="카메라"/>
    
</RelativeLayout>

4. MainActivity.java 작업하기

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    ImageView imageView;
    Button btnCamera;
    final static int TAKE_PICTURE = 1;
    final static int CROP_PICTURE = 2;
    private Uri pictureUri;

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

        imageView = (ImageView)findViewById(R.id.iv_takepicture);
        btnCamera = (Button)findViewById(R.id.btn_takepicture);

        btnCamera.setOnClickListener(this);

        //Uri exposure 무시
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());

        //권한 요청
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ //안드로이드 버전확인
            //권한 허용이 됐는지 확인
            if(checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {

            }
            else { //권한 허용 요청
                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if(grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults[2] == PackageManager.PERMISSION_GRANTED){

        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId())
        {
            // 버튼 눌렀을 때, 카메라에서 이미지 가져오기
            case R.id.btn_takepicture:
                Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                // 임시로 사용할 파일의 경로를 생성
                String url = "tmp_" + String.valueOf(System.currentTimeMillis()) + ".jpg";
                pictureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), url));

                cameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, pictureUri);
                cameraIntent.putExtra("return-data", true);
                startActivityForResult(cameraIntent, CROP_PICTURE);
                break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            //크롭된 이미지 가져와서 이미지뷰에 보여주기
            case TAKE_PICTURE:
                if (resultCode == RESULT_OK && data.hasExtra("data")) { //데이터를 가지고 있는지 확인
                    final Bundle extras = data.getExtras();

                    if(extras != null)
                    {
                        Bitmap photo = extras.getParcelable("data"); //크롭한 이미지 가져오기
                        imageView.setImageBitmap(photo); //이미지뷰에 넣기
                    }
                    
                    // 임시 파일 삭제
                    File f = new File(pictureUri.getPath());
                    if(f.exists())
                        f.delete();

                    break;
                }
                break;
                
            // 이미지 크롭
            case CROP_PICTURE: {
                // 이미지를 가져온 이후의 리사이즈할 이미지 크기를 결정합니다.
                // 이후에 이미지 크롭 어플리케이션을 호출하게 됩니다.

                Intent intent = new Intent("com.android.camera.action.CROP");
                intent.setDataAndType(pictureUri, "image/*");

                intent.putExtra("outputX", 200); //크롭한 이미지 x축 크기
                intent.putExtra("outputY", 200); //크롭한 이미지 y축 크기
                intent.putExtra("aspectX", 1); //크롭 박스의 x축 비율
                intent.putExtra("aspectY", 1); //크롭 박스의 y축 비율
                intent.putExtra("scale", true);
                intent.putExtra("return-data", true);
                startActivityForResult(intent, TAKE_PICTURE);

                break;
            }
        }
    }
}

 

SDK version이 24이상에서는 이런 오류가 발생할 수 있습니다.

android.os.FileUriExposedException: file:///storage/emulated/0/tmp_1608973420078.jpg exposed beyond app through ClipData.Item.getUri()

이 오류를 해결하기 위해서는 FileProvide를 사용하는 방법이 있습니다. FileProvide는 여러 가지로 복잡해서 여기서는 onCreate( )에 다음 코드를 추가해서 해결해주었습니다.

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

FileProvide를 사용하려면 다음 링크를 참조하면 도움이 될 것입니다.

stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed/55138155

 

android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()

The app is crashing when I'm trying to open a file. It works below Android Nougat, but on Android Nougat it crashes. It only crashes when I try to open a file from the SD card, not from the system

stackoverflow.com

실행화면

 

반응형