* 이 글은 인프런 강의 <[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린>을 보고 정리한 글입니다.
목표
맛집 사이트(https://www.siksinhot.com/)에서
가게들의 페이지(url), 이미지, 이름을 RecyclerView를 이용하여 레이아웃을 설정하자.
여기서 Glide와 WebView를 사용하고,
Firebase를 이용하여 이메일과 비번으로 인증을 하도록 한다.
그리고 또, 실시간 데이터베이스를 사용하여 북마크 기능을 만든다.
세부 목표 (1)
Splash 화면이 3초 동안 나오도록 한다.
리사이클러뷰를 위해 아이템들의 모양, 어댑터를 만든다.
가게들의 페이지(url), 이미지, 가게이름을 담는 데이터 클래스를 만든다.
가게 이미지는 Glide를 이용하여 인터넷 이미지 주소를 통해 업로드 하도록 한다.
MainActivity에서 데이터 클래스 자료형으로 item을 만들고
리사이클러뷰와 Glide를 통해 item의 설정한 레이아웃대로 나오는지 확인한다.
activity_splash.xml
먼저 Splash 화면부터 만든다.
처음 화면에 나올 레이아웃을 설정한다.
<?xml version="1.0" encoding="utf-8"?>
<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=".SplashActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIKSIN"
android:textSize="40dp"
android:textColor="#ff7f00"
android:textStyle="bold"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml
SplashActivity부터 실행되도록 <intent-filter>를 ".SplashActivity"의 <activity> 안에 넣는다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
<activity
android:name=".SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
</activity>
</application>
</manifest>
SplashActivity
Handler를 사용하여 3초 동안 Splash 화면이 나오도록 한 후
MainActivity 화면으로 넘어가도록 한다.
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
Handler().postDelayed({
startActivity(Intent(this, MainActivity::class.java))
finish()
}, 3000)
}
}
activity_main.xml
리사이클러뷰를 만든다.
<?xml version="1.0" encoding="utf-8"?>
<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.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
radius.xml
리사이클러뷰의 item의 모양을 설정하기 위해
drawable 폴더에서 radius.xml을 작성한다.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"></solid>
<corners
android:bottomRightRadius="15dp"
android:bottomLeftRadius="15dp"
android:topRightRadius="15dp"
android:topLeftRadius="15dp"></corners>
<stroke
android:width="1dp"
android:color="#BDBDBD"/>
</shape>
rv_item.xml
리사이클러뷰에서 사용할 item의 레이아웃을 설정한다.
맛집의 이미지와 맛집 이름(텍스트)를 배치하기로 한다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="@drawable/radius"
android:orientation="vertical">
<ImageView
android:id="@+id/rvImageArea"
android:src="@drawable/ic_launcher_background"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<TextView
android:id="@+id/rvTextArea"
android:text="text"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
AndroidManifest.xml
Glide를 사용하려면(이미지 주소로 이미지를 로드하려면) 인터넷이 되도록 해야 한다.
<uses-permission>을 통해 인터넷 허용을 한다.
<?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.INTERNET"/>
...
</manifest>
Glide 다운로드
'android glide'를 구글에 검색하면 다음과 같은 GitHub 링크가 보인다.
https://github.com/bumptech/glide
gradle의 dependencies에 추가를 한다.
dependencies {
...
implementation("com.github.bumptech.glide:glide:4.16.0")
}
RVAdapter
리사이클러뷰를 위한 어댑터를 만들어준다.
Glide를 사용하여 이미지 주소를 통해 이미지뷰에 로드하도록 하였다.
// RecyclerView.Adapter를 상속받아 커스텀 어댑터를 구현
class RVAdapter(val context : Context, val List : MutableList<ContentsModel>) : RecyclerView.Adapter<RVAdapter.ViewHolder>() {
// 새로운 ViewHolder 객체를 생성
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RVAdapter.ViewHolder {
// LayoutInflater를 사용하여 rv_item 레이아웃을 인플레이트하고 ViewHolder에 전달
val v = LayoutInflater.from(parent.context).inflate(R.layout.rv_item, parent, false)
return ViewHolder(v)
}
// ViewHolder와 데이터를 바인딩
override fun onBindViewHolder(holder: RVAdapter.ViewHolder, position: Int) {
// bindItems 메서드를 호출하여 데이터를 뷰에 바인딩
holder.bindItems(List[position])
}
// RecyclerView의 항목 총 개수를 반환
override fun getItemCount(): Int {
return List.size
}
// ViewHolder 클래스는 RecyclerView.ViewHolder를 상속받아 각 항목 뷰를 보유
inner class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
// bindItems 메서드는 항목 데이터를 뷰에 설정
fun bindItems(item : ContentsModel) {
// rv_item 레이아웃의 ImageView와 TextView를 찾아 바인딩
val rv_img = itemView.findViewById<ImageView>(R.id.rvImageArea)
val rv_text = itemView.findViewById<TextView>(R.id.rvTextArea)
// 항목의 제목을 TextView에 설정
rv_text.text = item.titleText
// Glide를 사용하여 이미지 URL을 ImageView에 로드
Glide.with(context)
.load(item.imageUrl)
.into(rv_img)
}
}
}
ContentsModel
콘텐츠 모델 데이터 클래스를 만든다.
맛집 url과 이미지 주소, 맛집 이름을 인자로 갖는다.
data class ContentsModel (
val url : String = "",
val imageUrl : String = "",
val titleText : String = ""
)
MainActivity
테스트 삼아서 3개의 item을 생성하고 리사이클러뷰에 제대로 구현되는지 확인하였다.
GridLayoutManager를 사용하여 item들이 두 줄로 나뉘도록 하였다.
class MainActivity : AppCompatActivity() {
// items 리스트는 ContentsModel 객체를 담는 가변 리스트로, RecyclerView에 표시할 데이터를 포함
private val items = mutableListOf<ContentsModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// items 리스트에 ContentsModel 객체를 추가
items.add(
ContentsModel(
"https://www.siksinhot.com/P/349263",
"https://img.siksinhot.com/place/1462946564336361.png?w=307&h=300&c=Y",
"미 피아체"
)
)
items.add(
ContentsModel(
"https://www.siksinhot.com/P/349263",
"https://img.siksinhot.com/place/1462946564336361.png?w=307&h=300&c=Y",
"미 피아체"
)
)
items.add(
ContentsModel(
"https://www.siksinhot.com/P/349263",
"https://img.siksinhot.com/place/1462946564336361.png?w=307&h=300&c=Y",
"미 피아체"
)
)
// RecyclerView를 XML 레이아웃에서 찾아 recyclerView 변수에 할당
val recyclerView = findViewById<RecyclerView>(R.id.rv)
// RVAdapter 객체를 생성하여 rvAdapter 변수에 할당, items 리스트를 어댑터에 전달
val rvAdapter = RVAdapter(baseContext, items)
// RecyclerView에 어댑터를 설정
recyclerView.adapter = rvAdapter
// RecyclerView의 레이아웃 매니저를 GridLayoutManager로 설정, 2열 그리드로 배치
recyclerView.layoutManager = GridLayoutManager(this, 2)
}
}