RecyclerView 选择与对话框一起导致崩溃。空对象上的 getAdapterPostion()
RecyclerView Selection together with dialog leads to crash. getAdapterPostion() on null object
在我目前正在开发的应用程序中,我在 viewpager 中有一个片段,它显示了一个由数据库中的一些数据填充的 RecyclerView。在这个 RecyclerView 中,我使用 recyclerview-selection 库和 actionmode 实现了 ItemSelection。仅此一项就可以正常工作。但是,我还有一个 FloatingActionButton,它会打开一个对话框,用户可以在其中向数据库添加一个新入口,该入口也将显示在 recyclerview 中。对话框打开成功,但当用户单击编辑文本时,它崩溃并显示以下错误消息:
2020-08-16 16:45:36.413 12939-12939/com.nilswinking.kochbuch2 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.me.myapp, PID: 12939
java.lang.NullPointerException: Attempt to invoke virtual method 'int androidx.recyclerview.widget.RecyclerView$ViewHolder.getAdapterPosition()' on a null object reference
at androidx.recyclerview.selection.StableIdKeyProvider.onDetached(StableIdKeyProvider.java:90)
at androidx.recyclerview.selection.StableIdKeyProvider.onChildViewDetachedFromWindow(StableIdKeyProvider.java:69)
at androidx.recyclerview.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:7546)
at androidx.recyclerview.widget.RecyclerView.removeDetachedView(RecyclerView.java:4349)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:9243)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:4207)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3862)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.viewpager.widget.ViewPager.onLayout(ViewPager.java:1775)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:446)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
2020-08-16 16:45:36.416 12939-12939/com.me.myapp E/AndroidRuntime: at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:784)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3470)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2938)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:731)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
对话:
class AddBookDialog : DialogFragment() {
private val TAG = AddBookDialog::class.java.simpleName
private lateinit var listener: AddBookDialogInterface
private lateinit var editTextName: EditText
private lateinit var title: TextView
private var id: String? = null
interface AddBookDialogInterface {
fun addKochbuch(name: String, id: String? = null): Boolean
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(it, R.style.AppTheme_AlertDialog)
val inflater = requireActivity().layoutInflater;
val view = inflater.inflate(R.layout.add_book_dialog, null)
editTextName = view.findViewById(R.id.editTextName)
// editTextNameLayout = view.findViewById(R.id.editTextNameLayout)
title = view.findViewById(R.id.title)
arguments?.let {
it.getString(nameParam).let {
editTextName.setText(it)
}
id = it.getString(idParam)
}
editTextName.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP) {
add()
return@OnKeyListener true
}
false
})
builder.setView(view)
.setPositiveButton(
"Hinzufügen"
) { dialog, id ->
add()
}
.setNegativeButton(
"Abbrechen"
) { dialog, id ->
// User cancelled the dialog
}
// Create the AlertDialog object and return it
val dialog: AlertDialog = builder.create()
dialog.setOnShowListener {
val button: Button = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
button.setOnClickListener {add()}
}
dialog
} ?: throw IllegalStateException("Activity cannot be null")
}
override fun onAttach(context: Context) {
super.onAttach(context)
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
listener = context as AddBookDialogInterface
} catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception
throw ClassCastException((context.toString() +
" must implement AddBookDialogInterface"))
}
}
private fun add() {
val name = editTextName.text.toString()
listener.addKochbuch(name, id).let {
if (it)
dismiss()
}
}
companion object {
fun newInstance(kochbuch: Kochbuch? = null) =
AddBookDialog().apply {
arguments = Bundle().apply {
putString(nameParam, kochbuch?.name)
putString(idParam, kochbuch?.id)
}
}
}
}
对话框布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="Erstelle ein neues Kochbuch"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/material_on_background_emphasis_high_type"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextNameLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:hint="Name deines Kochbuches"
android:textColorHint="@color/input_outline_color"
app:boxStrokeColor="@color/input_outline_color"
app:hintTextColor="@color/input_outline_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:singleLine="true"
android:textColor="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
片段:
class HomeBooksFragment : Fragment(), OnActionItemClickListener {
private val TAG = HomeBooksFragment::class.java.simpleName
private lateinit var realm: Realm
private val adapter = ItemAdapterCookingBooks()
private lateinit var result: RealmResults<Kochbuch>
private lateinit var tracker: SelectionTracker<Long>
private var actionmode: SelectionActionModeCallback? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home_books, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
realm = Realm.getDefaultInstance()
result = realm.where<Kochbuch>().sort("name").findAll()
result.addChangeListener { t, _ ->
updateUI(t)
}
adapter.data = ArrayList()
rv.layoutManager = GridLayoutManager(context, 2)
rv.adapter = adapter
tracker = SelectionTracker.Builder<Long>(
"mySelection",
rv,
StableIdKeyProvider(rv),
ItemDetailLookup(rv),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
tracker.addObserver(
object : SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
super.onSelectionChanged()
tracker.selection.size().let { i ->
when {
i >= 2 -> actionmode?.setEditEnabled(false)
i == 1 -> actionmode?.setEditEnabled(true)
i == 0 -> Log.d(TAG, "onSelectionChanged: zero items selected")
else -> null
}
if (tracker.hasSelection() && actionmode == null) {
actionmode = SelectionActionModeCallback()
view?.let { actionmode?.startActionMode(it, R.menu.selection_action_mode_menu, tracker, "$i Ausgwählt") }
actionmode?.setListener(this@HomeBooksFragment)
} else if (!tracker.hasSelection() && actionmode != null) {
actionmode?.finishActionMode()
actionmode = null
} else {
actionmode?.setTitle("$i Ausgewählt")
}
}
}
}
)
adapter.tracker = tracker
updateUI(result)
}
}
ItemDetailLookup 没有问题,因为我将所有内容都放在 try-catch 块中,但错误仍然出现。
进一步的调试显示,只有在对话框后面显示项目时才会发生这种情况(在我的屏幕尺寸上,当显示七个项目时)。但是,当打开对话框时,当另一个片段显示在我的 viewpager 中时,也会发生这种情况。由此我怀疑点击事件以某种方式通过对话框传递到底层片段。
如有任何帮助,我们将不胜感激。
编辑:
显示其他对话框时也会发生
出于某种原因,选择跟踪器试图获取不存在或不在回收视图中并产生空指针异常的查看器的项目详细信息。在我的 ViewHolder 中,我有这个方法来 return 所有关于 viewholder 的必要信息,以使选择跟踪器工作,这里是正确的实现,它是 null 安全的:
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int {
try {
return adapterPosition
} catch (e: Exception) {
return -1
}
}
override fun getSelectionKey(): Long? = itemId
}
为了比较,请查看我的问题,其中发布了以前的实现。
可能有点晚了,但我最近 运行 遇到了同样的问题并找到了适合我的解决方案。创建自定义 ItemKeyProvider 为我解决了 NPE 问题。我使用了我在网上找到的这篇文章:A guide to recyclerview selection
class LongItemKeyProvider(private val recyclerView: RecyclerView) :
ItemKeyProvider<Long>(SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return recyclerView.adapter?.getItemId(position)
}
override fun getPosition(key: Long): Int {
return recyclerView.findViewHolderForItemId(key)?.layoutPosition ?:
RecyclerView.NO_POSITION
}
}
在我目前正在开发的应用程序中,我在 viewpager 中有一个片段,它显示了一个由数据库中的一些数据填充的 RecyclerView。在这个 RecyclerView 中,我使用 recyclerview-selection 库和 actionmode 实现了 ItemSelection。仅此一项就可以正常工作。但是,我还有一个 FloatingActionButton,它会打开一个对话框,用户可以在其中向数据库添加一个新入口,该入口也将显示在 recyclerview 中。对话框打开成功,但当用户单击编辑文本时,它崩溃并显示以下错误消息:
2020-08-16 16:45:36.413 12939-12939/com.nilswinking.kochbuch2 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.me.myapp, PID: 12939
java.lang.NullPointerException: Attempt to invoke virtual method 'int androidx.recyclerview.widget.RecyclerView$ViewHolder.getAdapterPosition()' on a null object reference
at androidx.recyclerview.selection.StableIdKeyProvider.onDetached(StableIdKeyProvider.java:90)
at androidx.recyclerview.selection.StableIdKeyProvider.onChildViewDetachedFromWindow(StableIdKeyProvider.java:69)
at androidx.recyclerview.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:7546)
at androidx.recyclerview.widget.RecyclerView.removeDetachedView(RecyclerView.java:4349)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:9243)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:4207)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3862)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.viewpager.widget.ViewPager.onLayout(ViewPager.java:1775)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:446)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
2020-08-16 16:45:36.416 12939-12939/com.me.myapp E/AndroidRuntime: at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:784)
at android.view.View.layout(View.java:22844)
at android.view.ViewGroup.layout(ViewGroup.java:6389)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3470)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2938)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:731)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
对话:
class AddBookDialog : DialogFragment() {
private val TAG = AddBookDialog::class.java.simpleName
private lateinit var listener: AddBookDialogInterface
private lateinit var editTextName: EditText
private lateinit var title: TextView
private var id: String? = null
interface AddBookDialogInterface {
fun addKochbuch(name: String, id: String? = null): Boolean
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(it, R.style.AppTheme_AlertDialog)
val inflater = requireActivity().layoutInflater;
val view = inflater.inflate(R.layout.add_book_dialog, null)
editTextName = view.findViewById(R.id.editTextName)
// editTextNameLayout = view.findViewById(R.id.editTextNameLayout)
title = view.findViewById(R.id.title)
arguments?.let {
it.getString(nameParam).let {
editTextName.setText(it)
}
id = it.getString(idParam)
}
editTextName.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP) {
add()
return@OnKeyListener true
}
false
})
builder.setView(view)
.setPositiveButton(
"Hinzufügen"
) { dialog, id ->
add()
}
.setNegativeButton(
"Abbrechen"
) { dialog, id ->
// User cancelled the dialog
}
// Create the AlertDialog object and return it
val dialog: AlertDialog = builder.create()
dialog.setOnShowListener {
val button: Button = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
button.setOnClickListener {add()}
}
dialog
} ?: throw IllegalStateException("Activity cannot be null")
}
override fun onAttach(context: Context) {
super.onAttach(context)
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
listener = context as AddBookDialogInterface
} catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception
throw ClassCastException((context.toString() +
" must implement AddBookDialogInterface"))
}
}
private fun add() {
val name = editTextName.text.toString()
listener.addKochbuch(name, id).let {
if (it)
dismiss()
}
}
companion object {
fun newInstance(kochbuch: Kochbuch? = null) =
AddBookDialog().apply {
arguments = Bundle().apply {
putString(nameParam, kochbuch?.name)
putString(idParam, kochbuch?.id)
}
}
}
}
对话框布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="Erstelle ein neues Kochbuch"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/material_on_background_emphasis_high_type"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextNameLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:hint="Name deines Kochbuches"
android:textColorHint="@color/input_outline_color"
app:boxStrokeColor="@color/input_outline_color"
app:hintTextColor="@color/input_outline_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editTextName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:singleLine="true"
android:textColor="@color/material_on_background_emphasis_high_type" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
片段:
class HomeBooksFragment : Fragment(), OnActionItemClickListener {
private val TAG = HomeBooksFragment::class.java.simpleName
private lateinit var realm: Realm
private val adapter = ItemAdapterCookingBooks()
private lateinit var result: RealmResults<Kochbuch>
private lateinit var tracker: SelectionTracker<Long>
private var actionmode: SelectionActionModeCallback? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home_books, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
realm = Realm.getDefaultInstance()
result = realm.where<Kochbuch>().sort("name").findAll()
result.addChangeListener { t, _ ->
updateUI(t)
}
adapter.data = ArrayList()
rv.layoutManager = GridLayoutManager(context, 2)
rv.adapter = adapter
tracker = SelectionTracker.Builder<Long>(
"mySelection",
rv,
StableIdKeyProvider(rv),
ItemDetailLookup(rv),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
tracker.addObserver(
object : SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
super.onSelectionChanged()
tracker.selection.size().let { i ->
when {
i >= 2 -> actionmode?.setEditEnabled(false)
i == 1 -> actionmode?.setEditEnabled(true)
i == 0 -> Log.d(TAG, "onSelectionChanged: zero items selected")
else -> null
}
if (tracker.hasSelection() && actionmode == null) {
actionmode = SelectionActionModeCallback()
view?.let { actionmode?.startActionMode(it, R.menu.selection_action_mode_menu, tracker, "$i Ausgwählt") }
actionmode?.setListener(this@HomeBooksFragment)
} else if (!tracker.hasSelection() && actionmode != null) {
actionmode?.finishActionMode()
actionmode = null
} else {
actionmode?.setTitle("$i Ausgewählt")
}
}
}
}
)
adapter.tracker = tracker
updateUI(result)
}
}
ItemDetailLookup 没有问题,因为我将所有内容都放在 try-catch 块中,但错误仍然出现。
进一步的调试显示,只有在对话框后面显示项目时才会发生这种情况(在我的屏幕尺寸上,当显示七个项目时)。但是,当打开对话框时,当另一个片段显示在我的 viewpager 中时,也会发生这种情况。由此我怀疑点击事件以某种方式通过对话框传递到底层片段。
如有任何帮助,我们将不胜感激。
编辑:
显示其他对话框时也会发生
出于某种原因,选择跟踪器试图获取不存在或不在回收视图中并产生空指针异常的查看器的项目详细信息。在我的 ViewHolder 中,我有这个方法来 return 所有关于 viewholder 的必要信息,以使选择跟踪器工作,这里是正确的实现,它是 null 安全的:
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int {
try {
return adapterPosition
} catch (e: Exception) {
return -1
}
}
override fun getSelectionKey(): Long? = itemId
}
为了比较,请查看我的问题,其中发布了以前的实现。
可能有点晚了,但我最近 运行 遇到了同样的问题并找到了适合我的解决方案。创建自定义 ItemKeyProvider 为我解决了 NPE 问题。我使用了我在网上找到的这篇文章:A guide to recyclerview selection
class LongItemKeyProvider(private val recyclerView: RecyclerView) :
ItemKeyProvider<Long>(SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return recyclerView.adapter?.getItemId(position)
}
override fun getPosition(key: Long): Int {
return recyclerView.findViewHolderForItemId(key)?.layoutPosition ?:
RecyclerView.NO_POSITION
}
}