NavArgs 返回 RuntimeException
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Safe args 创建自己的类(如此处的 HomeFragmentDirections )。因此,在
homeFragment
中,为适配器设置onclick
侦听器时,您应该使用->;并在相应导航图中的
UpdateFragment
中添加所需类型的参数(在您的情况下,id
可能是 int 或 String 类型)。我希望你明白我的意思,如果没有,请不要犹豫发表评论。
您可以在 Android 开发者网站此处阅读相关内容。
Safe args creates its own classes (like here
HomeFragmentDirections
). So inhomeFragment
, where you are setting uponclick
listeners for the adapter, you should use->And add a param (in your case,
id
may be of type int or String) of the required type inUpdateFragment
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.