
위의 사진과 같이 중첩리사이클러뷰를 구현 중에 있는데요.. MVVM 패턴을 공부하면서 진행중인데, 어떤식으로 코드를 넣어야할지 도저히 감이 안와서 힌트를 얻고자 질문드립니다.
우선 현재의 데이터 모델입니다.
@Entity(
tableName="task",
)
data class TaskEntity(
@PrimaryKey(autoGenerate = true)
var id : Int?,
var start_time : String,
var end_time : String,
var content : String,
@ColumnInfo(defaultValue = "NO")
var favorite : String,
@ColumnInfo(defaultValue = "NO")
var complete : String,
@ColumnInfo(defaultValue = "NO")
var alarm : String
)
그리고, 여기서부터 골머리가 아파오기 시작합니다.
DAO 파일입니다.
@Dao
interface ListDAO {
/**
* onConflict = REPLACE
* 기존의 기본키를 가진 값이 있는데, 나중에 해당 기본키에 다른 값을 넣으면,
* 넣은 값으로 대체된다는 의미이다. 쉽게 이야기해서 덮어쓰기의 개념.
*/
// Task Table CRD
// onConflict (충돌이 일어났을 때 덮어쓰기)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertTask(task : TaskEntity)
@Query("SELECT * FROM task")
fun getAllTask() : LiveData<List<TaskEntity>>
@Delete
fun deleteTask(task : TaskEntity)
@Update
fun updateTask(task: TaskEntity)
@Query("SELECT DISTINCT start_time FROM task ORDER BY start_time ASC")
fun getParentTask() : LiveData<List<String>>
@Query("SELECT * FROM TASK WHERE start_time = :start_time")
fun getChildTask(start_time : String) : List<TaskEntity>
}
위의 그림과 함께 보시면 아시겠지만, 우선 부모 리사이클러뷰에는 시작 날짜들을 세팅하고, 자식 리사이클러뷰에는 "시작 날짜에 속하는" 할일들만 들어갈 예정입니다.
사실 LiveData를 넣기 전에는 그럭저럭 위의 사진처럼 잘 돌아갔는데, LiveData를 넣으면서부터 어떻게 넣어야할지 도저히 감이 안잡히더라구요. 진짜 제가 멍청하다는걸 계속 깨달으면서 5일동안 고민해보아도 답이 안나오더라구요..
밑은 Repository, ViewModel 입니다.
class ListRepository(private val listDAO : ListDAO) {
val readAllData : LiveData<List<TaskEntity>> = listDAO.getAllTask()
val readParentData : LiveData<List<String>> = listDAO.getParentTask()
fun insertTask(task : TaskEntity) {
listDAO.insertTask(task)
}
fun deleteTask(task : TaskEntity) {
listDAO.deleteTask(task)
}
fun updateTask(task : TaskEntity) {
listDAO.updateTask(task)
}
fun readChildData(start_time : String) : List<TaskEntity> {
return listDAO.getChildTask(start_time)
}
}
// 뷰모델은 DB에 직접 접근 X
class ListViewModel(application : Application) : AndroidViewModel(application){
val readAllData : LiveData<List<TaskEntity>>
val readParentData : LiveData<List<String>>
private val repository : ListRepository
private var _childItem : MutableLiveData<ArrayList<TaskEntity>> = MutableLiveData(arrayListOf())
val childItem : LiveData<ArrayList<TaskEntity>>
get() = _childItem
// getter, setter
private var _currentData = MutableLiveData<List<TaskEntity>>()
val currentData : LiveData<List<TaskEntity>>
get() = _currentData
// 초기값 설정
init {
val listDAO = ListDatabase.INSTANCE!!.listDAO()
repository = ListRepository(listDAO)
readAllData = repository.readAllData
readParentData = repository.readParentData
}
fun insertTask(task : TaskEntity) {
repository.insertTask(task)
}
fun updateTask(task : TaskEntity) {
repository.updateTask(task)
}
fun deleteTask(task: TaskEntity) {
repository.deleteTask(task)
}
fun getChildTask(start_time : String) {
val tmp = repository.readChildData(start_time)
_currentData.postValue(tmp)
}
}
구글링도 여러번 해보고 그랬지만 중첩 리사이클러뷰는 정보가 너무.. 너무 없더라구요. 제가 ViewModel에 대한 이해도가 부족한 것도 굉장히 큰 비중을 차지하는 것 같구요..
밑은 프래그먼트와 부모 어댑터입니다. 자식 어댑터는 데이터 세팅하는게 다라서 일단 안넣겠습니다.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var currentTime : Long = System.currentTimeMillis()
var timeFormat = SimpleDateFormat("yyyy-MM-dd", Locale("ko","KR"))
today = timeFormat.format(Date(currentTime))
// 코틀린에서 apply 변수로 가독성 좋게 표현할 수 있음.
binding.listRcview.apply {
// this.addItemDecoration(dividerItemDecoration)
this.layoutManager = LinearLayoutManager(requireContext())
this.addItemDecoration(RecyclerViewDecoration(30))
}
// 현재 날짜 데이터 리스트 관찰하여 변경시 어댑터에 전달해줌
listViewModel.readParentData.observe(viewLifecycleOwner) {
Log.d("live Parent Data :: ", "ON")
binding.listRcview.adapter = parentAdapter
parentAdapter.setParent(it)
}
// . . . .
class ListParentAdapter(
val context: Context,
private val listViewModel: ListViewModel,
private val lifecycleOwner: LifecycleOwner
) : RecyclerView.Adapter<ListParentAdapter.ListParentViewHolder>() {
private var parentList = emptyList<String>()
var testList = emptyList<TaskEntity>()
fun setParent(task: List<String>) {
parentList = task
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return parentList.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListParentViewHolder {
val binding =
ItemListParentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ListParentViewHolder(binding)
}
@SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: ListParentViewHolder, position: Int) {
val parentDate = parentList[position]
Log.d("ParentDate :: ", parentDate)
holder.date.text = parentDate
var divideItemDecoration =
DividerItemDecoration(holder.child.context, LinearLayoutManager(context).orientation)
// ItemTouchHelper 를 RecyclerView 와 연결한다.
val swipeHelperCallback = SwipeHelperCallback().apply {
setClamp(200f)
}
val itemTouchHelper = ItemTouchHelper(swipeHelperCallback)
itemTouchHelper.attachToRecyclerView(holder.child)
holder.child.apply {
this.addItemDecoration(divideItemDecoration)
this.layoutManager = LinearLayoutManager(context)
// this.addItemDecoration(RecyclerViewDecoration(20))
this.hasFixedSize()
this.setOnTouchListener { _, _ ->
swipeHelperCallback.removePreviousClamp(this)
false
}
}
listViewModel.readAllData.observe(lifecycleOwner) {
test(parentDate)
}
listViewModel.currentData.observe(lifecycleOwner) {
testTwo(it, holder.child)
}
@SuppressLint("StaticFieldLeak")
fun test(parentDate: String) {
var testData : List<TaskEntity>? = null
val childTask =
object : AsyncTask<Unit, Unit, Unit>() {
override fun doInBackground(vararg p0: Unit?) {
listViewModel.getChildTask(parentDate)
}
}
childTask.execute()
}
우선 제가 생각해본 것은, 모델이 TaskEntity 하나만으로는 안될 것 같은데,, 부모 자식으로 나눠서 부모쪽에 ArrayList 를 주는게 맞는가 싶으면서도 어떤식으로 데이터를 넣어야할지 도무지 감이 안잡히더라구요.
피가 되고 살이 되도록 쓴 소리도 달게 받겠으니 부디 조언을 주세요 ㅠㅠ 어떤 식으로 고민을 해야할지 조차 모르겠어서 너무나도 힘이 듭니다,,,, 도와주세요...!!!