์ค์ ํ๋ก์ ํธ
์ค์ต - ์ ์ ์ก์
์ฃผ์๊ตฌ์ฑ
- ํ๋๊ทธ๋จผํธ๋ผ๋ UI ์กฐ๊ฐ์ผ๋ก ๊ตฌ์ฑํ๊ณ ํ๋๊ทธ๋จผํธ๋ค์ ์ข์ฐ๋ก ์ฌ๋ผ์ด๋ ํ ์ ์๋๋ก ๋ทฐํ์ด์ ViewPager๋ฅผ ์ฌ์ฉ
- ์ฌ์ง์ด๋ฏธ์ง ๋ก๋ฉ์ Glide ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉ
- timer๋ฅผ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ์ฌ๋ผ์ด๋ ๋๊ฒ ํจ
1. ํ๋ก๋ฐ์ด๋ ์ฌ์ฉ
์ฝํ ์ธ ํ๋ก๋ฐ์ด๋
- ์ฑ์ ๋ฐ์ดํฐ ์ ๊ทผ์ ๋ค๋ฅธ ์ฑ์ ํ์ฉํ๋ ์ปดํฌ๋ํธ
- ํ๋ก๋ฐ์ด๋๋ฅผ ์ด์ฉํ์ฌ ์ฌ์ง ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์ค๋ ์์
โ ์ฌ์ง ๋ฐ์ดํฐ๋ ์ธ๋ถ ์ ์ฅ์์ ์ ์ฅ๋์ด ์์ผ๋ฏ๋ก ์ธ๋ถ ์ ์ฅ์ ์ฝ๊ธฐ ๊ถํ์ ์ฑ์ ๋ถ์ฌ
โก ์ธ๋ถ ์ ์ฅ์ ์ฝ๊ธฐ ๊ถํ์ ์ํ ๊ถํ์ผ๋ก ์คํ ์ค์ ์ฌ์ฉ์์๊ฒ ๊ถํ์ ํ์ฉํ๋๋ก ํจ
โข contentResolver ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ Cursor ๊ฐ์ฒด๋ก ๊ฐ์ง๊ณ ์ด
์๋๋ก์ด๋ 4๋ ์ปดํฌ๋ํธ - ์กํฐ๋นํฐ: ํ๋ฉด์ ๊ตฌ์ฑ - ์ฝํ ์ธ ํ๋ก๋ฐ์ด๋: ๋ฐ์ดํฐ๋ฒ ์ด์ค, ํ์ผ, ๋คํธ์ํฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฅธ ์ฑ์ ๊ณต์ - ๋ธ๋ก๋์บ์คํธ ๋ฆฌ์๋ฒ: ์ฑ์ด๋ ๊ธฐ๊ธฐ๊ฐ ๋ฐ์กํ๋ ๋ฐฉ์ก์ ์์ ํจ - ์๋น์ค: ํ๋ฉด์ด ์๊ณ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ ์ฉ์ด |
์ค๋งํธํฐ์ ์ฌ์ง ๊ฒฝ๋ก ์ฐพ๊ธฐ
์ธ๋ถ ์ ์ฅ์์ ์ ์ฅ๋ ๋ชจ๋ ์ฌ์ง์ ์ต์ ์์ผ๋ก ์ ๋ ฌํ์ฌ Cursor๊ฐ์ฒด์ ์ฐ๊ฒฐ
โ ์ฒซ ๋ฒ์งธ ์ธ์ : ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋๋ฅผ URI ํํ๋ก ์ง์
โก ๋ ๋ฒ์งธ ์ธ์ : ์ด๋ค ํญ๋ชฉ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๊ฒ์ธ์ง String ๋ฐฐ์ด๋ก ์ง์
โข ์ธ ๋ฒ์งธ ์ธ์ : ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์กฐ๊ฑด์ ์ง์ . ์ ์ฒด ๋ฐ์ดํฐ๋ null๋ก ์ค์
โฃ ๋ค ๋ฒ์งธ ์ธ์ : ์ธ ๋ฒ์งธ ์ธ์์ ์ถ๊ฐ ์กฐ๊ฑด์ ์ง์ , ์ฌ์ฉํ์ง ์๋๋ค๋ฉด null๋ก ์ค์
โค ๋ค์ฏ ๋ฒ์งธ ์ธ์ : ์ ๋ ฌ ๋ฐฉ๋ฒ์ ์ง์ , ์ฌ์ง์ด ์ฐํ ๋ ์ง์ ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ
// ๋ชจ๋ ์ฌ์ง ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URIm //โ
null, // โก ๊ฐ์ ธ์ฌ ํญ๋ชฉ ๋ฐฐ์ด
null, // โข ์กฐ๊ฑด
null, // โฃ ์กฐ๊ฑด
MediaStore.Images.ImageColumns.DATE_TAKEN + "DESC") // โค ์ดฌ์ ๋ ์ง ๋ด๋ฆผ์ฐจ์
โป ์ฑ ์คํํ๋ฉด ์ฑ์ด ์ข ๋ฃ๋๋ฉด์ ์๋ฌ๊ฐ ๋ธ → ๋ก๊ทธ์บฃ ์ฐฝ์ ํ์ (→ ์ธ๋ถ ์ ์ฅ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ผ๋ ค ํ๋๋ฐ ๊ถํ์ด ์๋ค๋ ์๋ฌ ๋ฐ์)
๋งค๋ํ์คํธ์ ์ธ๋ถ ์ ์ฅ์ ์ฝ๊ธฐ ๊ถํ ์ถ๊ฐ
- AndroidManifest.xml ํ์ผ์ ์ด๊ณ READ_EXTERNAL_STORAGE ์ธ๋ถ ์ ์ฅ์ ์ฝ๊ธฐ ๊ถํ์ ์ฑ์ ์ถ๊ฐ
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<application
...
</application>
</manifest>
๊ถํ ๊ทธ๋ฃน | ๊ถํ |
STORAGE | - READ_EXTERNAL_STORAGE - WRITE_EXTERNAL_STORAGE |
LOCATION | - ACCESS_FINE_LOCATION - ACCESS_COARSE_LOCATION |
SMS | - SEND_SMS - RECEIVE_SMS |
CAMERA | - CAMERA |
๊ถํ ์ถ๊ฐ
- ์คํ ์ค์ ์ํ ๊ถํ์ด ํ์ํ ์์ ์ ์ํํ ๋๋ง๋ค ๊ถํ์ด ์๋์ง ํ์ธํด์ผ ํจ
- ์ฑ์ด ์ธ๋ถ ์ ์ฅ์ ์ฝ๊ธฐ ๊ถํ์ด ์๋์ง ํ์ธํ๋ ์ฝ๋
API 33 ์ด์๋ถํฐ READ_EXTERNAL_STORAGE ๊ถํ์ด “READ_MEDIA_IMAGES”, ”READ_MEDIA_VIDEO”, ”READ_MEDIA_AUDIO”๋ก ์ธ๋ถํ ๋จ
→ ์ฌ์ง์ ์ฌ์ฉํ๋ ค๋ฉด ํด๋น์ด๋ฆ์ ๊ถํ์ผ๋ก ๊ถํ ์ฒดํฌ ๋ฐ ๊ถํ์์ฒญ์ ์งํํด์ผ ํจ
// ๊ถํ์ด ๋ถ์ฌ๋์๋์ง ํ์ธ
if (ContextCompat.checkSelfPermission(this, readIMagePermission) != PackageManager.PERMISSION_GRANTED)
- Compat Class : ๋งค๋ ์๋ก ๋ฐํ๋๋ SDK์ API ๋ฒ์ ๋ณ ์ฐจ์ด์ ํธํ์ฑ ์ง์
- ContextCompat : ์ฑ๊ณผ ์ฑ ์ ์ฅ์ ์ฌ์ด์ ๋ฐ์ดํฐ ์ ๊ทผ ์ง์
- Manifest ํด๋์ค๋ ์ฌ๋ฌ ํจํค์ง์ ์กด์ฌํ๋๋ฐ ์ฝ๋ ์์ฑ ์ค ์ด๋ ๊ฒ์ ์ํฌํธํ ์ง ๋ฌผ์ด๋ณด๋ฉด android๋ฅผ ์ํฌํธํจ
๊ถํ ์์ฒญ(MainActivity.kt)
โ ์ฑ์ ๊ถํ์ด ๋ถ์ฌ ์ฌ๋ถ ํ์ธ
โก shoudShowRequestPermissionRationale( ) : ์ด์ ์ ๊ถํ ์์ฒญ์ ๊ฑฐ๋ถ ์ฌ๋ถ ๋ฐํ
โข๊ถํ ๋ถ์ฌ๋ฅผ ๊ฑฐ๋ถํ๋ค๋ฉด ๊ถํ์ด ์ ํ์ํ์ง์ ๋ํด์ ๋ฉ์์ง๋ฅผ ํ์ํ๊ณ ๋ค์ ๊ถํ์ ์์ฒญ
โฃ ๊ถํ์ด ๋ถ์ฌ๋์ง ์์๋ค๋ฉด requestPermissions( ) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ถํ ์์ฒญ
โค ๊ถํ ํ์ฉ์์๋ ์ด๋ฏธ์ง ์ฝ์ด ์ค๊ธฐ ์ํ
class MainActivity : AppCompatActivity() {
private val REQUEST_READ_EXTERNAL_STORAGE = 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val readIMagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) Manifest.permission.READ_MEDIA_IMAGES
else Manifest.permission.READ_EXTERNAL_STORAGE
// ๊ถํ์ด ๋ถ์ฌ๋์๋์ง ํ์ธ
if (ContextCompat.checkSelfPermission(this, readIMagePermission) != PackageManager.PERMISSION_GRANTED)
// ๊ถํ์ด ํ์ฉ๋์ง ์์
if(ActivityCompat.shouldShowRequestPermissionRationale(this, readIMagePermission)){
// ์ด์ ์ ๊ถํ ๊ฑฐ๋ถํ ์ ์์ผ๋ฉด ์ค๋ช
(๊ฒฝ๊ณ )
var dlg = AlertDialog.Builder(this)
dlg.setTitle("๊ถํ์ด ํ์ํ ์ด์ ")
dlg.setMessage("์ฌ์ง ์ ๋ณด๋ฅผ ์ป๊ธฐ ์ํด์๋ ์ธ๋ถ ์ ์ฅ์ ๊ถํ์ด ํ์๋ก ํ์ํฉ๋๋ค.")
dlg.setPositiveButton("ํ์ธ"){dialog, which-> ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(readIMagePermission), REQUEST_READ_EXTERNAL_STORAGE)}
dlg.setNegativeButton("์ทจ์", null)
dlg.show()
} else{
// ์ฒ์ ๊ถํ ์์ฒญ
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(readIMagePermission), REQUEST_READ_EXTERNAL_STORAGE)
} else{
// ๊ถํ์ด ์ด๋ฏธ ์ ๋๋ก ํ์ฉ๋จ
getAllPhotos()
}
}
์ฌ์ฉ๊ถํ ์์ฒญ ์๋ต ์ฒ๋ฆฌ
- ์ฌ์ฉ์๊ฐ ๊ถํ์ ์์ฒญํ๋ฉด ์์คํ ์ onRequestPermissionsResult ( ) ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์ฌ์ฉ์์ ์๋ต์ ์ ๋ฌ
- ์๋ต ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ์ฌ ์ฌ์ง ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๊ถํ์ด ๊ฑฐ๋ถ๋๋ค๋ ํ ์คํธ ๋ฉ์์ง๋ฅผ ํ์ํ๋ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ฑ
private fun getAllPhotos(){
...
if(cursor != null){
while(cursor.moveToNext()){
// ์ฌ์ง ๊ฒฝ๋ก Uri ๊ฐ์ ธ์ค๊ธฐ
val uri = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
Log.d("MainActivity", uri)
}
cursor.close()
}
}
- Device File Explorer ํด๋ฆญ > sdcard > Pictures ์ฐํด๋ฆญ > Upload ํด๋ฆญํด์ ์ด๋ฏธ์ง ์ถ๊ฐ > Synchronize
- AVD ์ฌ์์ ํ์ (์ฌ์ง ์ฑ์ ์ด๋ฏธ์ง ์๋ค์ด๊ฐ ์๊ธฐ ๋๋ฌธ์)
2. ์ ์์ก์ ๊ตฌํํ๊ธฐ
ํ๋๊ทธ๋จผํธ(Fragment): ์ฌ์ฉ์ ์ธํฐํ์ด์ค์ ๋ชจ์
ํ๋๊ทธ๋จผํธ์ ๊ฐ๋
- ํ๋๊ทธ๋จผํธ์ ํ์ํ ๋ทฐ๋ฅผ ๋ ์ด์์ ํ์ผ๋ก๋ถํฐ ์ฝ์ด์ค๋ ๋ถ๋ถ์ โก onCreateView( ) ๋ฉ์๋์ด๋ฉฐ ์กํฐ๋นํฐ์ onCreate( )์ ๋์ผ
- โ onCreate( ) ๋ฉ์๋๋ ํ๋๊ทธ๋จผํธ๋ฅผ ์์ฑํ ๋ ์ธ์๊ฐ ํจ๊ป ๋์ด์จ๋ค๋ฉด ์ฃผ๋ก โ onCreate( ) ๋ฉ์๋์์ ๋ฐ์์ ๋ณ์์ ๋ด์.
- โก onCreateView( ) ๋ฉ์๋์์ ์์ฑ๋ ๋ ์ด์์ ๋ทฐ๋ ์๋ช ์ฃผ๊ธฐ์๋ ํฌํจ๋์ง ์๋ โข onViewCreated( ) ๋ฉ์๋๋ก ์ ๋ฌ๋๋ฉฐ ์ด์ชฝ์์ ๋ทฐ๊ฐ ์์ฑ๋ ์ดํ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฑ์ ์ํ
ํ๋๊ทธ๋จผํธ ์์ฑ
- ํ๋ก์ ํธ ์ฐฝ์์ ํจํค์ง๋ฅผ ํด๋ฆญ ํ ์ฐํด๋ฆญ ๋๋ ์๋๋ก์ด๋ ์คํ๋์ค ์๋จ ๋ฉ๋ด์์ File → New → Fragment → Fragment (Blank)๋ฅผ ํด๋ฆญ
ํ๋๊ทธ๋จผํธ์ ๋ ์ด์์ ์์
๋ ์ด์์์ ConstraintLayout์ผ๋ก ๋ณ๊ฒฝํ๊ณ ์ด๋ฏธ์ง ๋ทฐ๋ฅผ ํ๋ฉด์ ๊ฝ ์ฑ์ฐ๋๋ก ์ค์
Glide ๋ผ์ด๋ธ๋ฌ๋ฆฌ : ๋น ๋ฅด๊ณ ํจ์จ์ ์ผ๋ก ์ด๋ฏธ์ง ์ฒ๋ฆฌ
- ์ด๋ฏธ์ง, Gif, ๋น๋์ค ์คํธ ๋ก๋ฉ๊ณผ ๋์ฝ๋ฉ, ์บ์ฑ ๋ฑ์ API ์ง์
- ๋ค์ํ ์ข ๋ฅ์ ์ด๋ฏธ์ง๋ฅผ ๋น ๋ฅด๊ณ ๋ถ๋๋ฌ์ด ์คํฌ๋กค ์ง์
Glide ์ฌ์ฉ์ ์ํด์๋ ์์กด์ฑ ๋ฐ ๊ถํ ์ค์ ํ์
- ๋ฐฉ๋ฒ1) Gradle ์ถ๊ฐ (์ง์ ์ฝ๋ ํ์ดํ)
- Sync Now ์ํ
- ๋งค๋ํ์คํธ ์ถ๊ฐ(์๊ฒฉ ์ด๋ฏธ์ง ์ธ ๊ฒฝ์ฐ, ๊ถํ ํ์) (// ์ธํฐ๋ท์ ํตํด ์ด๋ฏธ์ง ๊ฐ์ ธ์ด)
๋ทฐ์ ๋ก๋ํ๋ ๋ฐฉ๋ฒ
- .with(์ ๋ก๋ํ ์ด๋ฏธ์ง ๋์ธ ๊ณณ), .load(์ ๋ก๋ ์ด๋ฏธ์ง uri), .into(๋ณด์ฌ์ค ์์ ฏ)
๋ฐฉ๋ฒ2) Glide ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ ์ค๋น
- ์ง์ build.gradle ํ์ผ์ ์์ ํ์ง ์๊ณ ์๋๋ก์ด๋ ์คํ๋์ค์ ๋ฉ๋ด ํ์ฉ
- ์๋จ ๋ฉ๋ด ์ค File → Project Structure๋ฅผ ํด๋ฆญ
ํ๋๊ทธ๋จผํธ์ ์ฌ์ง ํ์ํ๊ธฐ
โ ํด๋์ค ์ ์ธ ๋ฐ์ const ํค์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์๋ก ์ ์
โก newInstance( ) ๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ ํ๋๊ทธ๋จผํธ๋ฅผ ์์ฑํ ์ ์๊ณ ์ธ์๋ก uri๊ฐ์ ์ ๋ฌ
โข ํ๋๊ทธ๋จผํธ๊ฐ ์์ฑ๋๋ฉด onCreate( ) ๋ฉ์๋๊ฐ ํธ์ถ๋๊ณ ARG_URI ํค์ ์ ์ฅ๋ uri๊ฐ์ ๋ณ์์ ์ ์ฅ
โฃ onCreateView( ) ๋ฉ์๋์์๋ ํ๋๊ทธ๋จผํธ์ ํ์๋ ๋ทฐ๋ฅผ ์์ฑ
โค Glide.with (this)๋ก ์ฌ์ฉ ์ค๋น๋ฅผ ํ๊ณ load ( )์ uri๊ฐ์ ์ธ์๋ก ์ฃผ๊ณ ์ด๋ฏธ์ง ๋ก๋ฉ
โฅ into( ) ๋ฉ์๋๋ก imageView์ ํ์
package com.example.mygallery
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.bumptech.glide.Glide
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_URI = "uri"
/**
* A simple [Fragment] subclass.
* Use the [PhotoFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class PhotoFragment : Fragment() {
// TODO: Rename and change types of parameters
private var uri: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
uri = it.getString(ARG_URI)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_photo, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var imageView : ImageView = view.findViewById(R.id.imageView)
Glide.with(this).load(uri).into(imageView) // ๊ทธ๋ฆผ ์ถ๋ ฅ
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment PhotoFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(uri: String) =
PhotoFragment().apply {
arguments = Bundle().apply {
putString(ARG_URI, uri)
}
}
}
}
์กํฐ๋นํฐ์ ViewPager ์ถ๊ฐ
- ๋ทฐํ์ด์ : ์ฌ๋ฌ ํ๋๊ทธ๋จผํธ๋ค์ ์ข์ฐ๋ก ์ฌ๋ผ์ด๋ํ๋ ๋ทฐ
PagerAdapter ์์ฑ
- ViewPager์ ํ์ํ ๋ด์ฉ์ ์ ์ํ๋ ค๋ฉด ์ด๋ํฐ๊ฐ ํ์
- ํ๋๊ทธ๋จผํธ๋ฅผ ์์ดํ ์ผ๋ก ๊ฐ์ง๋ฉด์ ViewPager์ ์ค์ ํ๋ ์ด๋ํฐ๋ ๋ค์ ๋ ๊ฐ์ง
- FragmentPAgerAdapter: ํ์ด์ง ๋ด์ฉ์ด ์๊ตฌ์ ์ผ ๋ ์ ํฉ. ํ ๋ฒ ๋ก๋ฉํ ํ์ด์ง๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ณด๊ดํ๊ธฐ ๋๋ฌธ์ ๋นจ๋ฆ. ํ์ด์ง๊ฐ ๋ง์ผ๋ฉด ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉ
- FragmentStatePagerAdapter: ๋ง์ ์์ ํ์ด์ง๊ฐ ์์ ๋ ์ ํฉ. ๋ณด์ด์ง ์๋ ํ์ด์ง๋ฅผ ๋ฉ๋ชจ๋ฆฌ์์ ์ ๊ฑฐํ ์ ์์. ์๋์ ์ผ๋ก ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์ง
FragmentStatePagerAdapter ์ฌ์ฉ
- MainActivity ์ฐํด๋ฆญ → new → Kotlin File/Class (๋๋ ์๋๋ก์ด๋ ์คํ๋์ค ์๋จ ๋ฉ๋ด์ File → New → Kotlin File/Class๋ฅผ ํด๋ฆญ)
โ MyPagerAdapter.kt ํ์ผ์ด ์ด๋ฆฌ๋ฉด FragmentStatePagerAdapter ํด๋์ค๋ฅผ ์์๋ฐ์
โก ์ํผ ํด๋์ค์ ์์ฑ์๋ฅผ ์ถ๊ฐํ๋ Add constructor parameters from FragmentStatePagerAdapter(FragmentManager!)๋ฅผ ํด๋ฆญ
โข ์์ฑ์ ํ๋ผ๋ฏธํฐ๊ฐ ์ถ๊ฐ๋๊ณ ๋ค์ ๋นจ๊ฐ ์ค์ด ํ์๋๋ ํด๋์ค ์ด๋ฆ์ ์ปค์๋ฅผ ๋๊ณ
๋ฏธ๊ตฌํ๋ ๋ฉค๋ฒ๋ฅผ ๊ตฌํํ๋ Implement members๋ฅผ ํด๋ฆญ
โฃ 'Ctrl + A' ๋ฅผ ํด๋ฆญํ์ฌ ๋ชจ๋ ๋ฉ์๋๋ฅผ ์ ํํ๊ณ OK๋ฅผ ํด๋ฆญ
โค ์๋์ผ๋ก ์์ฑ๋ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํจ
โฅ ์ด๋ํฐ๊ฐ ํ๋๊ทธ๋จผํธ์ ๋ชฉ๋ก์ ๋ฆฌ์คํธ๋ก ๊ฐ์ง๋๋ก ํจ
โฆ ์ด ๋ชฉ๋ก์ update Fragments( ) ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ์ธ๋ถ์์ ์ถ๊ฐํ ์ ์์ (์ฌ์ฉ์๊ฐ ์ง์ ์์ฑ)
โง getItem ( ) : position ์์น์ ์ด๋ค ํ๋๊ทธ๋จผํธ๋ฅผ ํ์ํ ์ง๋ฅผ ์ ์
โจ getCount( ) : ์์ดํ ํ๋๊ทธ๋จผํธ ๊ฐ์๋ฅผ ์ ์
package com.example.mygallery
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager2.adapter.FragmentStateAdapter
class MyPagerAdapter(fragment: FragmentManager) : FragmentStatePagerAdapter(fragment) {
private val items = ArrayList<Fragment>()
// position ์์น์ ํ๋๊ทธ๋จผํธ
override fun getItem(position: Int): Fragment {
return items[position]
}
// ํ๋๊ทธ๋จผํธ์ ๊ฐฏ์
override fun getCount(): Int {
return items.size
}
// ์์ดํ
๊ฐฑ์
fun updateFragments(items : List<Fragment>){
this.items.addAll(items)
}
}
์ ์์ก์ ์์ฑ
- getAllPhotos ( ) ๋ฉ์๋ ์์
โ ํ๋๊ทธ๋จผํธ๋ฅผ ์์ดํ ์ผ๋ก ํ๋ ArrayList ๋ฅผ ์์ฑ
โก ์ฌ์ง์ Cursor๊ฐ์ฒด์์ ๊ฐ์ ธ์ฌ ๋ PhotoFragment.newInstance (uri)๋ก ํ๋๊ทธ๋จผํธ๋ฅผ ์์ฑํ๋ฉด์ fragments ๋ฆฌ์คํธ์ ์ถ๊ฐ
โข MyPagerAdapter ๋ฅผ ์์ฑํ๋ฉด์ ํ๋๊ทธ๋จผํธ ๋งค๋์ ๋ฅผ ์์ฑ์์ ์ธ์๋ก ์ ๋ฌ
โฃ ์ฑ์ ์คํํ์ ๋ ์ฌ์ง์ด ํ์๋๊ณ ์ข์ฐ๋ก ์ฌ๋ผ์ด๋ ํ์ ๋ ๋ค์ ์ฌ์ง์ผ๋ก ๋์ด๊ฐ๋ฉด ์ฑ๊ณต
class MainActivity : AppCompatActivity() {
private val REQUEST_READ_EXTERNAL_STORAGE = 1000
lateinit var viewPager: ViewPager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.viewPager)
...
}
private fun getAllPhotos(){
// ๋ชจ๋ ์ฌ์ง ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
...
val fragments = ArrayList<Fragment>() // 1
if(cursor != null){
while(cursor.moveToNext()){
// ์ฌ์ง ๊ฒฝ๋ก Uri ๊ฐ์ ธ์ค๊ธฐ
val uri = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
Log.d("MainActivity", uri)
fragments.add(PhotoFragment.newInstance(uri)) // 2
}
cursor.close()
}
// ์ด๋ํฐ 3
val adapter = MyPagerAdapter(supportFragmentManager)
adapter.updateFragments(fragments)
viewPager.adapter = adapter
}
}
// MyPagerAdapter.kt ์ฝ๋ ์์
class MyPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
3. ์ฌ๋ผ์ด๋์ผ ์ํํ๊ธฐ
getAllPhotos ( ) ๋ฉ์๋์ ๋ง์ง๋ง์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐ
โ 3์ด๋ง๋ค ์คํ๋๋ ํ์ด๋จธ๋ฅผ ์์ฑ : 3์ด๋ง๋ค ํ์ด์ง๋ฅผ ์ ํํ๋ UI ๋ณ๊ฒฝ ์คํ (timer ์ฌ์ฉ)
โก Timer๊ฐ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋๋ก ๋์ํด UI๋ฅผ ๋ณ๊ฒฝ์ runOnUiThread ์ํ
โข ํ์ฌ ํ์ด์ง๊ฐ ๋ง์ง๋ง ํ์ด์ง๊ฐ ์๋๋ผ๋ฉด ๋ค์ ํ์ด์ง๋ก ๋ณ๊ฒฝํ๊ณ , ๋ง์ง๋ง ํ์ด์ง๋ผ๋ฉด ์ฒซ ํ์ด์ง๋ก ๋ณ๊ฒฝ
class MainActivity : AppCompatActivity() {
...
// 3์ด๋ง๋ค ์๋์ผ๋ก ์ฌ๋ผ์ด๋
timer(period = 3000){
runOnUiThread {
if(viewPager.currentItem < adapter.count-1){
viewPager.currentItem++
} else{
viewPager.currentItem = 0
}
}
}
}
}
์ต์ข
MainActivity.kt
package com.example.mygallery
import android.Manifest
import android.app.AlertDialog
import android.content.pm.PackageManager
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager
import kotlin.concurrent.timer
class MainActivity : AppCompatActivity() {
private val REQUEST_READ_EXTERNAL_STORAGE = 1000
lateinit var viewPager: ViewPager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.viewPager)
val readIMagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) Manifest.permission.READ_MEDIA_IMAGES
else Manifest.permission.READ_EXTERNAL_STORAGE
// ๊ถํ์ด ๋ถ์ฌ๋์๋์ง ํ์ธ
if (ContextCompat.checkSelfPermission(this, readIMagePermission) != PackageManager.PERMISSION_GRANTED)
// ๊ถํ์ด ํ์ฉ๋์ง ์์
if(ActivityCompat.shouldShowRequestPermissionRationale(this, readIMagePermission)){
// ์ด์ ์ ๊ถํ ๊ฑฐ๋ถํ ์ ์์ผ๋ฉด ์ค๋ช
(๊ฒฝ๊ณ )
var dlg = AlertDialog.Builder(this)
dlg.setTitle("๊ถํ์ด ํ์ํ ์ด์ ")
dlg.setMessage("์ฌ์ง ์ ๋ณด๋ฅผ ์ป๊ธฐ ์ํด์๋ ์ธ๋ถ ์ ์ฅ์ ๊ถํ์ด ํ์๋ก ํ์ํฉ๋๋ค.")
dlg.setPositiveButton("ํ์ธ"){dialog, which-> ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(readIMagePermission), REQUEST_READ_EXTERNAL_STORAGE)}
dlg.setNegativeButton("์ทจ์", null)
dlg.show()
} else{
// ์ฒ์ ๊ถํ ์์ฒญ
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(readIMagePermission), REQUEST_READ_EXTERNAL_STORAGE)
} else{
// ๊ถํ์ด ์ด๋ฏธ ์ ๋๋ก ํ์ฉ๋จ
getAllPhotos()
}
}
private fun getAllPhotos(){
// ๋ชจ๋ ์ฌ์ง ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, // ๊ฐ์ ธ์ฌ ํญ๋ชฉ ๋ฐฐ์ด
null, // ์กฐ๊ฑด
null, // ์กฐ๊ฑด
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC") // ์ดฌ์ ๋ ์ง ๋ด๋ฆผ์ฐจ์
val fragments = ArrayList<Fragment>()
if(cursor != null){
while(cursor.moveToNext()){
// ์ฌ์ง ๊ฒฝ๋ก Uri ๊ฐ์ ธ์ค๊ธฐ
val uri = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
Log.d("MainActivity", uri)
fragments.add(PhotoFragment.newInstance(uri))
}
cursor.close()
}
// ์ด๋ํฐ
val adapter = MyPagerAdapter(supportFragmentManager)
adapter.updateFragments(fragments)
viewPager.adapter = adapter
// 3์ด๋ง๋ค ์๋์ผ๋ก ์ฌ๋ผ์ด๋
timer(period = 3000){
runOnUiThread {
if(viewPager.currentItem < adapter.count-1){
viewPager.currentItem++
} else{
viewPager.currentItem = 0
}
}
}
}
}
PhotoFragment.kt
package com.example.mygallery
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.bumptech.glide.Glide
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_URI = "uri"
/**
* A simple [Fragment] subclass.
* Use the [PhotoFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class PhotoFragment : Fragment() {
// TODO: Rename and change types of parameters
private var uri: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
uri = it.getString(ARG_URI)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_photo, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var imageView : ImageView = view.findViewById(R.id.imageView)
Glide.with(this).load(uri).into(imageView) // ๊ทธ๋ฆผ ์ถ๋ ฅ
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment PhotoFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(uri: String) =
PhotoFragment().apply {
arguments = Bundle().apply {
putString(ARG_URI, uri)
}
}
}
}
MyPagerAdapter.kt
package com.example.mygallery
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
class MyPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
// ๋ทฐํ์ด์ ๊ฐ ํ์ํ ํ๋๊ทธ๋จผํธ ๋ชฉ๋ก
private val items = ArrayList<Fragment>()
// position ์์น์ ํ๋๊ทธ๋จผํธ
override fun getItem(position: Int): Fragment {
return items[position]
}
// ํ๋๊ทธ๋จผํธ์ ๊ฐฏ์ (์์ดํ
๊ฐฏ์)
override fun getCount(): Int {
return items.size
}
// ์์ดํ
๊ฐฑ์
fun updateFragments(items : List<Fragment>){
this.items.addAll(items)
}
}
์ ๋ฆฌ
- ์ฝํ ์ธ ํ๋ก๋ฐ์ด๋๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ๊ธฐ์ ์ ์ฅ๋ ์ฌ์ง ์ ๋ณด๋ฅผ ์ป์ ์ ์์
- ์ํํ ๊ถํ์ ์ฌ์ฉํ ๋๋ ์ฑ์ ์คํํ๋ ์ค์ ํด๋น ๊ถํ์ ์ฌ์ฉ ํ์ฉ์ ์ฌ์ฉ์์๊ฒ ์์ฒญํด์ผ ํจ
- ํ๋๊ทธ๋จผํธ๋ UI ์กฐ๊ฐ์ ์ผ๋ก ์กํฐ๋นํฐ์๋ ์ฌ๋ฌ ํ๋๊ทธ๋จผํธ๋ฅผ ๋ฐฐ์นํ ์ ์๊ณ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅ
- ์ด๋ฏธ์ง๋ฅผ ๋ก๋ฉํ ๋ ๋ฉ๋ชจ๋ฆฌ์ ์บ์ ๊ด๋ฆฌ ๋ฐ ์ฑ๋ฅ์ ๊ณ ๋ คํด Glide ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์
- ๋ทฐํ์ด์ ๋ ์ฌ๋ฌ ํ๋๊ทธ๋จผํธ๋ฅผ ์ข์ฐ๋ก ์ฌ๋ผ์ด๋ํ๋ ์ด๋ํฐ ํจํด์ ๊ตฌํํจ