Reputation: 385
I'd like to add bottom padding to the last view in a RecyclerView, but only if the RecyclerView is scrollable - in other words, if the last adapter item is completely visible with no scroll, do not add padding. I realized that from within onBindViewHolder, findLastCompletelyVisibleItemPosition()
will always return the previous item's position because the current view is not technically visible yet. I also tried an ItemDecorator but this also doesn't work, since these are added before the views and so we still wouldn't know whether the RecyclerView is scrollable or not. My ideal method would look something like this:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
...
if (position == items.size() - 1 && [is scrollable]) {
((MyViewHolder) viewHolder).addBottomPadding(...);
}
}
That [is scrollable] bit is what I'm not sure of. Is there another way to accomplish this?
Upvotes: 3
Views: 2013
Reputation: 255
Add android:clipToPadding attribute to your view and give padding to your view that will scroll with recylerview item.
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="16dp"
android:paddingTop="16dp"
/>
Hope this will help
Upvotes: 4
Reputation: 4206
Okay so I have a solution for you :).
The core concept is attaching a viewTreeObserver OnGlobalLayoutListener
to the last item in the onBind
. Then the OnGlobalLayout
method of it will be invoked after the View has been measured which is critical for your requirement. Once it is called just calculate y + itemViewHeight
and compare it to the height of the RecyclerView
. To get this height you also have to attach an OnGlobalLayoutListener
to the RecyclerView and then set the Adapter with the height of it as parameter
. You may need to set an "empty" Adapter before your onGobalLayout gets called to prevent an error.
Two important things to remember:
- Never forget to remove the OnGlobalLayoutListener
-
The code below is
ugly and just a minimal viable product
The Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rvTest.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
//maybe an adapter without content has to be provided so you won't get the error: no adapter attached skipping layout
rvTest.viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
rvTest.adapter = RvTestAdapter(this@MainActivity, rvTest.height)
rvTest.viewTreeObserver.removeOnGlobalLayoutListener(this) //must remove!
}
})
}
}
The Adapter:
class RvTestAdapter(val context: Context, val recyclerViewHeight: Int): RecyclerView.Adapter<TestViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = TestViewHolder(LayoutInflater.from(context).inflate(R.layout.vh_test, parent, false))
override fun getItemCount() = 3
override fun onBindViewHolder(holder: TestViewHolder, position: Int) {
if (position == 2) { //last position
holder.itemView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (holder.itemView.y + holder.itemView.height > recyclerViewHeight) {
Log.d("YESSSS", "WOOP WOOP")
}
holder.itemView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
}
}
(hope you don't mind Kotlin code)
Upvotes: 1