Before 2022/Android

안드로이드 갤러리에서 이미지 선택 후 보여주는 방법(Glide)

Eljoe 2018. 8. 28. 14:32

먼저 TedPermission(권한 관리 라이브러리), Glide, 권한을 각각 등록해준다.

// app gradle
implementation 'gun0912.ted:tedpermission:2.2.1'
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

// manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

동기화가 완료되었으면 main activity에 Tedpermission을 불러와 권한 체크를 한다.

// onCreate
TedPermission.with(this)
                .setPermissionListener(permissionListener)
                .setDeniedMessage("권한이 거부되었습니다. 사용을 원하시면 설정에서 해당 권한을 직접 허용해주세요.")
                .setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .check();

// Listner
PermissionListener permissionListener = new PermissionListener() {
        @Override
        public void onPermissionGranted() {
           // 권한 승낙 후 로직 작성 부분
        }

        @Override
        public void onPermissionDenied(List<String> deniedPermissions) {
            Toast.makeText(SmartIdActivity.this, "권한이 승인되지 않은 경우, 예기치 않은 오류가 발생할 수 있습니다.", Toast.LENGTH_LONG).show();
        }
    };

사진을 불러와서 보여줄 로직을 수행할 액티비티에서 버튼, 이미지뷰를 불러온 후 다음과 같은 메소드들을 등록한다.

// requestCode
private static final int PROFILE_PICTURE = 0;

// file path
private String filePath;

// onCreate
filePath = SharedUtil.getString(getApplicationContext(), "imgPath");

if (!TextUtils.isEmpty(filePath)) {
    Glide.with(this).asBitmap().load(filePath).into(mIvProfile);
} else {
    // 파일 경로가 없는 경우, 보여줄 기본 이미지
    Glide.with(this).load(R.drawable.no_profile).into(mIvProfile);
}

mBtnReg.setOnClickListener(v -> selectGallery());

// methods
public void selectGallery() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); // 외부 저장소에 있는 이미지 파일들에 대한 모든 정보를 얻을 수 있다.
    intent.setType("image/*"); // image/* 형식의 Type 호출 -> 파일을 열 수 있는 앱들이 나열된다.
    startActivityForResult(intent, PROFILE_PICTURE);
}

public void getPic(Uri imgUri) {
    String imgPath = getRealPathFromURI(imgUri);
    // SharedPreferences 관리 Class : 해당 내용은 블로그에 있으니 참조
    SharedUtil.putString(getApplicationContext(), "imgPath", imgPath);
    Glide.with(this).asBitmap().load(imgPath).into(mIvProfile);
}

public String getRealPathFromURI(Uri uri) {
    int index = 0;
    String[] proj = {MediaStore.Images.Media.DATA};

    // 이미지 경로로 해당 이미지에 대한 정보를 가지고 있는 cursor 호출
    Cursor cursor = getContentResolver().query(uri, proj, null, null, null);

    // 데이터가 있으면(가장 처음에 위치한 레코드를 가리킴)
    if (cursor.moveToFirst()) {
        // 해당 필드의 인덱스를 반환하고, 존재하지 않을 경우 예외를 발생시킨다.
         index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    }

    Log.d("getRealPathFromURI", "getRealPathFromURI: " + cursor.getString(index));
    
    return cursor.getString(index);
}

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

    switch (requestCode) {
        case PROFILE_PICTURE:
            if (data != null) {
                getPic(data.getData());
            } else {
                return;
            }
        break;
    }
}


2019. 01. 09 수정 사항


cursor에 대한 Null Check를 수행하지 않아 NPE가 발생되어 앱이 Crash되는 현상이 발생하였다.

cursor의 Null Check와 close를 꼭 처리하자.


*getRealPathFromURI 메소드는 삭제하고 다음과 같이 진행한다.

public void selectGallery(Activity activity) {
	Intent intent = new Intent(Intent.ACTION_PICK);
	intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
	intent.setType("image/*");
	activity.startActivityForResult(intent, PROFILE_PICTURE);
}

//MVP로 리팩토링함(이전 소스와 view 메소드 이름을 참고해서 수정하면 된다)
private void getPicture(Context context, Uri uri){
	int index = 0;

	String[] proj = {MediaStore.Images.Media.DATA};

	Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);

	if(cursor == null){
		view.showToast("사진이 없습니다.");
		view.setInitProfile();
	} else if (cursor.moveToFirst()) {
		index = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
		String imgPath = cursor.getString(index);
		setData(context, "imgPath", imgPath);
		view.setProfile(imgPath);
		Log.d("realPathFromURI", "realPathFromURI: " + imgPath);
		cursor.close();
	}else{
		view.showToast("커서가 비었습니다.");
		view.setInitProfile();
		cursor.close();
	}
}


2020. 08. 07 수정 사항


Q(=29) 버전부터는 외부저장소에 Scoped Storage 개념이 적용되어 외부 저장소의 파일을 읽거나 쓰는 기능에 제한을 두게 되었다.


이에 대응한 게시글의 링크를 첨부한다.