Background
ViewPager binds to the view after scrolling, and therefore can RecyclerView if you use something like this:
LinearSnapHelper().attachToRecyclerView(recyclerView)
Or using the library to snap to a specific edge, as shown in this library . It can imitate ViewPager almost completely if you want, as shown in this library mentioned by CommonsWare here .
Both of them can function in the same way, if necessary (binding to one view, not necessarily with full free space), therefore this question concerns both of them.
There are some processing problems in ViewPager, but they can be fixed, for example, using this library .
Problem
I need to update the interface of the new view shown as soon as the RecyclerView / ViewPager is idle in the scroll.
Of course, since the user can still touch it, it would be a problem to know what should happen, so I should also block touch events when it sets the scroll state.
This means, for example, that when a user throws one page from page 0 to page 1, as soon as the link starts, tap the blocked events and I can find out that he is linking to page 1 and refresh this page.
The problem is that both RecyclerView and ViewPager do not offer this feature. I can get an element that was selected only after it stopped scrolling, and not while it settles.
What i tried
For ViewPager adapter has setPrimaryItem , so unfortunately it tells me which item is selected after it has completed the setup. I have an addOnPageChangeListener function that tells me about the scroll position at any given time (using onPageScrolled ), but it doesnโt tell me which direction I am going (left / right), and I canโt know what data will be displayed and which will be displayed . addOnPageChangeListener also provides scroll status (downtime, subsidence, drag and drop).
For RecyclerView I can get the scroll state callback when it is about to install and when it is idle, but I donโt see how to get the element it is about to install about.
About blocking touch events, I think that I could put a clickable view (which has no content, therefore it is invisible to the user) on top of it when it settles, and hide it (set the visibility to GONE ) when it is idle, but I am wondering if there is a better way.
I tried using setOnTouchListener for the RecyclerView idle and settling states, but when I tried to touch it when it was installed, it got stuck in the current scroll location.
So both of RecyclerView and ViewPager have obstacles to doing all this ...
Here is what I got so far:
ViewPager:

RecyclerView:

POC code of what I tried (both ViewPager and RecyclerView ):
MainActivity.kt
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val inflater = LayoutInflater.from(this) //viewPager area viewPager.adapter = object : RecyclerPagerAdapter<RecyclerPagerAdapter.ViewHolder>() { var selectedHolder: RecyclerPagerAdapter.ViewHolder? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return object : RecyclerPagerAdapter.ViewHolder(inflater.inflate(R.layout.cell, parent, false)) {} } override fun getItemCount(): Int = 100 override fun onBindViewHolder(holder: ViewHolder, position: Int) { (holder.itemView as TextView).text = position.toString() } override fun setPrimaryItem(container: ViewGroup?, position: Int, obj: Any?) { super.setPrimaryItem(container, position, obj) //TODO get the soon-to-be-selected page sooner val holder = obj as RecyclerPagerAdapter.ViewHolder if (selectedHolder != null && selectedHolder != holder) (selectedHolder!!.itemView as TextView).text = position.toString() (holder.itemView as TextView).text = "selected:${position.toString()}" selectedHolder = holder } } viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) { when (state) { ViewPager.SCROLL_STATE_DRAGGING -> Log.d("AppLog", "onPageScrollStateChanged: SCROLL_STATE_DRAGGING") ViewPager.SCROLL_STATE_IDLE -> Log.d("AppLog", "onPageScrollStateChanged: SCROLL_STATE_IDLE") ViewPager.SCROLL_STATE_SETTLING -> Log.d("AppLog", "onPageScrollStateChanged: SCROLL_STATE_SETTLING") } } override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { Log.d("AppLog", "onPageScrolled: position:$position positionOffset :$positionOffset positionOffsetPixels:$positionOffsetPixels") } override fun onPageSelected(position: Int) { Log.d("AppLog", "onPageSelected:" + position) } }) //recyclerView area // Not needed, as I use a library for this: LinearSnapHelper().attachToRecyclerView(recyclerView) recyclerView.setHasFixedSize(true) recyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() { override fun getItemCount(): Int = 100 override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder { return object : RecyclerView.ViewHolder(inflater.inflate(R.layout.cell, parent, false)) {} } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder.itemView as TextView).text = position.toString() } } recyclerView.addOnPageChangedListener { oldPosition, newPosition -> Log.d("AppLog", "OnPageChanged:$oldPosition->$newPosition") } recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { @SuppressLint("ClickableViewAccessibility") override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { super.onScrollStateChanged(recyclerView, newState) when (newState) { RecyclerView.SCROLL_STATE_IDLE -> { Log.d("AppLog", "state: SCROLL_STATE_IDLE") recyclerViewStateTextView.text = "state: SCROLL_STATE_IDLE" //setOnTouchListener doesn't really work well. It makes the scrolling stuck // recyclerView!!.setOnTouchListener(null) } RecyclerView.SCROLL_STATE_SETTLING -> { //TODO when settling, block touches, and update the soon-to-be-focused page Log.d("AppLog", "state: SCROLL_STATE_SETTLING") recyclerViewStateTextView.text = "state: SCROLL_STATE_SETTLING" // recyclerView!!.setOnTouchListener(object : View.OnTouchListener { // override fun onTouch(v: View?, event: MotionEvent?): Boolean { // return true // } // // }) } RecyclerView.SCROLL_STATE_DRAGGING -> { Log.d("AppLog", "state: SCROLL_STATE_DRAGGING") recyclerViewStateTextView.text = "state: SCROLL_STATE_DRAGGING" } } } }) recyclerViewStateTextView.text = "state: SCROLL_STATE_IDLE" }
cell.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:id="@android:id/text1" android:textSize="36sp" tools:text="@tools:sample/lorem"/>
activity_main.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:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.user.snapblockertest.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ViewPager :"/> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="RecyclerView with snapping:"/> <com.lsjwzh.widget.recyclerviewpager.RecyclerViewPager android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1" android:orientation="horizontal" app:layoutManager="android.support.v7.widget.LinearLayoutManager" app:rvp_singlePageFling="true" app:rvp_triggerOffset="0.1"/> <TextView android:id="@+id/recyclerViewStateTextView" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
gradle files are "non-standard" dependencies from here and here
//https://github.com/henrytao-me/recycler-pager-adapter implementation "me.henrytao:recycler-pager-adapter:2.1.0" //https://github.com/lsjwzh/RecyclerViewPager implementation 'com.github.lsjwzh.RecyclerViewPager:lib:v1.1.2@aar'
Questions
How can I get a callback when the ViewPager / RecyclerView sets, including which element will be bound to?
How to block touch events from the moment of its establishment to the moment of its idle time? Is there a better way than what I wrote (with clickable top view)?
Update:
It seems that for the ViewPager, I could use the onPageSelected callback to get the element that it is going to set. Interestingly, the best way to get the ViewHolder on my page this way. I could save the necessary data in onBindViewHolder and then check it myself, but I am wondering if there is a better way.
Now there is not enough how to do this for the RecyclerView and how to block touch events (if there is a better way than what I wrote). The library has a function called addOnPageChangedListener , but it is called after the completion of the settlement, so this will not help here.