I am trying to make a RecyclerView that allows for selecting multiple items. I am using the androidx.recyclerview.selection.SelectionTracker
library for this.
My fragment that contains the RecyclerView also has a TextView. When you click into the TextView, the soft keyboard comes onto the screen and my app crashes.
If the RecyclerView is empty or only has enough items in it that the keyboard does not overlap the list items, the app does not crash.
I am absolutely lost. Any insight would be greatly appreciated.
Here is the Logcat error output:
2020-05-30 14:56:14.511 31037-31037/? E/exercisetracke: Unknown bits set in runtime_flags: 0x8000
2020-05-30 14:57:29.847 31037-31037/com.example.android.exercisetracker E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.exercisetracker, PID: 31037
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$1.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:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:446)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
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:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
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:779)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:725)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Adapter
class AddRoutineExerciseAdapter internal constructor(context: Context) :
RecyclerView.Adapter<AddRoutineExerciseAdapter.ViewHolder>() {
init {
setHasStableIds(true)
}
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var exercises: List<Exercise> = emptyList<Exercise>()
var tracker: SelectionTracker<Long>? = null
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val exerciseTitle: TextView = itemView.findViewById(R.id.exercise_title)
//val exerciseArea: TextView = itemView.findViewById(R.id.exercise_area)
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
}
fun bind(value: Exercise, isActivated: Boolean = false) {
exerciseTitle.text = value.toString()
itemView.isActivated = isActivated
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = inflater.inflate(R.layout.exercise_list_item, parent, false)
return ViewHolder(itemView)
}
override fun getItemCount(): Int {
return exercises.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val current = exercises[position]
holder.exerciseTitle.text = current.exerciseName
// holder.exerciseArea.text = current.exerciseBodyType
tracker?.let {
holder.bind(
current,
it.isSelected(position.toLong())
)
}
}
override fun getItemId(position: Int): Long = position.toLong()
fun setExercises(exercises: List<Exercise>) {
this.exercises = exercises
notifyDataSetChanged()
}
}
Fragment
class AddRoutineFragment : Fragment() {
private lateinit var exerciseViewModel: ExerciseViewModel
var tracker: SelectionTracker<Long>? = null
companion object {
fun newInstance(): ExerciseFragment {
return ExerciseFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_add_routine, container, false)
val activity = activity as Context
val recyclerView: RecyclerView = view.findViewById(R.id.rv_add_routine_exercises)
val adapter = AddRoutineExerciseAdapter(activity)
recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.adapter = adapter
exerciseViewModel = ViewModelProvider(this).get(ExerciseViewModel::class.java)
exerciseViewModel.allExercises.observe(viewLifecycleOwner, Observer { exercises ->
exercises?.let { adapter.setExercises(it) }
})
tracker = SelectionTracker.Builder<Long>(
"mySelection",
recyclerView,
StableIdKeyProvider(recyclerView),
ExerciseDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
adapter.tracker = tracker
return view
}
}
I came up with the answer.
I implemented an ItemKeyProvider
.
class MyItemKeyProvider(private val recyclerView: RecyclerView) :
ItemKeyProvider<Long>(ItemKeyProvider.SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return recyclerView.adapter?.getItemId(position)
}
override fun getPosition(key: Long): Int {
val viewHolder = recyclerView.findViewHolderForItemId(key)
return viewHolder?.layoutPosition ?: RecyclerView.NO_POSITION
}
}
Then created a function to create a tracker and assign the tracker to the Adapter, which I call in onCreateView
.
private fun setUpTracker(
recyclerView: RecyclerView,
adapter: AddRoutineExerciseAdapter
) {
tracker = SelectionTracker.Builder(
"mySelection",
recyclerView,
MyItemKeyProvider(recyclerView),
ExerciseDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
adapter.tracker = tracker
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.