Всем привет! Пишу таск трекер. Хочу по нажатию на название задачи открывать детали о ней в другом фрагменте. Сразу говорю на всякий случай что юзаю data class вместо view model здесь. Данные передаю через deeplink. Выдает ошибку required value was null. Подозреваю что дело в самом диплинке а именно в curTodo. Если не писать в этом диплинки аргументы, а просто URI для простой навигации
app://taskmaster.taskDetailFragment то все гуд, а вот если писать как в исхоном коде, то начинаются проблемы. Что делать не знаю, срочно надо как-то починиться. Буду благодарна за помощь!
TodoAdapter
class TodoAdapter(
private var todos: MutableList<Todo>, private val todoDao: TodoDao
):RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {
class TodoViewHolder( ItemTodoBinding: ItemTodoBinding): RecyclerView.ViewHolder(ItemTodoBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val ItemTodoBinding = ItemTodoBinding.inflate(layoutInflater, parent, false)
return TodoViewHolder(
return TodoViewHolder(ItemTodoBinding)
)
}
private fun toggleStrikeThrough(tvTodoTitle: TextView, isChecked:Boolean){
if(isChecked){
tvTodoTitle.paintFlags = tvTodoTitle.paintFlags or STRIKE_THRU_TEXT_FLAG
}else{
tvTodoTitle.paintFlags = tvTodoTitle.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
}
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val curTodo = todos[position]
holder.itemView.apply{
val tvTodoTitle = findViewById(R.id.tvTodoTitle) as TextView
tvTodoTitle.text = curTodo.title
val cbDone = findViewById(R.id.cbDone) as CheckBox
val deleteBtn = findViewById(R.id.deleteBtn) as Button
cbDone.isChecked = curTodo.isChecked
toggleStrikeThrough(tvTodoTitle, curTodo.isChecked)
cbDone.setOnCheckedChangeListener { _, isChecked ->
toggleStrikeThrough(tvTodoTitle, isChecked)
curTodo.isChecked = !curTodo.isChecked
}
tvTodoTitle.setOnClickListener {
val request = NavDeepLinkRequest.Builder
.fromUri("${TASK_DETAIL_URI}?${TASK_BUNDLE_KEY}=${curTodo.title}&${TASK_DATE_BUNDLE_KEY}=${curTodo.date}&${TASK_DESCR_BUNDLE_KEY}=${curTodo.description}".toUri())
.build()
findNavController().navigate(request)
}
cbDone.setOnClickListener(View.OnClickListener{
GlobalScope.launch {
todoDao.update(curTodo)
todoDao.delete(curTodo)
}
todos.removeAll { todo ->
todo.isChecked
}
notifyDataSetChanged()
})
deleteBtn.setOnClickListener{
GlobalScope.launch {
todoDao.delete(curTodo)
}
notifyDataSetChanged()
}
}
}
override fun getItemCount(): Int {
return todos.size
}
}
TaskDetailFragment
class TaskDetailFragment : Fragment() {
private var _binding: FragmentTaskDetailBinding? = null
private val binding: FragmentTaskDetailBinding
get() = checkNotNull(_binding)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val title = arguments?.getString(TASK_BUNDLE_KEY)
val date = arguments?.getString(TASK_DATE_BUNDLE_KEY)
val descr = arguments?.getString(TASK_DESCR_BUNDLE_KEY)
binding.taskTitle.setText(title.toString())
binding.selectedDate.setText(date.toString())
binding.selectedDescription.setText(descr.toString())
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentTaskDetailBinding.inflate(inflater, container, false)
return binding.root
}
}
TaskListFragment
class TaskListFragment : Fragment(R.layout.fragment_task_list) {
private lateinit var todoAdapter: TodoAdapter
private var _binding: FragmentTaskListBinding? = null
private lateinit var todoDao: TodoDao
private val binding: FragmentTaskListBinding
get() = checkNotNull(_binding)
private var todos: MutableList<Todo> = mutableListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val appDatabase = AppDatabase.getInstance(this.requireActivity())
todoDao = appDatabase.todoDao()
todoDao.getAllTasks().observe(this, Observer { todoList ->
todos.clear()
todos.addAll(todoList)
todoAdapter.notifyDataSetChanged()
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentTaskListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
todoAdapter = TodoAdapter(todos, todoDao)
binding.rvTodoItems.adapter = todoAdapter
binding.rvTodoItems.layoutManager = LinearLayoutManager(this.activity)
binding.btnAddTodo.setOnClickListener {
NewTaskSheet().show(requireActivity().supportFragmentManager, "NewTaskTag")
}
requireActivity().supportFragmentManager.setFragmentResultListener(
"requestKey",
this
) { requestKey, bundle ->
val newTodo = bundle.getSerializable("bundleKey") as Todo
GlobalScope.launch {
todoDao.insert(newTodo)
}
todoAdapter.notifyDataSetChanged()
}
binding.rvTodoItems.addItemDecoration(
DividerItemDecoration(
binding.rvTodoItems.getContext(),
DividerItemDecoration.VERTICAL
)
)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object{
const val TASK_DETAIL_URI = "app://taskmaster.taskDetailFragment"
}
}
task_graph
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/task_graph"
app:startDestination="@id/taskListFragment">
<fragment
android:id="@+id/taskListFragment"
android:name="com.bignerdranch.android.taskmaster.TaskListFragment"
android:label="TaskListFragment" />
<fragment
android:id="@+id/taskDetailFragment"
android:name="com.bignerdranch.android.taskmaster.TaskDetailFragment"
android:label="TaskDetailFragment">
<deepLink
android:id="@+id/deepLink"
app:uri="app://taskmaster.taskDetailFragment?taskBundleKey={taskBundleKey}&taskDateBundleKey={taskDateBundleKey}" />
<argument
android:name="taskBundleKey"
app:argType="string" />
<argument
android:name="taskDateBundleKey"
app:argType="string" />
</fragment>
</navigation>