마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

체크박스 상태를 저장하려고하는데...

0 추천
data class Date(
        val name: String,
        val calendar_year: Int,
        var isChecked: Boolean = false
)
<CheckBox
    android:id="@+id/checkbox"
    android:checked="@{date.isChecked}"

 

리사이클러뷰에 isChecked를 이용해서 처음에는 false를 주고 체크 한 후 스크롤을 내렸다가 올리면 다시 false상태가 되어있어서 체크 시 data class에 isChecked 변수에 true값을 저장하려고하는데 만들고있는 list_test 액티비티에서 checkbox를 참조(?)를 못하고있어서 저장하는 기능을 못만들고 있는데 어떻게 해결해야하나요??

class list_test : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_list_test)
        //임시 Date 배열로 저장
        val example = arrayListOf<Date>()
        example.clear()
        val database : FirebaseDatabase = FirebaseDatabase.getInstance()

        for( i in 1..20){
            val myRef: DatabaseReference = database.getReference("List/list$i")

            myRef.addValueEventListener(object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val value = dataSnapshot?.value
                    example.add(Date("$value", 2022))
                    recycler_view.adapter?.notifyDataSetChanged()
                    recycler_view.adapter = DateAdapter(example.sortedBy { it.name }) { date ->
                    }
                    recycler_view.adapter?.notifyDataSetChanged()
                }

                override fun onCancelled(p0: DatabaseError) {
                    println("Failed to read value.")
                }

            })
        }

        recycler_view.apply {
            layoutManager = LinearLayoutManager(this@list_test)
            adapter = DateAdapter(example){ date ->
            }

        }

    }
}

//Adapter 설정
class DateAdapter(val items: List<Date>, private val clickListener: (date: Date) -> Unit) :
        RecyclerView.Adapter<DateAdapter.DateViewHolder>(){
    //ViewHolder 설정
    class DateViewHolder(var binding: ItemDateBinding): RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DateViewHolder {
        val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_date, parent, false)
        val viewHolder = DateViewHolder(ItemDateBinding.bind(view))

        view.setOnClickListener {
            clickListener.invoke(items[viewHolder.adapterPosition])
        }
        return viewHolder
    }

    override fun onBindViewHolder(holder: DateViewHolder, position: Int) {
        holder.binding.date = items[position]
    }
    override fun getItemCount(): Int = items.size
}

 

nagada32 (290 포인트) 님이 2021년 3월 21일 질문

3개의 답변

0 추천

액티비티에서 체크박스에 체크를 할 때 값을 받아서 example에 있는 데이터를 변경시킨 후 어댑터를 갱신시키시면 될 것 같은데요..

data class Date(
   val name: String,
   val year: Int,
   val isChedck: Boolean = false
)


// Actvity
private val example = arrayListOf<Date>()

adapter = DateAdapter(example){ date ->
     updateCalendarCheckedState(date)
}

private fun updateCalendarCheckedState() {
     val pos = example.indexOf(date)
     if (pos < 0)  {
            throw NoSuchItemException(""Cannot find $date)
     }

    val foundItem = example[pos]
    val updatedItem = foundItem.copy(isChecked = !foundItem.isChecked)
    example[pos] = updatedItem
  
    adpater.update(example)
    adapter.notifyItemChanged(pos)
}

 

해당 아이템을 찾아서 상태를 업데이트하는 부분은  map 같은 걸 쓰시면 조금 더 코틀린 스러운 코드가 되겠네요.

가능하다면 리사클러뷰 어댑터의 데이터를 가져오는 부분은 controll, presenter 또는 viewmodel 등을 통해 액티비테에서 분리하시는 것이 좋습니다. 이게 아주 일반적인 앱 구조입니다.

spark (224,800 포인트) 님이 2021년 3월 21일 답변
알려주신대로 추가해보려고하는데 어느 위치에 추가를 해야할까요??
안드로이드는 처음이라 힘드네요 ㅜㅠ
어느 부분이요? 어댑터 데이터를 가져오는 부분을 분리하고 싶은신 건가요, 아니면 updateCalendarCheckedState 를 적용하고 싶으신 건가요? 첫번째 거면 약간의 도움을 드릴 수 있고 두번째 거면 이미 제 답글에 다 나와있습니다.
저도 list_test에 너무 복잡하게 몰려있어서 분리를 하고 싶은데 그 전에 지금 알려주신 두번째 updateCalendarCheckedState적용하려고하는데 여기서부터 적용하는것도 벅차네요ㅎㅎ...
//Activity 해준 부분이 제 list_test activity에 추가해서 해결하시라는건지 아니면 새로운 activity를 만들어서 그곳에 위에 코드를 추가해서 실행하라는건지 잘 모르겠습니다. private val example = arrayListOf<Date>()이것은 제 class 밖으로 옮겼고 나머지
adapter = DateAdapter(example){ date ->
     updateCalendarCheckedState(date)
}
 
private fun updateCalendarCheckedState() {
     val pos = example.indexOf(date)
     if (pos < 0)  {
            throw NoSuchItemException(""Cannot find $date)
     }
 
    val foundItem = example[pos]
    val updatedItem = foundItem.copy(isChecked = !foundItem.isChecked)
    example[pos] = updatedItem
   
    adpater.update(example)
    adapter.notifyItemChanged(pos)
}

이 부분을 제 list_test 맨아래에 추가해보려고하는데
throw NosuchItemException(""Cannot find $date) 이 부분과
adapter.update(example)
이부분이 빨간색으로 오류가 나는데 이부분은 어떻게 해결해야할까요?
혹시 디자인 패턴을 공부해보신 적이 있으신지요? MVC, MPV, MVVM 패턴같은 것에 대해 전혀 모르신다면 그 부분을 공부해 보시길 먼저 추천드립니다. 기본적인 디자인패턴에이나 Object Oriented Modeling에 대한 이해가 없으면, 제가 뭐라고 말씀드려고 적용하기 힘드실 수 있어요. 여기서 설명을 드리기에는 내용이 많아서 적절하지 않을 것 같습니다.
다만 제가 말씀드린 분리의 기본원칙은 각자의 역할에 있습니다. 데이터를 가져오는 클래스는 그 역할만, 화면에 보여주는 클래스는 그것만 할 수있도록 분리하는 겁니다. 그래야 추후 변경이 생기거나 버그가 생기더라도 어디를 손대야할지 직관적으로 알 수 있게 됩니다. 그리고 데이터를 가져오는 클래스는 안드로이드 플랫폼에 종속적이지 않아야 합니다. 즉, 안드로이드 플랫폼에서 사용하는 context나 view와 같은 클래스를 참조하면 안됩니다. 그래야  유닛테스트가 가능해지고 변경사항에도 쉽게 대처할 수 있습니다.
제가 샘플로 보여드린 코드가 이해가 가지 않으시다면, 지금은 같은 클래스 안에 두시고 대신 작은 단위의 함수로 쪼개어 보세요. 함수의 이름이 어떤 동작을 하는지 명확하게 주셔서 말이죠. 그래서, 코드를 보면 어떤 동작을 하는구나 하고 쉽게 이해가 가도록 말이죠. 이 때문에 변수, 함수, 클래스 등 모든 요소의 이름을 잘 짓는 것은 읽기 쉬운 코드를 위해서 제일 먼저 그리고 많이 시간을 투자하셔야 하는 부분 중의 하나입니다. 그만큼 중요합니다.
getCalendarItems()
updateCalendatAdapter()
이런 식으로 말이죠.
private val example = arrayListOf<Date>()
class list_test : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_list_test)
        example.clear()
        val database : FirebaseDatabase = FirebaseDatabase.getInstance()

        for( i in 1..20){
            val myRef: DatabaseReference = database.getReference("List/list$i")

            myRef.addValueEventListener(object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val value = dataSnapshot?.value
                    example.add(Date("$value", 2022))
                    recycler_view.adapter?.notifyDataSetChanged()
                    recycler_view.adapter = DateAdapter(example.sortedBy { it.name }) { date ->
                    }
                    recycler_view.adapter?.notifyDataSetChanged()
                }

                override fun onCancelled(p0: DatabaseError) {
                    println("Failed to read value.")
                }

            })
        }
        recycler_view.apply {
            layoutManager = LinearLayoutManager(this@list_test)
            adapter = DateAdapter(example){ date ->

            }

        }

    }
    private val adapter = DateAdapter(example){ date ->
        updateCalendarCheckedState(date)
    }
    private fun updateCalendarCheckedState(date: Date) {
        val pos = example.indexOf(date)
        if (pos < 0)  {
            throw NosuchItemException(""Cannot find $date)
        }

        val foundItem = example[pos]
        val updatedItem = foundItem.copy(isChecked = !foundItem.isChecked)
        example[pos] = updatedItem

        adapter.update(example)
        adapter.notifyItemChanged(pos)
    }

}
//Adapter 설정
class DateAdapter(val items: List<Date>, private val clickListener: (date: Date) -> Unit) :
        RecyclerView.Adapter<DateAdapter.DateViewHolder>(){
    //ViewHolder 설정
    class DateViewHolder(var binding: ItemDateBinding): RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DateViewHolder {
        val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_date, parent, false)
        val viewHolder = DateViewHolder(ItemDateBinding.bind(view))

        view.setOnClickListener {
            clickListener.invoke(items[viewHolder.adapterPosition])
        }
        return viewHolder
    }

    override fun onBindViewHolder(holder: DateViewHolder, position: Int) {
        holder.binding.date = items[position]
    }
    override fun getItemCount(): Int = items.size
    
   
}

이런 식으로 추가를 했는데
adapter.update(example) 쪽에 update에 빨간글씨로 Unresolved reference: update 이런 오류가 발생하고
throw NosuchItemException(""Cannot find $date) 이쪽도
NosuchItemException과 Cannot find 쪽에 빨간글씨로 오류가 납니다.
NosuchItemException과 Cannot은 Unresolved reference: 오류
find는  Function invocation 'find(...)' expected 이런 오류인데 어떻게 해결할까요??
0 추천

example의  scope(범위는) Activity 안에 있어야 합니다. 그리고 해당 에러는 함수들이 제대로 정의되지 않아서 나오는 에러입니다. 제가 기본적인 샘플을 올려드릴게요. 참고하셔서 더 깔금하게 만드시면 좋을 것 같습니다.

 

// 기본적으로 데이터를 가져오는 부분과 화면을 담당하는 부분은 데이터 클래스도 분리해서 사용하는게 좋습니다. 구조가 같더라고 말이죠.
// 님의 코드를 기반으로 임의로 만든 클래스입니다. 클래스 이름이 *Schema로 끝나는 이유는 이 클래스는 데이터 레이어에서 사용되는 클래스라고 알려주는 명명규칙입니다.
data class DateSchema(
    val value: String,
    val year: Int
)

// 이건 UseCase라고 불리는 클래스입니다. 이 클래스는 데이터 레이어(여기서는 FirebaseDatabase)를 가져와서 비지니스 로직을 구현합니다.
// 이 클래스는 아주 중요합니다. 따라서 이 클래스를 중점으로 유닛테스트를 하면 좋습니다.
// 이 클래스는 옵저버 패턴을 사용하고 있습니다. 님이 딱히 RxJava나 coroutine같은 걸 안쓰시는 것 같아 콜백을 사용하여 처리할 수 있는 방법으로 구현했습니다.
// Listener 인터페이스를 구현한 클래스는 모두 옵저버가 될 수 있으며, 이 클래스의 인스턴스에 registerListener를 통하여 등록을 해주면, 이벤트가 발생할 때 콜백을 통해
// 알려주게 됩니다. 등록을 해제할 때는 unregisterListener 를 호출합니다.
// 생성자에 FirebaseDatabase를 넘겨주고 있는데, 이렇게 하면 나중에 유닛테스트를 할 때, test double이나 Mock을 사용하여 테스트를 만들기가 아주 쉬워집니다.
// 더 나아가 FirebaseDatabase 자체도 다른 클래스 안에 집어넣어서 직접 사용하지 않게 할 수도 있습니다. 이게 더 좋은 구조입니다. 어떤 라이브러리를 사용할 때는 가능하면 
// 직접 사용하기보다는 한번 다른 클래스로 감싸서 사용하는 것이 의존성을 훨씬 줄여줄 수 있는 더 좋은 코드입니다.
class GetCalendarUseCase(
    private val firebaseDatabase: FirebaseDatabase
) {

    interface Listener {
        fun onCalendarFetched(dates: List<DateSchema>)
        fun onCalendarFetchFailed(e: DatabaseError)
    }

    private val listeners = hashSetOf<Listener>()

    fun registerListener(listener: Listener) {
        listeners.add(listener)
    }

    fun unregisterListener(listener: Listener) {
        listeners.remove(listener)
    }

    fun fetch() {
        val schemas = listOf<DateSchema>()

        // 파이어베이스에서 데이터를 가져오는 부분. 이 부분은 제가 알 수 없는 부분이라 님의 코드를 그대로 붙여넣었습니다.
        // 꼭 확인해 보셔야할 부분은 20번이나 루프를 돌아야 하는게 이상합니다. 이러면 속도가 떨어집니다. 제 생각에는 한번에 원하시는 리스트를 가져올 수 있는 쿼리가 반드시 있을 것 같습니다.
        for( i in 1..20){
            val myRef: DatabaseReference = database.getReference("List/list$i")
            myRef.addValueEventListener(object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val value = dataSnapshot?.value
                    //example.add(Date("$value", 2022))
                    //recycler_view.adapter?.notifyDataSetChanged()
                    //recycler_view.adapter = DateAdapter(example.sortedBy { it.name }) { date ->
                    //}
                    //recycler_view.adapter?.notifyDataSetChanged()
                }

                override fun onCancelled(e: DatabaseError) {
                    notifyCalendarFetchFailed(e)
                }

            })

            
        }
 
      // 위에서 리스트를 가져오신 다음 등록된 리스너에게 알려줍니다.
        notifyCalendarFetched(schemas)

        //에러시, notifyCalendarFetchFailed() 호출
    }

    // 리스너들에게 데이터를 가져왔다는 이벤트 통보
    private fun notifyCalendarFetched(schemas: List<DateSchema>) {
        for (listener in listeners) {
            listener.onCalendarFetched(schemas)
        }
    }

    // 리스너들에게 데이터 가져오기에 실패했다는 이벤트 통보
    private fun notifyCalendarFetchFailed(error: DatabaseError) {
        for (listener in listeners) {
            listener.onCalendarFetchFailed(error)
        }
    }
}


========= 여기서부터는 뷰레이어입니다.
// 어댑터에 사용할 클래스. 이름은 님이 코드만 봐서는 더 좋은 이름이 바로 떠오르지 않아 이렇게 주었습니다. 가능하면 좀 더 명확한 이름이 좋습니다. 
// 다시 한번 말씀드리지만, clean code를 작성하는데 Naming은 정말 중요합니다.
data class DateItem(
    val value: String,
    val year: String
)

// 생성자에 List를 넘겨주는 부분을 사용하지 않아서 뺏습니다. 그리고 lambda 생성자 변수의 이름도 좀 더 명확하게 주었습니다.
class DateAdapter(
    private val dateItemClicked: (item: DateItem) -> Unit
): RecyclerView.Adapter<DateViewHolder>() {

    private val calendarItems = arrayListOf<DateItem>()

    // 파이어베이스에서 데이터를 받으면 어댑터를 갱신해야 하므로 이 부분은 꼭 필요합니다.
    fun submitList(items: List<DateItem>) {
        calendarItems.clear()
        calendarItems.addAll(items)
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int  = calendarItems.size

    // 아주 드물지만 리사이클러뷰의 레이아웃이 완전히 준비되기 전에 아이템에 액세할 경우가 생깁니다. 이때 position이 -1이 되기 때문에 방어적인 코딩차원에서 null을 리턴할 수 있도록 했고,
    // onViewBindHolder에서 null인 경우는 무시를 하고 있습니다.
    private fun getItem(position: Int): DateItem? = calendarItems.getOrNull(position)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DateViewHolder {
        // 뷰바인딩을 뷰홀더로 넘겨줍니다.
        val itemBinding = ItemDateBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return DateViewHolder(itemBinding, dateItemClicked)
    }

    override fun onBindViewHolder(holder: DateViewHolder, position: Int) {
        val item = getItem(position) ?: return
        holder.bind(item)
    }

}

// adpaterPosition은 리스트 아이템 구성에 따라 다른 값을 리턴할 수 있기 때문에 전 개인적으로 사용하지 않습니다. 대신 bind에서 리스너를 초기화 줍니다. 이건 개인적인 스타일입니다.
class DateViewHolder(
    private val binding: ItemDateBinding,
    private val dateItemClicked: (item: DateItem) -> Unit
): RecyclerView.ViewHolder(binding.root) {

    fun bind(item: DateItem) {
        binding.titleTxt.text = item.value
        itemView.setOnClickListener {
            dateItemClicked(item)
        }
    }
}



// 액티비티입니다.
class MainActivity : AppCompatActivity(), GetCalendarUseCase.Listener {

    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    private val context: Context get() = this@MainActivity
    private val calendarAdapter = DateAdapter {

    }

    private val dateItemMapper by lazy { DateItemMapper() }

    private lateinit var getCalendarUseCase: GetCalendarUseCase

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        setupDependencies()
        setupViews()
    }

    private fun setupDependencies() {
        getCalendarUseCase = GetCalendarUseCase(FirebaseDatabase.getInstance())
    }

    private fun setupViews() {
        binding.calendarRcv.apply {
            adapter = calendarAdapter
        }
    }

    override fun onDestroy() {
        // RecyclerView의 Adapter를 명시적으로 해제해주지 않으면 생각보다 많은 상황에서 메모리 누수가 발생합니다. 그래서 강제로  null로 만들어서 가비지 콜렉터가 메모리를 회수할 수 있도록 도와줍니다.
        binding.calendarRcv.adapter = null
        super.onDestroy()
    }

    // onStop에서 getCalendarUseCase에 등록하고 onStop에서 등록해제를 하는 부분은 아주 중요합니다. 이렇게 하는 이유는 앱이 백그라운드로 빠지면 이벤트를 더이상 받지 않게 하기 위함이고
    // MainActivity가 종료되면 자연스럽게 옵저버를 등록해제해서 가비지 콜렉터가 메모리를 회수할 수 있도록 해주기 위함입니다.
    override fun onStart() {
        super.onStart()
        getCalendarUseCase.registerListener(this)
        fetchCalendar()
    }


    private fun fetchCalendar() {
        getCalendarUseCase.fetch()
    }

    override fun onStop() {
        super.onStop()
        getCalendarUseCase.unregisterListener(this)
    }

    override fun onCalendarFetched(dates: List<DateSchema>) {
        calendarAdapter.submitList(dateItemMapper.schemasToUiEntities(dates))
    }

    override fun onCalendarFetchFailed(e: DatabaseError) {
        // show error message
    }
}

 

spark (224,800 포인트) 님이 2021년 3월 24일 답변
저의 프로젝트에는 xml파일이

activity_list_test.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=".list_test">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

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">

    <com.prolificinteractive.materialcalendarview.MaterialCalendarView
        android:id="@+id/calendarView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"
        android:layout_weight="1"
        app:layout_constraintBottom_toTopOf="@+id/recyclerView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:mcv_selectionColor="#00F"
        app:mcv_showOtherDates="defaults" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/plus_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:clickable="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:srcCompat="@android:drawable/ic_input_add" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        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_toBottomOf="@+id/calendarView" />

</androidx.constraintlayout.widget.ConstraintLayout>

item_date.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <variable
//dateschema 사용하시라고 해서 name을 dateschema로 변경했습니다.
            name="dateschema"
            type="com.example.calendar1.DateSchema" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="86dp">

        <TextView
            android:id="@+id/age_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="295dp"
            android:layout_marginBottom="24dp"
// String.valueOf(date.year) >> String.valueOf(dateschema.year)로 변경
            android:text="@{String.valueOf(dateschema.year)}"
            android:textSize="24sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

        <CheckBox
            android:id="@+id/checkBox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="28dp"
            android:layout_marginBottom="20dp"
            android:fontFamily="@font/nexon_medium"
//이 부분도 date 빼고 dateschema로 변경했습니다.
            android:text="@{dateschema.value}"
            android:textSize="24sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

DateSchema.kt

data class DateSchema(
        val value: String,
        val year: Int
)

DateItem.kt

data class DateItem(
        val value: String,
        val year: String
)
class DateAdapter(
        private val dateItemClicked: (item: DateItem) -> Unit
): RecyclerView.Adapter<DateViewHolder>() {

    private val calendarItems = arrayListOf<DateItem>()
    fun submitList(items: List<DateItem>) {
        calendarItems.clear()
        calendarItems.addAll(items)
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int  = calendarItems.size
    
    private fun getItem(position: Int): DateItem? = calendarItems.getOrNull(position)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DateViewHolder {
        
        val itemBinding = ItemDateBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return DateViewHolder(itemBinding, dateItemClicked)
    }

    override fun onBindViewHolder(holder: DateViewHolder, position: Int) {
        val item = getItem(position) ?: return
        holder.bind(item)
    }

}

class DateViewHolder(
        private val binding: ItemDateBinding,
        private val dateItemClicked: (item: DateItem) -> Unit
): RecyclerView.ViewHolder(binding.root) {

    fun bind(item: DateItem) {
//binding.titletxt.text = item.value로 되어있는데 제가 쓰는 text뷰는 ageText밖에 없어서 이걸로 변경했는데 맞을까요?
        binding.ageText.text = item.value
        itemView.setOnClickListener {
            dateItemClicked(item)
        }
    }
}

그리고 제가 기존에 사용했던 list_test.kt에 알려주신 액티비티를 복붙해봤습니다

list_test.kt

class list_test : AppCompatActivity(), GetCalendarUseCase.Listener {
//현재 이 부분에 ActivityMainBinding이 오류가 발생하는데 이는 제가 main.xml이 아닌 list_test쪽을 써야하는거같은데 ActivityList_testBinding으로 넣으려고 해도 뷰 바인딩이 안되어있는건지 참조가 안됩니다.
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    private val context: list_test get() = this@list_test
    private val calendarAdapter = DateAdapter {

    }
//그리고 이부분에 DateItemMapper도 오류가 발생하는데 이또한 참조가 안된다고합니다.
    private val dateItemMapper by lazy { DateItemMapper() }

    private lateinit var getCalendarUseCase: GetCalendarUseCase

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        setupDependencies()
        setupViews()
    }

    private fun setupDependencies() {
        getCalendarUseCase = GetCalendarUseCase(FirebaseDatabase.getInstance())
    }

    private fun setupViews() {
        binding.calendarRcv.apply {
// 여기 adapter 도 참조가 안됩니다.
            adapter = calendarAdapter
        }
    }

    override fun onDestroy() {
      
        binding.calendarRcv.adapter = null
        super.onDestroy()
    }
    
    override fun onStart() {
        super.onStart()
        getCalendarUseCase.registerListener(this)
        fetchCalendar()
    }


    private fun fetchCalendar() {
        getCalendarUseCase.fetch()
    }

    override fun onStop() {
        super.onStop()
        getCalendarUseCase.unregisterListener(this)
    }

    override fun onCalendarFetched(dates: List<DateSchema>) {
        calendarAdapter.submitList(dateItemMapper.schemasToUiEntities(dates))
    }

    override fun onCalendarFetchFailed(e: DatabaseError) {
        // show error message
    }
}

이렇게 기존에 하던거에 올려주신 샘플을 넣어서 실행해보려고하는데 조금씩의 오류가 발생하는데 어떻게 해결해야할까요?
0 추천

기본적으로 어떤 부분을 손대셔야 할지 아실거라고 생각하고 올려두신 코드였는데, 이해가 가지 않으시는 걸 보니 제 샘플코드는 쓰지 마시고 님께서 이해가 가는 만큼만 조금씩 손을 대세요. 몇가지 코멘트를 달아드리면,

class list_test : AppCompatActivity(), GetCalendarUseCase.Listener {


    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }


 ActivityList_testBinding으로 넣으려고 해도 뷰 바인딩이 안되어있는건지 참조가 안됩니다.
현재 이 부분에 ActivityMainBinding이 오류가 발생하는데 이는 제가 main.xml이 아닌 list_test쪽을 써야하는거같은데
ViewBinding 클래스는은  xml 레이아웃의 파일의 이름을에 따라 자동으로 생성됩니다. _는 제거되고 Java의  클래스명명규칙이 적용됩니다. ActivityListTestBinding이 맞을 것 같네요.
생성된 클래스이름이 헷갈리시면 안드로이드 스튜디오 검색기능을 이용하셔서 Binding이라고 쳐보세요. 님의  xml파일에 맞는 생성된 클래스를 검색결과 목록에서 찾으실 수 있을 겁니다.

안드로이드 개발문서에서  ViewBinding에 대해 기본적인 부분을 읽어보세요. https://developer.android.com/topic/libraries/view-binding

 private val dateItemMapper by lazy { DateItemMapper() }
//그리고 이부분에 DateItemMapper도 오류가 발생하는데 이또한 참조가 안된다고합니다.
Mapper 클래스는 단순히 필드들을 맵핑해주면 됩니다. 별반 로직이 없어요. 님이 사용하는 클래스 타입에 맞춰서 하나 만드세요. 바로 밑의 코드처럼 그냥 필드 대 필드를 매칭시켜주시면 됩니다.
        val dateItems = schmas.map { schma ->
            DateItem( value = schema.value, year = schema.year)
        }

   
    private fun setupViews() {
        binding.calendarRcv.apply {
// 여기 adapter 도 참조가 안됩니다.
이유는 binding.calendarRev.adapter를 참조하기 때문입니다. 님의 binding은 클래스가 다르니까 당연히 안될 겁니다. apply를 사용하면 apply를 사용한 인스턴스로 apply의 범위가 제한이 됩니다.
            adapter = calendarAdapter
        }
    }

  }

spark (224,800 포인트) 님이 2021년 3월 24일 답변
...