导航到另一个片段时,片段中的RecyclerView崩溃

use*_*730 6 android android-fragments kotlin android-recyclerview

我一直在网上跟踪创建RecyclerView的示例。我唯一不同的是将RecyclerView放入Fragment中,而不是将其放入MainActivity中。RecyclerView可以很好地显示数据。但是,当我导航到另一个Fragment时,应用程序崩溃,并抛出与RecyclerView相关的异常:

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.support.v7.widget.RecyclerView$ViewHolder.shouldIgnore()' on a null object reference
Run Code Online (Sandbox Code Playgroud)

这是一个重现的最小示例:

主要活动:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_layout)
    }
}
Run Code Online (Sandbox Code Playgroud)

main_layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <fragment class="package.RecyclerFragment"
              android:id="@+id/fragment"
              app:layout_constraintTop_toTopOf="parent"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>
Run Code Online (Sandbox Code Playgroud)

RecyclerFragment:

class RecyclerFragment : Fragment() {

    private val data = listOf("Moscow", "Washington")

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        setHasOptionsMenu(true)
        val view = inflater.inflate(R.layout.recycler_list, container, false)
        view.findViewById<RecyclerView>(R.id.list)?.apply {
            adapter = RecyclerAdapter(data)
        }
        return view
    }

    override fun onCreateOptionsMenu(menu: Menu?, menuInflater: MenuInflater) {
        menuInflater.inflate(R.menu.menu, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        return when (item?.itemId) {
            R.id.navigate -> {
                fragmentManager?.beginTransaction()?.replace(R.id.fragment, HelloFragment())?.commit()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

recycler_list:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/list"
        android:orientation="vertical"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
Run Code Online (Sandbox Code Playgroud)

RecyclerAdapter:

class RecyclerAdapter(private val data: List<String>):
    RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {

    inner class ViewHolder(val view: CardView): RecyclerView.ViewHolder(view)

    override fun onCreateViewHolder(root: ViewGroup, viewType: Int): ViewHolder {
        val listItem = LayoutInflater.from(root.context)
            .inflate(R.layout.list_item, root, false) as CardView
        return ViewHolder(listItem)
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        viewHolder.view.findViewById<TextView>(R.id.text).text = data[position]
    }

    override fun getItemCount() = data.size
}
Run Code Online (Sandbox Code Playgroud)

项目清单:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:layout_margin="5sp">
    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="20sp"
            android:textSize="20sp"
            android:id="@+id/text"/>
</android.support.v7.widget.CardView>
Run Code Online (Sandbox Code Playgroud)

菜单:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/navigate"
          android:title="Navigate"
          app:showAsAction="ifRoom"/>
</menu>
Run Code Online (Sandbox Code Playgroud)

HelloFragment:

class HelloFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.hello, container, false)
    }
}
Run Code Online (Sandbox Code Playgroud)

你好:

<?xml version="1.0" encoding="utf-8"?>
<TextView android:text="Hello"
          android:textSize="30sp"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          xmlns:android="http://schemas.android.com/apk/res/android"/>
Run Code Online (Sandbox Code Playgroud)

此实现有问题吗?如何在片段中使用RecyclerView?

Bir*_*ani 1

方法一

只是不要RecyclerView在布局中用作父级RecyclerFragmentLinearLayout像这样把它包起来:

recycler_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

方法 2 在您当前的实现中,您已从 xml 添加RecyclerFragment,但当您尝试将其替换为 时HelloFragment,它不会被替换,而是将在其上方或下方添加新片段。

为了正确实现这一点,您应该RecyclerFragment从 Activity 的onCreate方法中添加如下所示的方法,并将其从 xml 中删除:

主要活动

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        supportFragmentManager.beginTransaction()
            .add(R.id.root,RecyclerFragment())
            .commit()
    }
}
Run Code Online (Sandbox Code Playgroud)

活动主文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</androidx.constraintlayout.widget.ConstraintLayout>
Run Code Online (Sandbox Code Playgroud)

当您想要替换选项项上的片段时,请单击如下所示:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.navigate -> {
            requireActivity().supportFragmentManager.beginTransaction().replace(R.id.root, HelloFragment(), "Hello")
                .commit()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,您之前的片段将被删除,并添加新的片段。