1

I have a SlidingPaneLayout with a RecyclerView and a FragmentContainerView inside of it, as shown below.

What I'm trying to implement, is the "pull down to refresh" functionality on the recyclerview, but I'm a bit stuck. The only examples that I find, are with SwipeRefreshLayout, but I'd really like to keep the SlidingPaneLayout.

Is there any way to implement this "pull down to refresh" functionality? Or should I accept that I have to redo my layout?

<?xml version="1.0" encoding="utf-8"?>
<androidx.slidingpanelayout.widget.SlidingPaneLayout
    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:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FeedBaseFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:padding="8dp"
        android:layout_width="550dp"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:layout_gravity="start"/>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_container"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:name="com.example.test.FeedDetailsFragment" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

class FeedBaseFragment : Fragment() {
    private lateinit var currentView: View

    private val feedItemsViewModel: FeedItemsViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_feed_base, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        currentView = view
        super.onViewCreated(currentView, savedInstanceState)
        val slidingPaneLayout = currentView.findViewById<SlidingPaneLayout>(R.id.sliding_pane_layout)

        slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED

        // Initialize the adapter and set it to the RecyclerView.
        val adapter = FeedItemsAdapter {
            feedItemsViewModel.updateCurrentFeedItem(it)
            slidingPaneLayout.openPane()
        }
        currentView.findViewById<RecyclerView>(R.id.recycler_view).adapter = adapter
        adapter.submitList(feedItemsViewModel.feedItemsData)
    }
}
Mathlight
  • 6,436
  • 17
  • 62
  • 107
  • You can try extending the `SlidingPaneLayout` and overriding the onTouch method that will work in conjunction with [this](https://stackoverflow.com/questions/13095494/how-to-detect-swipe-direction-between-left-right-and-up-down) – Prajjwal Srivastav Apr 20 '23 at 13:19
  • Sorry, I'm not really sure I followed your question. This is what I get when I run your code, right? https://imgur.com/a/zkrJ4BT Why implementing the PTR functionality would require you to remove the SlidingPaneLayout? – 4gus71n Apr 24 '23 at 15:01

3 Answers3

2

You can definitely implement "pull down to refresh" functionality in your SlidingPaneLayout with a RecyclerView. You just need to wrap your RecyclerView inside a SwipeRefreshLayout. This won't affect your SlidingPaneLayout, and you can still use it as intended. Here's how you can do it:

  1. In your layout XML file, wrap your RecyclerView with a SwipeRefreshLayout:
<androidx.slidingpanelayout.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>
  1. In your Java or Kotlin code, set up the SwipeRefreshLayout and implement the refresh listener:

Kotlin:

import androidx.swiperefreshlayout.widget.SwipeRefreshLayout

class YourActivity : AppCompatActivity() {

    private lateinit var swipeRefreshLayout: SwipeRefreshLayout

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

        swipeRefreshLayout = findViewById(R.id.swipe_refresh_layout)

        // Set up the refresh listener
        swipeRefreshLayout.setOnRefreshListener {
            // Refresh your data here
            refreshData()
        }
    }

    private fun refreshData() {
        // Perform your data refreshing logic here
        // ...

        // Don't forget to call this method once the data is refreshed
        swipeRefreshLayout.isRefreshing = false
    }
}

This should allow you to implement the "pull down to refresh" functionality in your RecyclerView without having to change your SlidingPaneLayout.

Hayes Haugen
  • 822
  • 1
  • 7
  • 7
1

To implement pull-down-to-refresh functionality in a RecyclerView within a SlidingPaneLayout

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    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:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FeedBaseFragment">

     <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_container"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:name="com.example.test.FeedDetailsFragment" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

In you activity

val swipeRefreshLayout = findViewById<SwipeRefreshLayout>(R.id.swipe_refresh_layout)
swipeRefreshLayout.setOnRefreshListener {
    // Perform the data update operation here
    // When the data update is complete, call `swipeRefreshLayout.isRefreshing = false` to hide the loading indicator
}

Add a OnScrollListener to RecyclerView to disable the swipe gesture inside SlidingPaneLayout when the RecyclerView is scrolled at the top

    val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)
        val swipeRefreshLayout = findViewById<SwipeRefreshLayout>(R.id.swipe_refresh_layout)
        val slidingPaneLayout = findViewById<SlidingPaneLayout>(R.id.sliding_pane_layout)
        // Disable the swipe gesture of the SlidingPaneLayout when the RecyclerView is scrolled to the top
        slidingPaneLayout.isEnabled = !recyclerView.canScrollVertically(-1)
        // Enable the swipe gesture of the SwipeRefreshLayout when the RecyclerView is at the top and not refreshing
        swipeRefreshLayout.isEnabled = !recyclerView.canScrollVertically(-1) && !swipeRefreshLayout.isRefreshing
    }
})
Anks
  • 11
  • 2
0
 class FeedBaseFragment : Fragment() {
    private lateinit var currentView: View

    private val feedItemsViewModel: FeedItemsViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_feed_base, container, false)
    }

    var newData = false
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        currentView = view
        super.onViewCreated(currentView, savedInstanceState)
        val slidingPaneLayout =
            currentView.findViewById<SlidingPaneLayout>(R.id.sliding_pane_layout)

        if (newData) {
            slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED

            // Initialize the adapter and set it to the RecyclerView.
            val adapter = FeedItemsAdapter {
                feedItemsViewModel.updateCurrentFeedItem(it)
                slidingPaneLayout.openPane()
            }
            currentView.findViewById<RecyclerView>(R.id.recycler_view).adapter = adapter
            adapter.submitList(feedItemsViewModel.feedItemsData)
            newData = false
        } else {
            val adapter = FeedItemsAdapter {
                feedItemsViewModel.updateCurrentFeedItem(it)
                slidingPaneLayout.openPane()
            }
            currentView.findViewById<RecyclerView>(R.id.recycler_view).adapter = adapter
            adapter.submitList(feedItemsViewModel.feedItemsData)
            newData = true
        }

    }
}

My suggestion for you is to use this method, I hope it works for you too