如何在kotlin中实现material CircularProgressIndicator

How to implement material CircularProgressIndicator in kotlin

我试图在从服务器获取数据时显示 CircularProgressIndicator,但出现此错误 root.findViewById(R.id.loadingbar) must not be null

逻辑

  1. 在获取数据之前隐藏回收器视图项目
  2. 在获取数据时显示 Circular Progress
  3. 数据准备好后隐藏Circular Progress
  4. 显示 recyclerview 项目

代码

xml

<LinearLayout
    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:orientation="vertical"
    android:layout_height="wrap_content">

    <!-- Circular progress indicator -->
    <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" />

    <!-- RecyclerView items -->
    <androidx.cardview.widget.CardView
        android:id="@+id/cardView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:elevation="8dp">

        <TextView
            android:id="@+id/order_Iid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="5dp"
            android:gravity="start|top"
            android:text="@string/order_ID"
            android:textSize="12sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/order_status_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|start"
            android:layout_marginStart="5dp"
            android:layout_marginTop="35dp"
            android:text="@string/order_status"
            android:textColor="#5CDCBD"
            android:textSize="18sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/order_price_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="top|end"
            android:layout_marginEnd="5dp"
            android:text="@string/price"
            android:textSize="12sp"
            android:textStyle="bold" />

    </androidx.cardview.widget.CardView>

</LinearLayout>

Fragment

Code is commented for better understanding

class OrdersFragment : Fragment() {

    lateinit var sesssion: SessionManager
    lateinit var laundriesRecycler: RecyclerView
    lateinit var progressBar: CircularProgressIndicator
    lateinit var cardView2: CardView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val root = inflater.inflate(R.layout.fragment_orders, container, false)
        sesssion = SessionManager(context)
        laundriesRecycler = root.findViewById(R.id.orders_list)


        // get progress
        progressBar = root.findViewById(R.id.loadingbar)
        //get recycler item
        cardView2 = root.findViewById(R.id.cardView2)

        getOrders()
        return root
    }

    private fun getOrders() {

        //show progress
        progressBar.visibility = View.VISIBLE
        //hide items
        cardView2.visibility = View.GONE




        var session = SessionManager(context)
        session.checkLogin()
        var user = session.getUserDetails()
        var token: String? = user.get(SessionManager.KEY_ACCESS_TOKEN)
        val tokenFull = "Bearer $token"

        val queue = Volley.newRequestQueue(context)
        val url = "https://example.com/api/orders"

        val stringReq : StringRequest =
                object : StringRequest(
                        Method.GET, url,
                        Response.Listener { response ->


                            // hide progress
                            progressBar.visibility = View.GONE
                            //show items
                            cardView2.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 ->
                            Toast.makeText(context, error.message, Toast.LENGTH_LONG)
                                    .show()
                        }
                ){
                    override fun getHeaders(): Map<String, String> {
                        val headers = HashMap<String, String>()
                        headers["Content-Type"] = "application/json"
                        headers["Authorization"] = tokenFull
                        return headers
                    }
                }
        queue.add(stringReq)
    }
}

有什么想法吗?

在你build.gradle(app)

android{
    buildFeatures {
        viewBinding = true
        dataBinding = true
    }
}
// 1 - direct inflate the fragment layout into Fragment()
class OrdersFragment : Fragment(R.layout.fragment_orders) {

    // 2 - view binding
    private lateinit var binding: FragmentOrdersBinding
    lateinit var sesssion: SessionManager
    lateinit var laundriesRecycler: RecyclerView
    lateinit var progressBar: CircularProgressIndicator
    lateinit var cardView2: CardView

    // 3 - directly call onViewCreated() as the layout already inflated
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        
       // 4 - bind view to viewBinding variable
        binding = FragmentOrdersBinding.bind(view)
        sesssion = SessionManager(context)
        getOrders()
    }

    private fun getOrders() {
        
        // 5 - After that, basically every element just call binding to locate them
        //show progress
        binding.loadingBar.visibility = View.VISIBLE
        //hide items
        binding.cardView2.visibility = View.GONE

    }
}

已解决

特别感谢Teo让我意识到了这个问题。

修复

  1. 我不得不将进度添加到我的片段 xml 中,而不是将我的进度放在我的项目中 xml 我的回收程序代码所在的位置(#1 错误放置的代码)
  2. 使用绑定(与提到的 Teo 不同,不需要使用 onViewCreated 事实上会导致未定义的 binding 错误。相反,它应该在 onCreateView 函数中定义)这是最终代码
    private lateinit var binding: FragmentOrdersBinding
    
    override fun onCreateView(
      inflater: LayoutInflater, container: ViewGroup?,
      savedInstanceState: Bundle?
    ): View? {
      // Inflate the layout for this fragment
      val root = inflater.inflate(R.layout.fragment_orders, container, false)
      sesssion = SessionManager(context)
      laundriesRecycler = root.findViewById(R.id.orders_list)
      binding = FragmentOrdersBinding.bind(root) // <--here
      getOrders()
      return root
    }
    
    private fun getOrders() {
      binding.loadingBar.visibility = View.VISIBLE
      binding.ordersList.visibility = View.GONE
      
      // API process and flip those 2 lines above in success response to
      binding.loadingBar.visibility = View.GONE
      binding.ordersList.visibility = View.VISIBLE
    }