I have a android.support.v4.view.ViewPager
in my application and I would like to differentiate between a programmatically-initiated smooth scroll and a user-initiat
I have based myself on the answer mark as correct and in the comments below.
First I analyze how the complete listener behave:
USER
onPageScrollStateChanged: 1 SCROLL_STATE_DRAGGING
onPageScrollStateChanged: 2 SCROLL_STATE_SETTLING
onPageSelected: SELECTION
onPageScrollStateChanged: 0 SCROLL_STATE_IDLE
PROGRAMATIC
onPageScrollStateChanged: 2 SCROLL_STATE_SETTLING
onPageSelected: SELECTION
onPageScrollStateChanged: 0 SCROLL_STATE_IDLE
As you can see in both cases the events end when the onPageScrollStateChanged
move to SCROLL_STATE_IDLE
, this means idle is the end of the cycle
The user event is SCROLL_STATE_DRAGGING
and then SCROLL_STATE_SETTLING
which are 2 states
different than only 1 state
for the programmatic event SCROLL_STATE_SETTLING
onPageSelected
happens before the end of the cycle, but after we are able to determinate if the change was triggered by the user or programmatically, so whatever happened before will tell us in that point if it was the user or not
So I use a List<Integer>
that is reset every time the cycle ends, and to be able to know if the user triggered the event in the onPageSelected
method I check the size of the List
. If the size is 2 that means the user scroll the pager.
abstract class PagerListenerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {
private static final int USER_SCROLL = 2;
private List<Integer> validator = new ArrayList<>();
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (validator.size() == USER_SCROLL) {
userScroll(position);
}
}
@Override
public void onPageScrollStateChanged(int state) {
validator.add(state);
if (ViewPager.SCROLL_STATE_IDLE == state) {
validator.clear();
}
}
protected abstract void userScroll(int position);
}
Now, this class can conveniently be inherited by another that need it.
You are right about using ViewPager.OnPageChangeListener
:
@Override
public void onPageSelected(int arg0) {
// programmatically-initiated
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
// user-initiated touch scroll
}
Alternatively, you can use boolean flags to differentiate between programmatically-initiated smooth scroll and a user-initiated touch scroll
. For example, if you use setCurrentItem(int item)
to programmatically change the page, try:
boolean progChange = false;
....
....
....
progChange = true;
setCurrentItem(somePageId); // Set progChange = true every time
....
....
....
Inside your ViewPager.OnPageChangeListener
:
@Override
public void onPageSelected(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
if (progChange) {
// programmatically-initiated
} else {
// user-initiated touch scroll
}
// Set progChange to false;
progChange = false;
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
OK, so it turns out that I was right about the answer lying in ViewPager.onPageChangeListener
. In particular it lies in using onPageScrollStateChanged(int state)
. Essentially there are three states that a page in a ViewPager
can be in:
So the dragging state only occurs when the current page is being physically dragged by the user. Thus when the user has swiped a page the states occur in the following order: Dragging -> Settling -> Idle. Now, the onPageSelected(int position)
method is called between the "Settling" and "Idle" states. Thus, in order to determine whether or not a page change was caused by a user scroll one just needs to check that the previous state was "dragging" and that the current state is "Settling". You can then keep a boolean
variable to track whether or not the page change was user initiated or not and check it in your onPageSelected(int position)
method.
Here is my onPageScrollStateChanged
method
public void onPageScrollStateChanged(int state)
{
if (previousState == ViewPager.SCROLL_STATE_DRAGGING
&& state == ViewPager.SCROLL_STATE_SETTLING)
userScrollChange = true;
else if (previousState == ViewPager.SCROLL_STATE_SETTLING
&& state == ViewPager.SCROLL_STATE_IDLE)
userScrollChange = false;
previousState = state;
}
The if
and else if
statements need not be so explicit but I did so for clarity.