Bird
Raised Fist0
Android Kotlinmobile~20 mins

Pull-to-refresh in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: PullToRefreshScreen
A simple screen that shows a list of items and allows the user to refresh the list by pulling down from the top.
Target UI
-------------------------
| PullToRefreshScreen    |
-------------------------
| ↓ Pull down to refresh |
|-----------------------|
| Item 1                |
| Item 2                |
| Item 3                |
| Item 4                |
| Item 5                |
-------------------------
Use SwipeRefreshLayout to enable pull-to-refresh gesture
Display a vertical list of 5 static items labeled Item 1 to Item 5
When user pulls down, show a loading spinner for 2 seconds
After loading, update the list by appending ' - refreshed' to each item
Ensure the UI is simple and accessible
Starter Code
Android Kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.LinearLayoutManager

class PullToRefreshScreen : AppCompatActivity() {
    private lateinit var swipeRefreshLayout: SwipeRefreshLayout
    private lateinit var recyclerView: RecyclerView
    private val items = mutableListOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")

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

        swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout)
        recyclerView = findViewById(R.id.recyclerView)

        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = ItemAdapter(items)

        // TODO: Add pull-to-refresh logic here
    }
}

// TODO: Create ItemAdapter class for RecyclerView
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
Android Kotlin
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView

class PullToRefreshScreen : AppCompatActivity(), SwipeRefreshLayout.OnRefreshListener {
    private lateinit var swipeRefreshLayout: SwipeRefreshLayout
    private lateinit var recyclerView: RecyclerView
    private val items = mutableListOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
    private lateinit var adapter: ItemAdapter

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

        swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout)
        recyclerView = findViewById(R.id.recyclerView)

        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = ItemAdapter(items)
        recyclerView.adapter = adapter

        swipeRefreshLayout.setOnRefreshListener(this)
    }

    override fun onRefresh() {
        // Show loading spinner for 2 seconds
        Handler(Looper.getMainLooper()).postDelayed({
            for (i in items.indices) {
                items[i] = items[i].removeSuffix(" - refreshed") + " - refreshed"
            }
            adapter.notifyDataSetChanged()
            swipeRefreshLayout.isRefreshing = false
        }, 2000)
    }
}

class ItemAdapter(private val items: List<String>) : RecyclerView.Adapter<ItemAdapter.ViewHolder>() {
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.findViewById(android.R.id.text1)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(android.R.layout.simple_list_item_1, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.textView.text = items[position]
    }

    override fun getItemCount(): Int = items.size
}

This solution uses SwipeRefreshLayout to enable the pull-to-refresh gesture. The activity implements SwipeRefreshLayout.OnRefreshListener to listen for the pull gesture.

When the user pulls down, onRefresh() is called. We simulate a 2-second loading delay using a Handler and postDelayed. After the delay, we update each item by appending " - refreshed" (removing duplicates if any), then notify the adapter to refresh the list UI.

The refreshing spinner is stopped by setting isRefreshing = false. The ItemAdapter is a simple RecyclerView adapter that shows each item in a text view.

This approach keeps the UI responsive and provides a familiar pull-to-refresh experience.

Final Result
Completed Screen
-------------------------
| PullToRefreshScreen    |
-------------------------
| ↓ Pull down to refresh |
|-----------------------|
| Item 1 - refreshed     |
| Item 2 - refreshed     |
| Item 3 - refreshed     |
| Item 4 - refreshed     |
| Item 5 - refreshed     |
-------------------------
User pulls down from top of list
A spinner appears at top indicating loading
After 2 seconds, spinner disappears
List items update to show ' - refreshed' appended
Stretch Goal
Add a button that resets the list items back to original labels without ' - refreshed'.
💡 Hint
Add a Button below the list. On click, reset the items list to original values and notify adapter.

Practice

(1/5)
1. What is the main purpose of using SwipeRefreshLayout in an Android app?
easy
A. To handle background tasks
B. To create a navigation drawer
C. To enable pull-to-refresh functionality on a scrollable view
D. To display a popup menu

Solution

  1. Step 1: Understand SwipeRefreshLayout role

    SwipeRefreshLayout is a layout that wraps scrollable views to add pull-to-refresh support.
  2. Step 2: Identify its main function

    Its main function is to detect swipe down gestures and show a refresh spinner to update content.
  3. Final Answer:

    To enable pull-to-refresh functionality on a scrollable view -> Option C
  4. Quick Check:

    SwipeRefreshLayout = Pull-to-refresh wrapper [OK]
Hint: SwipeRefreshLayout wraps scroll views for pull-to-refresh [OK]
Common Mistakes:
  • Confusing SwipeRefreshLayout with navigation components
  • Thinking it handles background tasks automatically
  • Using it without wrapping a scrollable view
2. Which of the following is the correct way to set a refresh listener on a SwipeRefreshLayout named swipeRefresh in Kotlin?
easy
A. swipeRefresh.refreshListener = { refreshData() }
B. swipeRefresh.setOnRefreshListener { refreshData() }
C. swipeRefresh.onRefresh = refreshData()
D. swipeRefresh.setRefreshListener(refreshData)

Solution

  1. Step 1: Recall SwipeRefreshLayout listener syntax

    The correct method is setOnRefreshListener with a lambda for refresh action.
  2. Step 2: Match the correct Kotlin syntax

    swipeRefresh.setOnRefreshListener { refreshData() } uses the correct method and lambda syntax: swipeRefresh.setOnRefreshListener { refreshData() }.
  3. Final Answer:

    swipeRefresh.setOnRefreshListener { refreshData() } -> Option B
  4. Quick Check:

    Listener method = setOnRefreshListener [OK]
Hint: Use setOnRefreshListener with a lambda for refresh action [OK]
Common Mistakes:
  • Using incorrect property names like onRefresh or refreshListener
  • Calling refreshData() outside a lambda
  • Passing function reference without parentheses incorrectly
3. Given the following Kotlin code snippet, what will happen when the user pulls down on the list?
swipeRefresh.setOnRefreshListener {
    fetchData()
    swipeRefresh.isRefreshing = false
}
medium
A. The refresh spinner shows briefly, fetchData() runs, then spinner hides
B. The spinner never shows because isRefreshing is false immediately
C. fetchData() runs but spinner stays visible forever
D. App crashes due to missing coroutine for fetchData()

Solution

  1. Step 1: Analyze refresh listener behavior

    When user pulls down, setOnRefreshListener triggers, calling fetchData() and then sets isRefreshing = false.
  2. Step 2: Understand spinner visibility

    Spinner shows when refresh starts, then hides after isRefreshing is set false, so it appears briefly.
  3. Final Answer:

    The refresh spinner shows briefly, fetchData() runs, then spinner hides -> Option A
  4. Quick Check:

    Spinner visibility controlled by isRefreshing [OK]
Hint: Set isRefreshing false to hide spinner after refresh [OK]
Common Mistakes:
  • Assuming spinner never shows if isRefreshing set false immediately
  • Thinking fetchData() must be asynchronous here
  • Confusing spinner behavior with background thread errors
4. You implemented pull-to-refresh but the spinner never disappears after refresh. What is the most likely cause in this Kotlin code?
swipeRefresh.setOnRefreshListener {
    fetchData()
}
medium
A. Missing setting swipeRefresh.isRefreshing = false after fetchData()
B. fetchData() is called outside the listener
C. SwipeRefreshLayout is not wrapping the scrollable view
D. Using setOnRefreshListener instead of onRefreshListener

Solution

  1. Step 1: Identify spinner control

    The spinner stays visible until isRefreshing is set to false manually.
  2. Step 2: Check code for isRefreshing update

    The code does not set swipeRefresh.isRefreshing = false, so spinner never hides.
  3. Final Answer:

    Missing setting swipeRefresh.isRefreshing = false after fetchData() -> Option A
  4. Quick Check:

    Stop spinner by setting isRefreshing false [OK]
Hint: Always set isRefreshing = false to stop spinner [OK]
Common Mistakes:
  • Forgetting to stop spinner after refresh
  • Misplacing fetchData() call outside listener
  • Incorrectly wrapping views causing no refresh
5. You want to implement pull-to-refresh in a RecyclerView inside a Fragment. Which sequence of steps is correct to properly set it up in Kotlin?
hard
A. Wrap RecyclerView in SwipeRefreshLayout, call fetchData() outside listener, set isRefreshing true manually
B. Add SwipeRefreshLayout as sibling to RecyclerView, call fetchData() in onCreateView, no need to set isRefreshing
C. Use SwipeRefreshLayout without wrapping RecyclerView, set isRefreshing true in onViewCreated, no listener needed
D. Wrap RecyclerView in SwipeRefreshLayout, setOnRefreshListener to fetch data, then set isRefreshing false after update

Solution

  1. Step 1: Wrap RecyclerView in SwipeRefreshLayout

    SwipeRefreshLayout must wrap the scrollable RecyclerView to detect pull gestures.
  2. Step 2: Set refresh listener and stop spinner

    Use setOnRefreshListener to call data fetch, then set isRefreshing = false after update to hide spinner.
  3. Final Answer:

    Wrap RecyclerView in SwipeRefreshLayout, setOnRefreshListener to fetch data, then set isRefreshing false after update -> Option D
  4. Quick Check:

    Wrap + listener + stop spinner = correct setup [OK]
Hint: Wrap RecyclerView, set listener, then stop spinner after refresh [OK]
Common Mistakes:
  • Not wrapping RecyclerView inside SwipeRefreshLayout
  • Forgetting to set isRefreshing false after refresh
  • Calling fetchData() outside listener causing no refresh trigger
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard