일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 앱에 광고달기
- android notification
- android 영단어 기능 만들기
- android 뒤집히는 카드뷰
- android 수익
- 불그리레시피
- android 터치시 뒤집히는 뷰
- android 광고달기
- firebase RemoteConfig
- 안드로이드
- FlipView
- 정국라면
- Android
- RecyclerView in Admob
- 안드로이드 뒤집히는 뷰
- 앱에 광고 수익
- android remoteconfig
- Android AdMob
- 라면레시피추천
- 국제우편물
- 테러우편물
- 안드로이드 리스트뷰와 광고
- 정국라면레시피
- android kotlin
- 안드로이드 광고
- android 앱업데이트 없이 변경하기
- 애드몹 설정
- 우편물재난문자
- kotlin
- 앱 광고 설정
- Today
- Total
TAE
[Android/Kotlin] 커스텀 앨범 만들기 - 커스텀 갤러리(2) 본문
안드로이드 앱 개발을 하다 보면 앨범에서 사진 선택 등 앨범에 접근하는 기능이 필요합니다.
기본 갤러리에 접근하여 미디어 파일을 가져올 수도 있지만 UI/UX 변경이 불가능합니다.
내부 저장소에서 미디어 파일을 가지고 와서 앨범을 커스텀을 하게 되면 디자인 변경이 가능하여 커스텀 앨범을 만들어 보겠습니다.
추가로 커스텀 작업을 하면 최초 앨범 선택 하는 팝업 없이 바로 커스텀 앨범 띄우는 것도 가능합니다
실행동작
코드
미디어 파일에 접근하려면 저장소 권한을 승인 작업이 필요합니다
권한 승인 관련 포스팅은 아래에서 확인 가능합니다
2023.03.16 - [android/코드] - [Android/Kotlin] 갤러리 접근 권한(Premission) 설정하기 - 커스텀 갤러리(1)
접근 권한 작업을 마쳤으면 우선 MediaStore에서 파일을 가져오는 작업을 하여야 한다.
MediaStore
* CustomAlbumActivity.kt
@SuppressLint("Range")
private fun initData() {
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.MEDIA_TYPE,
MediaStore.Files.FileColumns.DURATION
)
var selection: String?
val queryUri = MediaStore.Files.getContentUri("external")
selection =
(MediaStore.Files.FileColumns.MEDIA_TYPE + "=" + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+ " OR " + MediaStore.Files.FileColumns.MEDIA_TYPE + "=" + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
//이런 방법도 있다~
// cursorLoader = CursorLoader(
// this@CustomAlbumActivity,
// queryUri,
// projection,
// selection,
// null, // Selection args (none).
// MediaStore.Files.FileColumns.DATE_ADDED + " DESC" // Sort order.
// )
// cursor = cursorLoader.loadInBackground()
var cursor: Cursor? = this@CustomAlbumActivity.contentResolver.query(
queryUri,
projection,
selection,
null,
MediaStore.Files.FileColumns.DATE_ADDED + " DESC"
)
if (cursor != null && cursor.moveToFirst()) {
do {
val data = ItemGallery()
data.id =
cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID))
data.mediaType =
cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns.MEDIA_TYPE))
data.duration =
cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns.DURATION))
if (data.mediaType == 1) {
data.mediaData = "content://media/external/images/media/" + data.id
} else if (data.mediaType == 3) {
data.mediaData = "content://media/external/video/media/${data.id}"
}
listOfPhotos.add(data)
} while (cursor.moveToNext())
}
cursor!!.close()
customAlbumAdapter.setData(listOfPhotos)
}
미디어 스토리지에 저장 시간을 내림차순(DESC)으로 정렬하여, 미디어 아이디와, 미디어 타입, 영상 지속시간?(영상 시간) 을 가져와서 listOfPhotos 리스트에 add 해준 후 Adapter에 연결하여 줍니다.
미디어 타입이 1일 경우 : 이미지
미디어 타입이 3일 경우 : 비디오
로 구분하여 data에 mediaData를 추가하여 어뎁터에서 타입에 맞는 url을 이용하여 이미지를 보여줍니다.
MeidaStore란?
- 안드로이드 시스템에서 제공하는 미디어 데이터 DB
- 미디어 파일(이미지, 동영상)을 쿼리문을 사용하여 사용가능
MediaStore.Files
- 파일을 다루기 위해 추가된 API
- getCountUri() 메서드를 통하여 쿼리 할 Uri를 얻을 수 있으며, 인자로는 Internal, external을 넘겨줘야 하며, 이 값으로 스토리지 영역을 구분한다
MediaStore.Files.FileColumns
- 미디어 DB에서 파일과 관련된 칼럼을 나타내는 상수값이 정의된 인터페이스
* https://developer.android.com/reference/android/provider/MediaStore.Files.FileColumns
RecyceView Adapter연결
customAlbumAdapter = CustomAlbumAdapter(listOfPhotos)
binding.photoRV.adapter = customAlbumAdapter
Adapter구성
* CustomAlbumAdapter.kt
class CustomAlbumAdapter(private var itemList: ArrayList<ItemGallery>) :
RecyclerView.Adapter<CustomAlbumAdapter.MyViewHolder>() {
inner class MyViewHolder(val binding: ItemPhotoBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: ItemGallery) {
binding.tag = item
binding.layoutCL.setOnClickListener {
item.isSelected = !binding.selectRatioBT.isChecked
binding.selectRatioBT.isChecked = item.isSelected
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(parent.context)
val listItemBinding = ItemPhotoBinding.inflate(inflater, parent, false)
return MyViewHolder(listItemBinding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(itemList[position])
}
override fun getItemCount(): Int {
return itemList.size
}
fun setData(itemList: ArrayList<ItemGallery>) {
this.itemList = itemList
notifyDataSetChanged()
}
}
CustomAlbumActivity에서 가져온 스토리지 데이터를 adapter에 연결 하여 줍니다.
layout을 클릭하게 되면 isSelected를 변경하며, UI 구성을 하였습니다
UI 구성을 DataBinding을 활용하였으며, DataBinding에 포스팅은 따로 하도록 하겠습니다.
Data Class
*ItemGallery.kt
class ItemGallery {
var mediaData: String = ""
var id: Long = -1
var isSelected = false
var mediaType: Int// 사진: 1, 동영상: 3
var duration: Int// 동영상 재생시간
companion object {
@JvmStatic
@BindingAdapter("imageUrl")
fun loadImage(view: ImageView, url: String) {
Glide.with(view.context).load(url).into(view)
}
@JvmStatic
@BindingAdapter("duration")
fun setDuration(view: TextView, duration: Long) {
if (duration == 0L) {
view.visibility = View.GONE
} else {
view.visibility = View.VISIBLE
view.text = Utils.getPrettyDuration(duration)
}
}
@JvmStatic
@BindingAdapter("radioButton")
fun setRadioButton(view: RadioButton, isSelected: Boolean) {
view.isChecked = isSelected
}
}
constructor() {
mediaData = ""
id = -1
isSelected = false
mediaType = -1
duration = 0
}
}
DataBinding을 활용하기 위하여 BindingAdapter 을 사용하였습니다.
사용법도 따로 포스팅 하도록 하겠습니다
layout xml
*activity_custom_album.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/headerLL"
android:layout_width="0dp"
android:layout_height="48dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/closeTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:src="@drawable/ic_baseline_arrow_back_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/headerTitleTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:text="커스텀 앨범"
android:textColor="@color/black"
android:textSize="18dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/confirmTV"
app:layout_constraintStart_toEndOf="@id/closeTV"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/confirmTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:background="@android:color/transparent"
android:text="확인"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/imageV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/headerLL"
android:src="@mipmap/ic_launcher_round"
android:visibility="gone"
/>
<androidx.recyclerview.widget.RecyclerView
android:paddingTop="10dp"
android:id="@+id/photoRV"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="visible"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/headerLL"
app:spanCount="3"
tools:listitem="@layout/item_photo" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
* item_photo.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="tag"
type="com.example.videoselectthumbnail.ItemGallery" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:id="@+id/layoutCL"
android:layout_height="150dp">
<ImageView
android:id="@+id/iv_profile"
imageUrl="@{tag.mediaData}"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/durationTV"
duration="@{tag.duration}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp"
android:background="#B3252525"
android:drawableLeft="@drawable/ic_baseline_play_arrow_24"
android:drawablePadding="2dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:shadowColor="#66000000"
android:shadowRadius="4.0"
android:textColor="@color/white"
android:textSize="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
tools:text="0:00" />
<RadioButton
android:id="@+id/selectRatioBT"
android:layout_width="wrap_content"
android:minWidth="0dp"
android:minHeight="0dp"
radioButton="@{tag.selected}"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
이상 안드로이드 코틀린 커스텀 앨범 만들기 포스팅이었습니다.
궁금하신 점이나 잘못된 게 있다면 댓글 달아주시면 감사하겠습니다!
'android > 코드' 카테고리의 다른 글
[android/Kotlin] 푸시 메시지 구현 - firebase 프로젝트 만들기 (0) | 2023.03.21 |
---|---|
[Android/Kotlin] 비디오에서 썸네일 이미지 지정하기 - 비디오 썸네일 지정(2) (0) | 2023.03.20 |
[Android/Kotlin] registerForActivityResult사용하여 앨범에서 사진 선택하기- 비디오 썸네일 지정(1) (0) | 2023.03.20 |
[Android/Kotlin] 갤러리 접근 권한(Premission) 설정하기 - 커스텀 갤러리(1) (0) | 2023.03.16 |
[kotlin] isEmpty() , toInt() 적용안됨 (0) | 2021.08.27 |