NavArgs 返回 RuntimeException

发布于 2025-01-12 16:09:48 字数 5680 浏览 6 评论 0原文

NavArgs 没有返回值,当我尝试通过更新片段中的 setOnClickListener 进行更新时,我得到了这个:

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException



Caused by: java.lang.IllegalArgumentException: Required argument "currentAlarm" is missing and does not have an android:defaultValue

我需要从 RecyclerView 中选择的警报返回 ID,因此默认值在这里没有意义。

NavArgs 在导航视图中正确设置,没有默认值。 autoGenerate 的型号和 ID 是否设置错误?或者是通过适配器来解决这里的问题?

更新片段:

class UpdateFragment : Fragment() {

private val timePickerUtil = TimePickerUtil()
lateinit var binding: FragmentUpdateBinding
private lateinit var alarmViewModel: AlarmViewModel
private val args by navArgs<UpdateFragmentArgs>()

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

    alarmViewModel = ViewModelProvider(this)[AlarmViewModel::class.java]

    binding.fragmentBtnUpdateAlarm.setOnClickListener {
        updateAlarm()
        Navigation.findNavController(requireView())
            .navigate(R.id.action_updateFragment_to_homeFragment)
    }
    return binding.root
}

private fun updateDatabase(id: Int, hour: Int, minute: Int, repeat: Boolean) {
    val alarm = Alarm(id, hour, minute, repeat)
    alarmViewModel.update(alarm)
}

private fun updateAlarm() {
    val timePicker = binding.fragmentUpdateAlarmTimePicker
    val id = args.currentAlarm.id
    val hour = timePickerUtil.getTimePickerHour(timePicker)
    val minute = timePickerUtil.getTimePickerMinute(timePicker)
    val repeat = binding.fragmentUpdateAlarmRecurring.isChecked

    val alarmManager = AlarmManager(
        id,
        hour,
        minute,
        true,
        binding.fragmentUpdateAlarmRecurring.isChecked
    )

    updateDatabase(id, hour, minute, repeat)
    alarmManager.cancel(requireContext())
    alarmManager.schedule(requireContext())
}

}

适配器:

class AlarmListAdapter() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {

class MyViewHolder(binding: LayoutAlarmBinding) : RecyclerView.ViewHolder(binding.root)

private var alarmList = ArrayList<Alarm>()


private var onItemClickListener: OnItemClickListener? = null

interface OnItemClickListener{
    fun onClick(alarm: Alarm)
    fun onLongClick(alarm: Alarm)
}

fun setOnItemClickListener(listener: OnItemClickListener){
    onItemClickListener = listener
}


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val binding = LayoutAlarmBinding.inflate(LayoutInflater.from(parent.context))
    return MyViewHolder(binding)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val currentItem = alarmList[position]
    val minute = currentItem.minute
    holder.itemView.findViewById<TextView>(R.id.tv_alarm_time).text =
        if (minute >= 10) {
            "${currentItem.hour}:${currentItem.minute}"
        } else {
            "${currentItem.hour}:0${currentItem.minute}"
        }

    holder.itemView.setOnClickListener{
        if(onItemClickListener != null){
            onItemClickListener?.onClick(currentItem)
        }
    }

    holder.itemView.setOnLongClickListener {
        if(onItemClickListener != null){
            onItemClickListener?.onLongClick(currentItem)
        }
        true
    }
}

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

fun setData(alarm: List<Alarm>) {
    alarmList.clear()
    alarmList.addAll(alarm)
    notifyDataSetChanged()
}

}

HomeFragment:

class HomeFragment : Fragment() {

lateinit var binding: FragmentHomeBinding
private lateinit var alarmViewModel: AlarmViewModel

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

    // RecyclerView
    val adapter = AlarmListAdapter()
    val recyclerView = binding.recyclerView
    recyclerView.adapter = adapter
    recyclerView.layoutManager = LinearLayoutManager(requireContext())

    //ViewModel
    alarmViewModel = ViewModelProvider(this).get(AlarmViewModel::class.java)
    alarmViewModel.readAlarmData.observe(viewLifecycleOwner, Observer { alarm ->
        adapter.setData(alarm)
    })

    binding.btnAddAlarm.setOnClickListener {
        Navigation.findNavController(requireView())
            .navigate(R.id.action_homeFragment_to_newAlarmFragment)
    }

    adapter.setOnItemClickListener(object : AlarmListAdapter.OnItemClickListener {

        override fun onClick(alarm: Alarm) {
            Navigation.findNavController(requireView())
                .navigate(R.id.action_homeFragment_to_updateFragment)
        }

        override fun onLongClick(alarm: Alarm) {
            val deleteBuilder = AlertDialog.Builder(requireContext())
            deleteBuilder.setPositiveButton("Delete") { _, _ ->
                alarmViewModel.delete(alarm)
                Toast.makeText(context, "Alarm Deleted", Toast.LENGTH_SHORT)
                    .show()
            }
            deleteBuilder.setNegativeButton("Cancel") { _, _ ->

            }
            deleteBuilder.setTitle("Delete Alarm?")
            deleteBuilder.create().show()
        }
    })

    return binding.root
}

}

和我的警报模型:

@Parcelize
@Entity(tableName = "alarm_table")
data class Alarm(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val hour: Int,
    val minute: Int,
    val repeat: Boolean
): Parcelable

NavArgs not returning a value, I just get this when I try to update through the setOnClickListener in update fragment:

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException



Caused by: java.lang.IllegalArgumentException: Required argument "currentAlarm" is missing and does not have an android:defaultValue

I need to return the ID from the alarm selected in the RecyclerView, so a default value wouldn't make sense here.

The NavArgs are set in the nav-view correctly, with no default. Could the model and ID be set up wrong with autoGenerate? or is going via the adapter the problem here?

update fragment:

class UpdateFragment : Fragment() {

private val timePickerUtil = TimePickerUtil()
lateinit var binding: FragmentUpdateBinding
private lateinit var alarmViewModel: AlarmViewModel
private val args by navArgs<UpdateFragmentArgs>()

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

    alarmViewModel = ViewModelProvider(this)[AlarmViewModel::class.java]

    binding.fragmentBtnUpdateAlarm.setOnClickListener {
        updateAlarm()
        Navigation.findNavController(requireView())
            .navigate(R.id.action_updateFragment_to_homeFragment)
    }
    return binding.root
}

private fun updateDatabase(id: Int, hour: Int, minute: Int, repeat: Boolean) {
    val alarm = Alarm(id, hour, minute, repeat)
    alarmViewModel.update(alarm)
}

private fun updateAlarm() {
    val timePicker = binding.fragmentUpdateAlarmTimePicker
    val id = args.currentAlarm.id
    val hour = timePickerUtil.getTimePickerHour(timePicker)
    val minute = timePickerUtil.getTimePickerMinute(timePicker)
    val repeat = binding.fragmentUpdateAlarmRecurring.isChecked

    val alarmManager = AlarmManager(
        id,
        hour,
        minute,
        true,
        binding.fragmentUpdateAlarmRecurring.isChecked
    )

    updateDatabase(id, hour, minute, repeat)
    alarmManager.cancel(requireContext())
    alarmManager.schedule(requireContext())
}

}

adapter:

class AlarmListAdapter() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {

class MyViewHolder(binding: LayoutAlarmBinding) : RecyclerView.ViewHolder(binding.root)

private var alarmList = ArrayList<Alarm>()


private var onItemClickListener: OnItemClickListener? = null

interface OnItemClickListener{
    fun onClick(alarm: Alarm)
    fun onLongClick(alarm: Alarm)
}

fun setOnItemClickListener(listener: OnItemClickListener){
    onItemClickListener = listener
}


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val binding = LayoutAlarmBinding.inflate(LayoutInflater.from(parent.context))
    return MyViewHolder(binding)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val currentItem = alarmList[position]
    val minute = currentItem.minute
    holder.itemView.findViewById<TextView>(R.id.tv_alarm_time).text =
        if (minute >= 10) {
            "${currentItem.hour}:${currentItem.minute}"
        } else {
            "${currentItem.hour}:0${currentItem.minute}"
        }

    holder.itemView.setOnClickListener{
        if(onItemClickListener != null){
            onItemClickListener?.onClick(currentItem)
        }
    }

    holder.itemView.setOnLongClickListener {
        if(onItemClickListener != null){
            onItemClickListener?.onLongClick(currentItem)
        }
        true
    }
}

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

fun setData(alarm: List<Alarm>) {
    alarmList.clear()
    alarmList.addAll(alarm)
    notifyDataSetChanged()
}

}

HomeFragment:

class HomeFragment : Fragment() {

lateinit var binding: FragmentHomeBinding
private lateinit var alarmViewModel: AlarmViewModel

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

    // RecyclerView
    val adapter = AlarmListAdapter()
    val recyclerView = binding.recyclerView
    recyclerView.adapter = adapter
    recyclerView.layoutManager = LinearLayoutManager(requireContext())

    //ViewModel
    alarmViewModel = ViewModelProvider(this).get(AlarmViewModel::class.java)
    alarmViewModel.readAlarmData.observe(viewLifecycleOwner, Observer { alarm ->
        adapter.setData(alarm)
    })

    binding.btnAddAlarm.setOnClickListener {
        Navigation.findNavController(requireView())
            .navigate(R.id.action_homeFragment_to_newAlarmFragment)
    }

    adapter.setOnItemClickListener(object : AlarmListAdapter.OnItemClickListener {

        override fun onClick(alarm: Alarm) {
            Navigation.findNavController(requireView())
                .navigate(R.id.action_homeFragment_to_updateFragment)
        }

        override fun onLongClick(alarm: Alarm) {
            val deleteBuilder = AlertDialog.Builder(requireContext())
            deleteBuilder.setPositiveButton("Delete") { _, _ ->
                alarmViewModel.delete(alarm)
                Toast.makeText(context, "Alarm Deleted", Toast.LENGTH_SHORT)
                    .show()
            }
            deleteBuilder.setNegativeButton("Cancel") { _, _ ->

            }
            deleteBuilder.setTitle("Delete Alarm?")
            deleteBuilder.create().show()
        }
    })

    return binding.root
}

}

and my alarm model:

@Parcelize
@Entity(tableName = "alarm_table")
data class Alarm(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val hour: Int,
    val minute: Int,
    val repeat: Boolean
): Parcelable

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

落日海湾 2025-01-19 16:09:48

Safe args 创建自己的类(如此处的 HomeFragmentDirections )。因此,在 homeFragment 中,为适配器设置 onclick 侦听器时,您应该使用->;

adapter.setOnItemClickListener(object : AlarmListAdapter.OnItemClickListener {

        override fun onClick(alarm: Alarm) {
            Navigation.findNavController(requireView())
                .navigate(HomeFragmentDirections.actionHomeFragmentToUpdateFragment(params))
        }

        override fun onLongClick(alarm: Alarm) {
            // preform longclick action here
        }
    })

并在相应导航图中的 UpdateFragment 中添加所需类型的参数(在您的情况下,id 可能是 int 或 String 类型)。

我希望你明白我的意思,如果没有,请不要犹豫发表评论。
您可以在 Android 开发者网站此处阅读相关内容。

Safe args creates its own classes (like here HomeFragmentDirections). So in homeFragment, where you are setting up onclick listeners for the adapter, you should use->

adapter.setOnItemClickListener(object : AlarmListAdapter.OnItemClickListener {

        override fun onClick(alarm: Alarm) {
            Navigation.findNavController(requireView())
                .navigate(HomeFragmentDirections.actionHomeFragmentToUpdateFragment(params))
        }

        override fun onLongClick(alarm: Alarm) {
            // preform longclick action here
        }
    })

And add a param (in your case, id may be of type int or String) of the required type in UpdateFragment in the corresponding navgraph.

I hope you got my point and if not don't hesitate to comment.
You can read about this on the android developer website here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文