0

I am trying to show CircularProgressIndicator while my data are fetching from server but I get this error root.findViewById(R.id.loadingbar) must not be null.

Logic

  1. Hide recycler view items till data are fetched
  2. Show Circular Progress while data are fetching
  3. Hide Circular Progress when data are ready
  4. Show recyclerview items

Code

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)
    }
}

Any idea?

mafortis
  • 6,750
  • 23
  • 130
  • 288

2 Answers2

1

In you 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
  • 876
  • 9
  • 22
0

Solved

Special thanks to Teo who made me realize the issue.

Fixes

  1. Instead of having my progress inside my items xml I had to add it into my fragment xml where my recycler code placed (#1 misplaced code)
  2. Using binding (unlike Teo mentioned , there is no need to use onViewCreated infact that cause undefined binding error. Instead it should be defined inside onCreateView function) Here is the final code
    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
    }
mafortis
  • 6,750
  • 23
  • 130
  • 288