简体   繁体   中英

Android Recyclerview Java.lang.NullPointerException when opening soft keyboard

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM