我有一个自定义
DialogFragment
类 ListWorkoutTypeDialog
,它会打开一个对话框,通过类内的 AlertDialog
删除或编辑我的健身应用程序的锻炼类型。 目标是能够根据用户是否删除了条目来更改此AlertDialog
上的正面和负面按钮的文本。即。如果用户没有更改任何内容,它将显示一个标有“关闭”的负向按钮,否则如果有任何更改,它将显示一个正向“保存”按钮和一个负向“取消”按钮。目前我只是想将“关闭”按钮更改为“取消”,即使我也遇到了麻烦。
我在设置对
AlertDialog
的引用并在调用类(我的 MainActivity
)中获取该引用时遇到问题。它总是返回 null,即使我在调用获取引用之前 create()
和 show()
AlertDialog
。
我尝试在
ListWorkoutTypeDialog
类中设置按钮文本,但仍然导致空指针异常。
观察到
alertDialog
在 onCreateDialog
重写函数中设置,因此应该设置。然而,即使在这个函数被调用之后,我尝试使用 alertDialog
获取对 getAlertDialog
的引用,它仍然是 null
。
这是我的
ListWorkoutTypeDialog
课程:
package com.example.workoutbuddy.dialogs
import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.util.Log
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope
import com.example.workoutbuddy.MainActivity
import com.example.workoutbuddy.R
import com.example.workoutbuddy.db.WorkoutType
import com.example.workoutbuddy.databinding.DialogWorkoutTypeListBinding
import com.example.workoutbuddy.util.findById
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
// display all workoutTypes in a list where user can long press to remove them or tap to edit them
// takes in a list of workoutTypes to display, activity context and a FragmentManager
class ListWorkoutTypeDialog(workoutTypes: MutableList<WorkoutType?>?, private var context: Activity, private var fm: FragmentManager) : DialogFragment() {
// copy of workoutTypes
private var realWorkoutTypesList = workoutTypes?.toMutableList()
// true when we want to save (ie. save button clicked)
private var save = false
// list of workoutTypes to be displayed
private var arrayOfWorkoutTypes = mutableListOf<String>();
// adaptor for workoutTypes array
private var adapter = ArrayAdapter(context, R.layout.blank_text_view_black_text, arrayOfWorkoutTypes)
// tempMap for mapping position in list to primary ID keys of workoutTypes
private var tempMap: MutableMap<Int, Int> = mutableMapOf<Int, Int>()
// if firstRun
private var firstRun = true
// mutableList of IDs to be removed
private var idsToRemove = mutableListOf<Int>()
// reference to self (this)
private var self = this
private lateinit var alertDialog: AlertDialog
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
val binding = DialogWorkoutTypeListBinding.inflate(layoutInflater)
// clear the array of workoutTypes
arrayOfWorkoutTypes.clear()
// null check
if (realWorkoutTypesList != null) {
// for each workoutType (wt) add them to the array list to be displayed
for (wt in realWorkoutTypesList!!) {
arrayOfWorkoutTypes.add("Workout ${wt!!.identifier} - ${wt.name}")
}
}
// setup adaptor
binding.workoutTypeList.adapter = adapter
// on long click we delete the long clicked item
binding.workoutTypeList.onItemLongClickListener = AdapterView.OnItemLongClickListener {
_,_,_,position ->
// if position is 0 (placeholder no workout item) don't remove as it is the default
if (position.toInt() != 0) {
// removes item from listview array
arrayOfWorkoutTypes.removeAt(position.toInt())
if (firstRun) {
// remove from lists
val num = findById(realWorkoutTypesList, (context as MainActivity).getWorkoutIDByPosition(position.toInt()))
realWorkoutTypesList?.remove(num)
idsToRemove.add((context as MainActivity).getWorkoutIDByPosition(position.toInt()))
firstRun = false
} else {
// tempMap now set so use that as positions will change as we remove items
idsToRemove.add(tempMap[position.toInt()]!!)
val num = findById(realWorkoutTypesList, tempMap[position.toInt()]!!)
realWorkoutTypesList?.remove(num)
}
// update the map as items are deleted
tempMap = (context as MainActivity).getSpinnerListMappings(realWorkoutTypesList)
// updates/recycles view
adapter.notifyDataSetChanged()
}
true
}
// on a short click we edit the item clicked
binding.workoutTypeList.onItemClickListener = AdapterView.OnItemClickListener {
_,_,_,position ->
// make sure we don't remove the default workout "No workout" placeholder
if (position.toInt() != 0) {
var tempWorkoutType: WorkoutType? = null
lifecycleScope.launch(Dispatchers.IO) {
val workoutTypeDAO = (context as MainActivity).db.getWorkoutTypeDao()
tempWorkoutType = workoutTypeDAO.get((context as MainActivity).getWorkoutIDByPosition(position.toInt()))
lifecycleScope.launch(Dispatchers.Main) {
// show the add/update workout dialog
val dialog = AddUpdateWorkoutTypeDialog(fm, tempWorkoutType, self)
dialog.show(fm, "WORKOUT_TYPE_UPDATE_DIALOG")
}
}
}
}
// HERE I SET THE ALERTDIALOG
alertDialog = builder.setView(binding.root)
.setNegativeButton("Dismiss") { _, _ ->
// send cancel data
save = false
this.dismiss()
}.create()
Log.d("status", "alertDialog set")
return builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
fun getAlertDialog(): AlertDialog {
// returns the AlertDialog used to make the alert
return alertDialog
}
// refresh workout types
fun refresh(wtl: MutableList<WorkoutType?>?) {
arrayOfWorkoutTypes.clear()
if (wtl != null) {
for (wt in wtl) {
arrayOfWorkoutTypes.add("Workout ${wt!!.identifier} - ${wt.name}")
}
}
if (wtl != null) {
realWorkoutTypesList = wtl.toMutableList()
}
// refresh/recycle view
adapter.notifyDataSetChanged()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
val activity: Activity? = activity
if (activity is DialogInterface.OnDismissListener) {
// if save button clicked update list and remove deleted IDs
if (save) {
(activity as MainActivity).onWorkoutListUpdate(idsToRemove)
}
}
}
}
这是我调用对话框的片段(在我的
MainActivity
中):
val workoutTypeDAO = db.getWorkoutTypeDao()
// spawn list dialog
val dialog = ListWorkoutTypeDialog(workoutTypeDAO.getAll()?.toMutableList(), self, supportFragmentManager)
// refresh the list
dialog.refresh(workoutTypeDAO.getAll()?.toMutableList())
// show the dialog
dialog.show(supportFragmentManager, "WORKOUT_LIST_VIEW")
Log.d("status", "changing button")
// set the button to cancel
dialog.getAlertDialog().getButton(AlertDialog.BUTTON_NEGATIVE).text = "Cancel" //ERROR happens here, getAlertDialog returns NULL
我收到以下错误:
alertDialog
未初始化,即使它在设置按钮之前运行:
2024-04-01 15:26:11.006 22922-22962 status com.example.workoutbuddy D changing button (this should be called last)
2024-04-01 15:26:11.025 22922-22962 AndroidRuntime com.example.workoutbuddy E FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.example.workoutbuddy, PID: 22922
kotlin.UninitializedPropertyAccessException: lateinit property alertDialog has not been initialized
at com.example.workoutbuddy.dialogs.ListWorkoutTypeDialog.getAlertDialog(ListWorkoutTypeDialog.kt:133)
at com.example.workoutbuddy.MainActivity$onOptionsItemSelected$1.invokeSuspend(MainActivity.kt:116)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:100)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@83d2b55, Dispatchers.IO]
2024-04-01 15:26:11.031 22922-22922 status com.example.workoutbuddy D alertDialog set (this should be called first)
我不确定这是否是解决这个问题的正确方法,因为我对 Android 开发还很陌生,但我认为这应该相对容易。谢谢您的帮助。
首先,您调用
create
两次,这可能是一个错误:
// HERE I SET THE ALERTDIALOG
alertDialog = builder.setView(binding.root)
.setNegativeButton("Dismiss") { _, _ ->
// send cancel data
save = false
this.dismiss()
}.create() // <-- alertDialog set to this instance
Log.d("status", "alertDialog set")
return builder.create() // <-- New, different instance returned
接下来,你对show的工作原理有一个误解:
我收到以下错误:alertDialog 未初始化,即使它在设置按钮之前运行:
对话框不会立即显示。它被添加到片段管理器事务队列中以便稍后执行(如下一帧)。
// This actually means "schedule the dialog to be shown soon"
dialog.show(supportFragmentManager, "WORKOUT_LIST_VIEW")
Log.d("status", "changing button")
// set the button to cancel
// Dialog will NOT have been initialized one line of execution later
dialog.getAlertDialog().getButton(AlertDialog.BUTTON_NEGATIVE).text = "Cancel" //ERROR happens here, getAlertDialog returns NULL
最后,真正做你想做的事:我过去所做的是在对话框完全初始化时更新 onStart() 中的按钮:
class ListWorkoutTypeDialog {
override onStart() {
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).text = "Cancel"
}
}