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-initiated touch scroll.
I’ve had a look at ViewPager.OnPageChangeListener
and I believe that the answer may lie in there but I’m just not sure how.
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:
- Dragging: Indicates that the pager is currently being dragged by the user.
- Idle: Indicates that the pager is in an idle, settled state.
- Settling: Indicates that the pager is in the process of settling to a final position.
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.
Answer:
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) {
}
Answer:
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
Findings:
-
As you can see in both cases the events end when the
onPageScrollStateChanged
move toSCROLL_STATE_IDLE
, this means idle is the end of the cycle -
The user event is
SCROLL_STATE_DRAGGING
and thenSCROLL_STATE_SETTLING
which are 2states
different than only 1state
for the programmatic eventSCROLL_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
Solution:
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.
Tags: androidandroid, scroll, view