Kotlin中实现Material CircularProgressIndicator及空指针报错解决
根因排查
你触发root.findViewById(R.id.loadingbar) must not be null的核心原因是布局文件与代码引用完全不匹配,同时逻辑设计不符合RecyclerView的工作规则。
具体问题点
- 你在
onCreateView中通过inflater.inflate(R.layout.fragment_orders, container, false)加载的是Fragment根布局,但你贴出的、包含@+id/loadingbar和@+id/cardView2的XML内容,根本不是fragment_orders.xml,而是RecyclerView的单条列表项布局。 - 你的
fragment_orders.xml实际内容里,既没有定义@id/loadingbar,也没有定义@id/cardView2,甚至连代码中findViewById(R.id.orders_list)对应的RecyclerView控件都不存在,所有findViewById都会拿到null,直接触发空指针。 - 你原本的设计逻辑存在偏差:你试图在Fragment中直接获取RecyclerView item内部的
CardView控件、通过控制它的显隐来实现列表加载状态切换,但RecyclerView的item布局是被Adapter批量复用的,根本不属于Fragment根视图的一部分,这种写法本身就不符合RecyclerView的运行逻辑。
完整修复步骤
1. 拆分两类布局文件
(1)重写Fragment根布局 res/layout/fragment_orders.xml
这个布局才是你代码中inflate给root的视图,需要包含全局的进度条和RecyclerView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 全局加载进度条 --> <com.google.android.material.progressindicator.CircularProgressIndicator android:id="@+id/loadingbar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true" android:layout_gravity="center_horizontal|center_vertical" /> <!-- 订单列表RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/orders_list" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> </LinearLayout>
(2)保留列表项布局为 res/layout/item_order.xml
你之前写的带CardView、三个订单TextView的XML,直接保存为这个item布局即可,后续会交给Adapter复用,不需要在这里加任何全局控件。
2. 修正Fragment代码逻辑
- 删除所有和
cardView2相关的代码:这个控件属于item布局,根本无法通过Fragment的根视图直接findViewById获取。 - 加载状态切换的控制对象从「item的CardView」换成「全局的RecyclerView和进度条」。
修正后的核心代码片段:
class OrdersFragment : Fragment() { lateinit var session: SessionManager lateinit var laundriesRecycler: RecyclerView lateinit var progressBar: CircularProgressIndicator // 删掉 lateinit var cardView2: CardView 这行,完全用不到 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val root = inflater.inflate(R.layout.fragment_orders, container, false) session = SessionManager(context) // 现在这两个控件都在fragment_orders.xml里,不会再返回null laundriesRecycler = root.findViewById(R.id.orders_list) progressBar = root.findViewById(R.id.loadingbar) getOrders() return root } private fun getOrders() { // 加载中:显示进度条,隐藏整个列表 progressBar.visibility = View.VISIBLE laundriesRecycler.visibility = View.GONE val user = session.getUserDetails() val token = user.get(SessionManager.KEY_ACCESS_TOKEN) val tokenFull = "Bearer $token" val queue = Volley.newRequestQueue(context) val url = "https://example.com/api/orders" val stringReq = object : StringRequest( Method.GET, url, Response.Listener { response -> // 加载完成:隐藏进度条,显示列表 progressBar.visibility = View.GONE laundriesRecycler.visibility = View.VISIBLE val list = Gson().fromJson(response, OrderArr::class.java) laundriesRecycler.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) laundriesRecycler.adapter = OrdersAdapter(context, list) }, Response.ErrorListener { error -> // 修复:加载失败也要隐藏进度条,避免一直转圈 progressBar.visibility = View.GONE Toast.makeText(context, error.message, Toast.LENGTH_LONG).show() } ) { override fun getHeaders(): Map<String, String> { return mapOf( "Content-Type" to "application/json", "Authorization" to tokenFull ) } } queue.add(stringReq) } }
可选优化建议
- 后续可以替换掉手动
findViewById的写法,引入View Binding,彻底避免布局id写错、控件找不到的空指针问题。 - 如果需要在空数据、加载失败的场景给用户展示提示,可以在根布局里再加一个全局的提示TextView,和进度条、RecyclerView做三者互斥的显隐控制即可。
内容的提问来源于stack exchange,提问作者mafortis
相关产品推荐
相关产品推荐

