Reputation: 1713
I am making a list, that will get more items when I go to the bottom of the list. I want this list to be refreshable too, so I need a SwipeRefreshLayout
.
Let's say I have an adapter like this :
class OrdersListAdapter(val selected : (Order) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
fun addOrders(orders: ArrayList<Order>) {
this.orders.addAll(orders)
notifyItemRangeInserted(this.orders.size - orders.size + FIRST_ITEM_INDEX, orders.size)
}
fun setFirstOrders(orders: ArrayList<Order>) {
this.orders = orders
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return (orders.size + 1 + FIRST_ITEM_INDEX)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
TYPE_ORDERS -> OrdersViewHolder(DataBindingUtil.inflate<OrdersItemBinding>(LayoutInflater.from(parent.context),
R.layout.orders_item, parent, false).apply {
viewModel = OrdersViewModel()
})
else -> LoadingMoreOrdersViewHolder(DataBindingUtil.inflate<LoadingMoreOrdersItemBinding>(LayoutInflater.from(parent.context),
R.layout.loading_more_orders_item, parent, false).apply {
viewModel = LoadingMoreOrdersViewModel()
})
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
TYPE_ORDERS -> (holder as? OrdersViewHolder)?.setData(orders[position - FIRST_ITEM_INDEX])
TYPE_LOADING -> (holder as? LoadingMoreOrdersViewHolder)?.setState(loading, error, noMoreItems)
}
}
override fun getItemViewType(position: Int): Int =
if (position == FIRST_ITEM_INDEX - 1) TYPE_HEADER else if (position == itemCount - 1) TYPE_LOADING else TYPE_ORDERS
Well if I put my RecyclerView
like this, inside a SwipeRefreshLayout
:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<android.support.v7.widget.RecyclerView
android:id="@+id/orders_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</android.support.v4.widget.SwipeRefreshLayout>
This will display all items, but the refreshLayout will not work : If I pull my view, nothing happens.
But if I do not set any loading layout inside my adapter (the one at bottom of the list, to load more items, inflated from R.layout.loading_more_orders_item
), then the SwipeRefreshLayout will work properly.
How can I have 2 view types in my adapter, AND set my RecyclerView in a SwipeRefreshLayout ?
EDIT :
Here is my onRefreshListener :
refreshLayout = binding.swipeRefreshLayout
refreshLayout?.setOnRefreshListener { reloadOrders() }
And in reloadOrders() :
private fun reloadOrders() {
viewModel.setLoading()
refreshLayout?.isRefreshing = false
Observable.timer(1, TimeUnit.SECONDS)
.subscribe { getOrders() }
}
Upvotes: 0
Views: 278
Reputation: 1713
Okay so thanks to @r2rek, I found a solution that worked for me :
In the code, we create a class that extends LinearLayoutManager
and overrides method canScrollVertically()
to always return false :
inner class MyLinearLayout(context: Context) : LinearLayoutManager(context) {
override fun canScrollVertically(): Boolean {
return (false)
}
}
Then, when we init our RecyclerView
, we apply this class to our layoutManager
:
ordersRv?.layoutManager = MyLinearLayout(this)
This will allow you to refresh, however you will not be able to scroll in your RecyclerView
(duh).
To fix this, add a NestedScrollView
in your xml, as following :
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:visibility="@{viewModel.ordersVisibility}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/orders_rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.NestedScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
You can now scroll in your RecyclerView
, and pull to refresh with your SwipeRefreshLayout
.
I think this method is kinda hacky, so if someone have a better solution, feel free to tell us !
Upvotes: 0
Reputation: 2253
I believe that what happens is the recyclerview takes over the ontouch events from swiperefreshlayout. You could try overwriting linearlayout and its canScrollVertically()
method to return false. Cheers!
Upvotes: 1
Reputation: 354
Answering your question, compatibility is not the issue you're having. Not sure of the size of your layout, as its height is 0dp. I'd play around with the view sizes and view hierarchy.
Upvotes: 0