📝 Standard Mission
2. RecyclerView를 활용한 메모장 앱 만들기
메인 화면
- 메모 목록을 표시하는 RecyclerView와 메모 추가 Button으로 구성
- RecyclerView의 각 Item에는 메모의 내용이 보이도록 TextView 설정
- 각 Item을 누르면 Item이 삭제되도록 구성
- 메모 추가 Button을 누르면 데이터를 받을 수 있도록 설정하고 글쓰기 화면으로 넘어가기
- 돌아오면 넘어온 데이터를 가지고 RecyclerView에 추가
메모 화면
- 메모를 할 수 있는 EditText와 저장 Button으로 구성
- EditText는 여러 줄을 입력할 수 있도록 설정
- 저장 Button을 누르면 메인 화면으로 넘길 데이터를 설정하고 메모 화면 닫기
일단 MainActivity에서 MemoActivity로 갔다가 돌아왔을 때,
Data를 받아올 수 있게 registerForActivityResult를 사용할 것이다. 사용방법은 아래 링크를 참고했다!
- [참고] 다른 Activity로 이동했다가 돌아올 때 Data를 받을 수 있는 `registerForActivityResult` 사용 방법
1. activity_main.xml에 recyclerView 추가
<?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_data"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="메모 추가"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="18dp"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp"
android:backgroundTint="@color/btn"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. RecylerView 안 각 아이템 배치할 레이아웃 만들기
item_data.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="wrap_content"
android:layout_marginTop="16dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/tv_memo"
android:layout_width="330dp"
android:layout_height="wrap_content"
android:background="@color/memo"
android:padding="10dp"
android:text="TextView"
android:textColor="@color/black"
android:textSize="22dp"
android:layout_marginRight="5dp"/>
<ImageView
android:id="@+id/delete_btn"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/icon"
android:layout_margin="8dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
3. list_item에 넣을 data class 만들기
ViewHolder 만들기
❓ ViewHolder: 데이터가 틀 안에 들어갈 수 있게 하는 기능 정의
DataRVAdapter.kt 클래스 안의 내부 클래스로 만들면 된다.
내가 만들 ViewHolder엔 val 예약어로 전역으로 사용하기 위해 바인딩을 전달받았다.
그리고 상속받는 ViewHolder생성자에는 꼭 binding.root를 전달해야 한다.
4. DataRVAdapter 만들기
❓ 어댑터의 역할: 간단히 말해서, 데이터 리스트를 실제 눈으로 볼수있게 item으로 변환하는 중간다리 역할
= 데이터를 받아오고 이를 레이아웃에 직접 연결하는 함수를 실행시키는 클래스
= 미리 생성해둔 뷰홀더 객체에 사용자가 원하는 data list를 주입하고 data list의 변경사항을 UI에 반영
RecyclerViewAdapter에는 필수로 구현해야 하는 메서드들이 있음!
-> OnCreateViewHolder(), onBindViewHolder(), getItemCount()
DataRVAdapter.kt
package com.example.homework5
import android.util.SparseBooleanArray
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.homework5.databinding.ItemDataBinding
import java.util.Calendar.getInstance
// Adapter 인자에 데이터를 넣어주면, MainActivity 에서 수정하면 자동으로 수정됨
class DataRVAdapter(
private val dataList: ArrayList<Data>,
val onClickDeleteBtn: (data: Data) -> Unit):
RecyclerView.Adapter<DataRVAdapter.DataViewHolder>() {
// ViewHolder 객체
inner class DataViewHolder(val binding: ItemDataBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(data: Data) {
binding.tvMemo.text = data.memo;
}
}
// ViewHolder 만들어질 때 실행할 동작
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataViewHolder {
val viewBinding = ItemDataBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DataViewHolder(viewBinding)
}
// ViewHolder 가 실제로 데이터를 표시해야 할 때 호출되는 함수
override fun onBindViewHolder(holder: DataViewHolder, position: Int) {
val listposition = dataList[position]
holder.bind(listposition)
// deleteBtn 이 눌렸을 때 position 전달
holder.binding.deleteBtn.setOnClickListener {
onClickDeleteBtn.invoke(listposition)
}
}
// 표현할 Item 의 총 개수
override fun getItemCount(): Int = dataList.size
}
5. MainActivity.kt에 연결
리사이클러뷰에 어댑터 연결하고, 레이아웃 매니저를 필수로 연결해준다!
MemoActivity에서 데이터를 가져오기 위해 registerForActivityResult를 사용해 ActivityResult를 받기 위한 Callback을 등록한다. 그리고 받아온 데이터를 dataList에 넣어주면서 notifyItemRangeInserted로 아이템이 변경되었다는 것을 알려준다.
❗중요
데이터를 신규 추가하거나 삭제한 경우 반드시 메서드를 호출하여
adapter에게 값이 변경되었음을 알려주어야 리사이클러뷰가 갱신됨을 잊지 말아야 한다.
처음에 변경된 것을 알려주지 않았어서 화면에 뜨지 않아서 매우 고생했다 ㅠㅠ
notifyDataSetChanged()를 사용해도 되지만, 전체가 새로 고침되기 때문에
notifyItemRangeChanged()로 확실하게 적어주는 것이 좋다.
MainActivity.kt
package com.example.homework5
import android.content.ContentValues.TAG
import android.content.Intent
import android.graphics.Insets.add
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat.startActivityForResult
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.homework5.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var getResultText: ActivityResultLauncher<Intent>
private val dataList: ArrayList<Data> = arrayListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// onBindViewHolder 에서 listposition 을 전달받고 함수 실행
val dataRVAdapter = DataRVAdapter(dataList, onClickDeleteBtn = {
deleteTask(it) // deleteTask 함수가 포지션 값인 it을 받고 지운다.
})
// ActivityResult 를 받기 위한 Callback 등록
getResultText =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
val mString = result.data?.getStringExtra("data") ?: ""
Log.d(TAG, "onCreate: $mString")
dataList.apply {
add(Data("$mString"))
}
dataRVAdapter.notifyItemRangeInserted(dataList.size, 1) // 변경된 아이템의 시작 위치, 변경된 아이템 개수
}
}
// 메모 추가 버튼 누르면 메모 화면으로 넘어가는 것 && 위에서 정의한 것을 launch 함수로 시작
binding.btnAdd.setOnClickListener {
val mintent = Intent(this@MainActivity, MemoActivity::class.java)
getResultText.launch(mintent)
}
binding.rvData.adapter = dataRVAdapter // 리사이클러뷰에 어댑터 연결
binding.rvData.layoutManager = LinearLayoutManager(this) // 레이아웃 매니저 연결
}
fun deleteTask(data: Data) {
dataList.remove(data)
binding.rvData.adapter?.notifyDataSetChanged()
}
}
MemoActivity.kt
package com.example.homework5
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.homework5.databinding.ActivityMainBinding
import com.example.homework5.databinding.ActivityMemoBinding
class MemoActivity : AppCompatActivity() {
private lateinit var viewBinding: ActivityMemoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMemoBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
// MemoActivity 에서 MainActivity 로 data 전달
viewBinding.btnStore.setOnClickListener {
val mIntent = Intent(this, MainActivity::class.java).apply {
putExtra("data", viewBinding.edtText.text.toString())
}
setResult(RESULT_OK, mIntent)
if(!isFinishing) finish()
}
}
}
📌 최종 결과
'🍞 대외활동 > Univ Makeus Challenge' 카테고리의 다른 글
[UMC] Android 7주차 워크북 (Thread) (0) | 2022.11.17 |
---|---|
[UMC] Android 6주차 워크북 (고급 Layout과 View) (0) | 2022.11.07 |
[UMC] Android 5주차 워크북 (ListView) (0) | 2022.10.23 |
[UMC] Android 4주차 워크북 (LifeCycle) (0) | 2022.10.18 |
[UMC] Android 3주차 워크북 (Activity와 Fragment) (0) | 2022.10.15 |