0

I am making a to do list and I am using multiple type recyclerview for it. If the task in the list has not been done, if it has been done under the Active title, it will be under the Done title. So I need to put two Headers for Active and Done headers, but I couldn't do this. I would be glad if you help.

I drew a picture to explain better. I want to add Active and Done headers here.

to do list with headers

Adapter Class

class ToDoListAdapter(var onItemClicked: ((item: ToDoData) -> Unit?)? = null) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        const val VIEW_TYPE_ACTIVE = 1
        const val VIEW_TYPE_DONE = 2
    }

    var dataList = emptyList<ToDoData>()

    class MyViewHolderActive(val binding: RowItemActiveBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(toDoData: ToDoData) {
            binding.toDoData = toDoData
            binding.executePendingBindings()
        }

        companion object {
            fun from(parent: ViewGroup): MyViewHolderActive {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = RowItemActiveBinding.inflate(layoutInflater, parent, false)
                return MyViewHolderActive(binding)
            }
        }
    }

    class MyViewHolderDone(val binding: RowItemDoneBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(toDoData: ToDoData) {
            binding.toDoData = toDoData
            binding.executePendingBindings()
        }

        companion object {
            fun from(parent: ViewGroup): MyViewHolderDone {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = RowItemDoneBinding.inflate(layoutInflater, parent, false)
                return MyViewHolderDone(binding)
            }
        }
    }

    override fun getItemViewType(position: Int): Int {
        return dataList[position].viewType
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if (viewType == VIEW_TYPE_ACTIVE) {
            return MyViewHolderActive.from(parent)
        }
        return MyViewHolderDone.from(parent)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val currentItem = dataList[position]

        if (currentItem.viewType == VIEW_TYPE_ACTIVE) {
            (holder as MyViewHolderActive).bind(currentItem)

            holder.binding.imageViewDeleteActive.setOnClickListener {
                onItemClicked?.invoke(currentItem)
            }
        } else {
            (holder as MyViewHolderDone).bind(currentItem)

            holder.binding.imageViewDeleteDone.setOnClickListener {
                onItemClicked?.invoke(currentItem)
            }
        }
    }

    override fun getItemCount(): Int {
        return dataList.size
    }

    fun setData(newList: List<ToDoData>) {
        this.dataList = newList
        notifyDataSetChanged()
    }
}

Model class

data class ToDoData(
    var id: Int,
    var viewType: Int,
    var title: String,
    var description: String
)

ToDoFragment

class ToDoFragment : Fragment() {
    private var _binding: FragmentToDoBinding? = null
    private val binding get() = _binding!!

    private val toDoViewModel: ToDoViewModel by viewModels()
    private val adapter: ToDoListAdapter by lazy { ToDoListAdapter() }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentToDoBinding.inflate(inflater, container, false)

        setAdapter()

        toDoViewModel.getAllData.observe(viewLifecycleOwner, { data ->
            adapter.setData(data)
        })

        binding.buttonAddTask.setOnClickListener {
            addTaskToDatabase()
        }

        return binding.root
    }

    private fun addTaskToDatabase() {
        val title = binding.editTextTitle.text.toString()
        val description = binding.editTextDescription.text.toString()
        val newTask = ToDoData(0, 1, title, description)
        toDoViewModel.insertData(newTask)
        clearInputFields()
    }

    private fun clearInputFields() {
        binding.editTextTitle.text.clear()
        binding.editTextDescription.text.clear()
    }

    private fun setAdapter() {
        val recyclerView = binding.recyclerViewTasks
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(requireContext())

        adapter.apply {
            onItemClicked = { currentItem ->
                toDoViewModel.deleteOrUpdateData(currentItem)
            }
        }
    }

}
alicealice
  • 29
  • 4

1 Answers1

0

You need to use groupBy

Your new model class

data class ToDoData(
    var headerType: String,
    var innerList: MutableList<InnerToDoData>,
) {
    data class InnerToDoData(
        var id: Int,
        var viewType: Int,
        var title: String,
        var description: String

    )
}
data.groupBy { it.headerType }.entries.map { groupedData ->
    //from here you will have a  Map.Entry<String?, List<ToDoData>>

    //from here to can create an inner list
    groupedData.value.forEach { innerData ->
        duration += innerTask.taskIntervalTime!!.toFloat()
        innerList.add(
            ToDoData.InnerTaskModel(
                "" + innerData.title,
                "" + innerData.description
            )
        )
    }

    //this is main list
    mainList.add(
        ToDoData(
            groupedData.key,
            innerList
        )
    )
}

What we are doing in above code is taking a list and converting it to a Map of String(key) and List(value) on the base of headerType value in the original list.

To properly use this you need to create nested recyclerViews and adapters

Update: 31/12/21

Design of your fragment should look like this

<Fragment>
    ...
    <RecyclerView
        ...
        tools:listitem="@layout/item_todo_layout"
    />
    ...
</Fragment>

Your item_todo_layout.xml

<LinearLayout>
    ...
    <TextView
        android:id="@+id/itemTodoHeaderView"
        ...     
    />

    <RecyclerView
        ...
        tools:listitem="@layout/item_todo_inner_layout"
    />
    ...
</LinearLayout>

Your item_todo_inner_layout.xml

<LinearLayout>
    ...
    <TextView
        android:id="@+id/itemTodoInnerTitleView"
        ...     
    />

    <TextView
        android:id="@+id/itemTodoInnerDescView"
        ...     
    />
    ...
</LinearLayout>

Similarly you need to change your adapter class

Please take a look at this answer also

Rahul Gaur
  • 1,661
  • 1
  • 13
  • 29
  • I have also added my ToDoFragment code above, can you please show me where to write the code you said in ToDoFragment? I'm a beginner and I'm having a hard time, thanks in advance. – alicealice Dec 31 '21 at 09:14
  • @alicealice please check my updated answer – Rahul Gaur Dec 31 '21 at 09:37