Implementing Circular Scrolling In PagerAdapter - android

I am using PagerAdapter for horizontal swiping for showing newspaper pages in my app.
Currently I want to implement the circular scrolling in this app.Right now what I have done is whenever I am getting on last page I try to set the currentItem to first pagei.e that functionality working for last page to first page,but the problem is that how can I go to last page from first page.
Here I am pasting my code related to pagerAdapter & onPageChangeListener:-
awesomeAdapter = new AwesomePagerAdapter(awesomePager);
awesomePager.setAdapter(awesomeAdapter);
awesomePager.setPageMargin(10);
awesomePager.setOnPageChangeListener(new OnPageChangeListener() {
int lastPosition;
float posOffset = 0;
#Override
public void onPageSelected(int position) {
viewerPage = position;
CommonLogic.logMessage("Viewer Page:- "+ viewerPage, TAG, Log.VERBOSE);
posOffset = 0;
}
#Override
public void onPageScrolled(int position,float positionOffset,int positionOffsetPixels) {
if (positionOffset == 0 && positionOffsetPixels == 0 && position != 0) {
lastPosition = position;
}
posOffset -= positionOffset;
CommonLogic.logMessage(" Position:- "
+ position + " Position Offset:- " + positionOffset
+ " Position Offset Variable:- "
+ posOffset
+ " Position Offset Pixels:- "
+ positionOffsetPixels
+ " Last Position " + lastPosition,
TAG, Log.VERBOSE);
CommonLogic.logMessage(" Last Position "
+ lastPosition, TAG, Log.VERBOSE);
}
#Override
public void onPageScrollStateChanged(int state) {
// To Detect the Last Page & This Sets it to first page.This working fine.
if (state == ViewPager.SCROLL_STATE_DRAGGING && viewerPage == (uris.size() - 1)) {
CommonLogic.logMessage("Scroll State Changed ", TAG,Log.VERBOSE);
postDelayed(new Runnable() {
#Override
public void run() {
awesomePager.setCurrentItem(0, true);
}
}, 200);
}
// I have also used this to detect whether the user is on first & try to move on last page,but it is not working well.
else if (state == ViewPager.SCROLL_STATE_DRAGGING && (lastPosition == 0 || lastPosition == (uris.size() - 1)) && viewerPage == 0 && posOffset <= 0) {
CommonLogic.logMessage( "Scroll State Changed ", TAG,Log.VERBOSE);
postDelayed(new Runnable() {
#Override
public void run() {
awesomePager.setCurrentItem((uris.size() - 1), true);
}
}, 200);
}
}
}
});
Also the PagerAdapter i.e AwesomweAdapter in my case,is also as folllows:-
private class AwesomePagerAdapter extends PagerAdapter {
ViewPager pdfContainer;
DocumentNewView documentNewView;
CustomViewPager customViewPager;
public AwesomePagerAdapter(CustomViewPager awesomePager) {
this.customViewPager = awesomePager;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return uris.size();
}
public DocumentNewView addViewAt(int position, DocumentNewView mainView) {
CommonLogic.logMessage("Position of View:- " + position, TAG,
Log.VERBOSE);
pdfContainer.addView(mainView);
return mainView;
}
/**
* Create the page for the given position. The adapter is responsible
* for adding the view to the container given here, although it only
* must ensure this is done by the time it returns from
* {#link #finishUpdate()}.
*
* #param container
* The containing View in which the page will be shown.
* #param position
* The page position to be instantiated.
* #return Returns an Object representing the new page. This does not
* need to be a View, but can be some other container of the
* page.
*/
#Override
public Object instantiateItem(View collection, int position) {
CommonLogic
.logMessage("Instantiate Item Called ", TAG, Log.VERBOSE);
documentNewView = new DocumentNewView(cxt, display, customViewPager);
documentNewView.setPdfContext(new PdfContext());
CodecDocument codecDocument = documentNewView.open(uris
.get(position));
documentNewView.renderDocument(codecDocument);
documentNewView.setMaxZoom(4f);
documentNewView.setVerticalScrollBarEnabled(true);
codecDocument = null;
this.pdfContainer = (ViewPager) collection;
return addViewAt(position, documentNewView);
}
/**
* Remove a page for the given position. The adapter is responsible for
* removing the view from its container, although it only must ensure
* this is done by the time it returns from {#link #finishUpdate()}.
*
* #param container
* The containing View from which the page will be removed.
* #param position
* The page position to be removed.
* #param object
* The same object that was returned by
* {#link #instantiateItem(View, int)}.
*/
#Override
public void destroyItem(View collection, int position, Object view) {
pdfContainer.removeView((DocumentNewView) view);
}
/**
* Called when the a change in the shown pages has been completed. At
* this point you must ensure that all of the pages have actually been
* added or removed from the container as appropriate.
*
* #param container
* The containing View which is displaying this adapter's
* page views.
*/
#Override
public void finishUpdate(View arg0) {
CommonLogic.logMessage("Finish Update Called ", TAG, Log.VERBOSE);
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public void startUpdate(View arg0) {
CommonLogic.logMessage("State Update Called ", TAG, Log.VERBOSE);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((DocumentNewView) object);
}
Please give me any suggestions/changes in my code (if applicable) for it.
Thanks in Advance.

I could achieve this by overriding onPageSelected method of OnPageChangeListener. Consider you have three pages in this order A<->B<->C. To goal is to reach C if we scroll right from A and similarly to reach A if we scroll left from C.
To do this, define your to have 5 pages (3+2), and organize the pages as follows:
C<->A<->B<->C<->A
Now in the onPageSelected method, check and if position if 0, change it to 3 (getCount()-2) and if position is 4 (getCount()-1), change it to 1. Make sure to use the method:
setCurrentItem(item, smoothScroll)
Here is complete code for CircularPagerAdaptor Class :
package zolender.adapters;
import android.content.Context;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
public class CircularPagerAdapter extends PagerAdapter{
private int[] pageIDsArray;
private int count;
public CircularPagerAdapter(final ViewPager pager, int... pageIDs) {
super();
int actualNoOfIDs = pageIDs.length;
count = actualNoOfIDs + 2;
pageIDsArray = new int[count];
for (int i = 0; i < actualNoOfIDs; i++) {
pageIDsArray[i + 1] = pageIDs[i];
}
pageIDsArray[0] = pageIDs[actualNoOfIDs - 1];
pageIDsArray[count - 1] = pageIDs[0];
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
int pageCount = getCount();
if (position == 0){
pager.setCurrentItem(pageCount-2,false);
} else if (position == pageCount-1){
pager.setCurrentItem(1,false);
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrollStateChanged(int state) {
// TODO Auto-generated method stub
}
});
}
public int getCount() {
return count;
}
public Object instantiateItem(View container, int position) {
LayoutInflater inflater = (LayoutInflater) container.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int pageId = pageIDsArray[position];
View view = inflater.inflate(pageId, null);
((ViewPager) container).addView(view, 0);
return view;
}
#Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
#Override
public void finishUpdate(View container) {
// TODO Auto-generated method stub
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
// TODO Auto-generated method stub
}
#Override
public Parcelable saveState() {
// TODO Auto-generated method stub
return null;
}
#Override
public void startUpdate(View container) {
// TODO Auto-generated method stub
}
}
And here is how you can use it:
myPager = (ViewPager) findViewById(R.id.myfivepanelpager);
PagerAdapter adapter = new CircularPagerAdapter(myPager, new int[]{R.layout.farleft, R.layout.left, R.layout.middle, R.layout.right, R.layout.farright});
myPager.setAdapter(adapter);
myPager.setCurrentItem(3);

I also needed a circular ViewPager. This is what I've done. I assume you get pageCount value from somewhere.
...
pager = (ViewPager) findViewById(R.id.pager);
//Gesture detection
final GestureDetector gestureDetector = new GestureDetector(new MyGestureDetector());
pager.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
//pagelistener is just for getting selected page
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
selectedPage = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
and here is the GestureDetector.
Copied from here
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
int SWIPE_MIN_DISTANCE = Utils.ConvertToPixel(mContext, 50);
int SWIPE_MAX_OFF_PATH = Utils.ConvertToPixel(mContext, 250);
int SWIPE_THRESHOLD_VELOCITY = Utils.ConvertToPixel(mContext, 200);
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
return false;
// right to left swipe
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY
&& selectedPage == (pageCount - 1)) {
pager.setCurrentItem(0);
return true;
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY
&& selectedPage == 0) {
pager.setCurrentItem(pageCount - 1);
return true;
}
} catch (Exception e) {
// nothing
}
return false;
}
}

Expanding on Z0lenDer's answer, when using a regular ViewPager where you don't need to free the memory for each associated view, it's more efficient to store the created views rather than the layout IDs. This is necessary if wanting to get rid of any delay and flicker when the item is being switched.
There's also an issue with the animation when using onPageSelected, as it doesn't let the slide finish before doing the switch. The only way I found to avoid this is to only perform the switch once the scroll state has changed to SCROLL_STATE_IDLE and just setting the current item in onPageSelected.
private int currentPage = 0;
...
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
currentPage = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageScrollStateChanged(int state) {
// TODO Auto-generated method stub
Log.d(TAG, "onPageScrollStateChanged: " + state);
if (state == ViewPager.SCROLL_STATE_IDLE) {
int pageCount = getCount();
if (currentPage == 0){
pager.setCurrentItem(pageCount-2,false);
} else if (currentPage == pageCount-1){
pager.setCurrentItem(1,false);
}
}
}
});

Try this
((ViewPager) container)
.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
Log.i("TAG", "pos::" + position);
}
#Override
public void onPageScrollStateChanged(int state) {
// TODO Auto-generated method stub
int currentPage = pager.getCurrentItem();
Log.i("TAG", "currentPage::" + currentPage);
Log.i("TAG", "currentState::" + currentState);
Log.i("TAG", "previousState::" + previousState);
if (currentPage == 4 || currentPage == 0) {
previousState = currentState;
currentState = state;
if (previousState == 1 && currentState == 0) {
pager.setCurrentItem(currentPage == 0 ? 4 : 0);
}
}
}
#Override
public void onPageScrolled(int arg0, float arg1,
int arg2) {
// TODO Auto-generated method stub
}
});
return
This should be placed inside
#Override
public Object instantiateItem(final View container, int position) {}

I used it this way,
fragment layouts in adapter 0>1>2>3>4>5,
0 & 5 are dummy
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(1, false); //going to page 1;
final int[] pagePosition = new int[1];
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
pagePosition[0] = position;
}
#Override
public void onPageScrollStateChanged(int state) { //state changes from 2 to 0 during a swipe
if (state == 0 && pagePosition[0] == 0){
viewPager.setCurrentItem(4, false);
} else if (state == 0 && pagePosition[0] == 5){
viewPager.setCurrentItem(1, false);
}
}
});

Well this helped
private class CircularViewPagerHandler implements ViewPager.OnPageChangeListener {
private ViewPager mViewPager;
private int mCurrentPosition;
private int mScrollState;
private int mPreviousPosition;
public CircularViewPagerHandler(final ViewPager viewPager) {
mViewPager = viewPager;
}
#Override
public void onPageSelected(final int position) {
mCurrentPosition = position;
mPreviousPosition = position-1;
}
#Override
public void onPageScrollStateChanged(final int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
setNextItemIfNeeded();
}
mScrollState = state;
}
private void setNextItemIfNeeded() {
if (!isScrollStateSettling()) {
handleSetNextItem();
}
}
private boolean isScrollStateSettling() {
return mScrollState == ViewPager.SCROLL_STATE_SETTLING; //indicated page is settling to it's final position
}
private void handleSetNextItem() {
final int lastPosition = mViewPager.getAdapter().getCount() - 1;
if (mCurrentPosition == 0) {
mViewPager.setCurrentItem(lastPosition,false);
} else if (mCurrentPosition == lastPosition) {
mViewPager.setCurrentItem(0, false);
}
}
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
}
}
It was #tobi_b's answer

Related

Android: Animation works in KitKat and LolliPop but not on other API versions

I have to build animation like This.
Sorry I don't have too much reputation to upload image. you can find gif file from the above link.
I have done all this and it works fine on KitKat and LollyPop only but not on the other API version. I am using this library. Can any one please figure out the actual problem
Here is my code. Thanks in advance
public abstract class AbstractSlideExpandableListAdapter extends WrapperListAdapterImpl
{
private View lastOpnUpperView = null;
ArrayList<View> upperViewsList = new ArrayList<View>();
ArrayList<View> lowerViewsList = new ArrayList<View>();
ArrayList<View> circleViewsList = new ArrayList<View>();
private View lastOpen = null;
private int lastOpenPosition = -1;
private int lastOpenItemIndex = 0;
private int animationDuration = 800;
private BitSet openItems = new BitSet();
private final SparseIntArray viewHeights = new SparseIntArray(10);
private ViewGroup parent;
public AbstractSlideExpandableListAdapter(ListAdapter wrapped)
{
super(wrapped);
lastOpenPosition = 0;
openItems.set(lastOpenPosition, true);
}
private OnItemExpandCollapseListener expandCollapseListener;
public void setItemExpandCollapseListener(OnItemExpandCollapseListener listener)
{
expandCollapseListener = listener;
}
public void removeItemExpandCollapseListener()
{
expandCollapseListener = null;
}
public interface OnItemExpandCollapseListener
{
public void onExpand(View itemView, int position);
public void onCollapse(View itemView, int position);
}
private void notifiyExpandCollapseListener(int type, View view, int position)
{
if (expandCollapseListener != null)
{
if (type == ExpandCollapseAnimation.EXPAND)
{
expandCollapseListener.onExpand(view, position);
}
else if (type == ExpandCollapseAnimation.COLLAPSE)
{
expandCollapseListener.onCollapse(view, position);
}
}
}
#Override
public View getView(int position, View view, ViewGroup viewGroup)
{
this.parent = viewGroup;
view = wrapped.getView(position, view, viewGroup);
enableFor(view, position);
return view;
}
public abstract View getExpandToggleButton(View parent);
public abstract View getExpandableView(View parent);
// upperView to expand animation for sequeeze
public abstract View getUpperView(View upperView);
// Lower view to expand and collapse
public abstract View getLowerView(View upperView);
// Get the circle view to hide and show
public abstract View getCircleView(View circleView);
/**
* Gets the duration of the collapse animation in ms. Default is 330ms. Override this method to change the default.
*
* #return the duration of the anim in ms
*/
public int getAnimationDuration()
{
return animationDuration;
}
/**
* Set's the Animation duration for the Expandable animation
*
* #param duration
* The duration as an integer in MS (duration > 0)
* #exception IllegalArgumentException
* if parameter is less than zero
*/
public void setAnimationDuration(int duration)
{
if (duration < 0)
{
throw new IllegalArgumentException("Duration is less than zero");
}
animationDuration = duration;
}
/**
* Check's if any position is currently Expanded To collapse the open item #see collapseLastOpen
*
* #return boolean True if there is currently an item expanded, otherwise false
*/
public boolean isAnyItemExpanded()
{
return (lastOpenPosition != -1) ? true : false;
}
public void enableFor(View parent, int position)
{
View more = getExpandToggleButton(parent);
View itemToolbar = getExpandableView(parent);
View upperView = getUpperView(parent);
View circleView = getCircleView(parent);
View lowerView = getLowerView(parent);
itemToolbar.measure(parent.getWidth(), parent.getHeight());
upperViewsList.add(upperView);
circleViewsList.add(circleView);
lowerViewsList.add(lowerView);
if (position == 0)
{
// lastopenUpperViewTemporary = upperView;
lastOpnUpperView = upperView;
upperView.setVisibility(View.GONE);
lowerView.setVisibility(View.GONE);
}
enableFor(more, upperView, itemToolbar, position);
itemToolbar.requestLayout();
}
private void animateListExpand(final View button, final View target, final int position)
{
target.setAnimation(null);
int type;
if (target.getVisibility() == View.VISIBLE)
{
type = ExpandCollapseAnimation.COLLAPSE;
}
else
{
type = ExpandCollapseAnimation.EXPAND;
}
// remember the state
if (type == ExpandCollapseAnimation.EXPAND)
{
openItems.set(position, true);
}
else
{
openItems.set(position, false);
}
// check if we need to collapse a different view
if (type == ExpandCollapseAnimation.EXPAND)
{
if (lastOpenPosition != -1 && lastOpenPosition != position)
{
if (lastOpen != null)
{
animateWithUpperView(lastOpen, ExpandCollapseAnimation.COLLAPSE, position);
// animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
notifiyExpandCollapseListener(ExpandCollapseAnimation.COLLAPSE, lastOpen, lastOpenPosition);
}
openItems.set(lastOpenPosition, false);
}
lastOpen = target;
lastOpenPosition = position;
}
else if (lastOpenPosition == position)
{
lastOpenPosition = -1;
}
// animateView(target, type);
// Expand the view which was collapse
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
target.startAnimation(anim);
this.notifiyExpandCollapseListener(type, target, position);
// }
}
private void enableFor(final View button, final View upperView, final View target, final int position)
{
// lastopenUpperViewTemporary = upperView;
if (target == lastOpen && position != lastOpenPosition)
{
// lastOpen is recycled, so its reference is false
lastOpen = null;
}
if (position == lastOpenPosition)
{
// re reference to the last view
// so when can animate it when collapsed
// lastOpen = target;
lastOpen = target;
lastOpnUpperView = upperView;
}
int height = viewHeights.get(position, -1);
if (height == -1)
{
viewHeights.put(position, target.getMeasuredHeight());
updateExpandable(target, position);
}
else
{
updateExpandable(target, position);
}
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View view)
{
System.out.println("Position: " + position);
if (lastOpenPosition == position)
{
return;
}
System.out.println("Upper View: " + upperView);
Animation anim = new ExpandCollapseUpperViewAnimation(upperViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
anim.setDuration(800);
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
circleViewsList.get(position).setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
// TODO Auto-generated method stub
// upperViewsList.get(position).setVisibility(View.VISIBLE);
animateListExpand(button, target, position);
}
});
upperViewsList.get(position).startAnimation(anim);
// Lower animation
Animation lowerAnim = new ExpandCollapseUpperViewAnimation(lowerViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
lowerAnim.setDuration(800);
lowerViewsList.get(position).startAnimation(lowerAnim);
}
});
}
private void updateExpandable(View target, int position)
{
final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) target.getLayoutParams();
if (openItems.get(position))
{
target.setVisibility(View.VISIBLE);
params.bottomMargin = 0;
}
else
{
target.setVisibility(View.GONE);
params.bottomMargin = 0 - viewHeights.get(position);
}
}
/**
* Performs either COLLAPSE or EXPAND animation on the target view
*
* #param target
* the view to animate
* #param type
* the animation type, either ExpandCollapseAnimation.COLLAPSE or ExpandCollapseAnimation.EXPAND
*/
private void animateView(final View target, final int type)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
System.out.println("Animation End");
if (type == ExpandCollapseAnimation.EXPAND)
{
if (parent instanceof ListView)
{
ListView listView = (ListView) parent;
int movement = target.getBottom();
Rect r = new Rect();
boolean visible = target.getGlobalVisibleRect(r);
Rect r2 = new Rect();
listView.getGlobalVisibleRect(r2);
if (!visible)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
else
{
if (r2.bottom == r.bottom)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
}
}
}
}
});
target.startAnimation(anim);
}
private void animateWithUpperView(final View target, final int type, final int position)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
// upperViewsList.get(lastOpenItemForUpperView).setVisibility(View.VISIBLE);
// lastOpenItemForUpperView = position;
Animation expandItemAniamtion = new ExpandCollapseLowerViewAnimation(upperViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
expandItemAniamtion.setDuration(800);
expandItemAniamtion.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
circleViewsList.get(lastOpenItemIndex).setVisibility(View.VISIBLE);
lastOpenItemIndex = position;
}
});
// Lower view animation
Animation lowerAnim = new ExpandCollapseLowerViewAnimation(lowerViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
lowerAnim.setDuration(800);
upperViewsList.get(lastOpenItemIndex).startAnimation(expandItemAniamtion);
lowerViewsList.get(lastOpenItemIndex).startAnimation(lowerAnim);
}
});
target.startAnimation(anim);
}
/**
* Closes the current open item. If it is current visible it will be closed with an animation.
*
* #return true if an item was closed, false otherwise
*/
public boolean collapseLastOpen()
{
if (isAnyItemExpanded())
{
// if visible animate it out
if (lastOpen != null)
{
animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
}
openItems.set(lastOpenPosition, false);
lastOpenPosition = -1;
return true;
}
return false;
}
public Parcelable onSaveInstanceState(Parcelable parcelable)
{
SavedState ss = new SavedState(parcelable);
ss.lastOpenPosition = this.lastOpenPosition;
ss.openItems = this.openItems;
return ss;
}
public void onRestoreInstanceState(SavedState state)
{
if (state != null)
{
this.lastOpenPosition = state.lastOpenPosition;
this.openItems = state.openItems;
}
}
/**
* Utility methods to read and write a bitset from and to a Parcel
*/
private static BitSet readBitSet(Parcel src)
{
BitSet set = new BitSet();
if (src == null)
{
return set;
}
int cardinality = src.readInt();
for (int i = 0; i < cardinality; i++)
{
set.set(src.readInt());
}
return set;
}
private static void writeBitSet(Parcel dest, BitSet set)
{
int nextSetBit = -1;
if (dest == null || set == null)
{
return; // at least dont crash
}
dest.writeInt(set.cardinality());
while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
{
dest.writeInt(nextSetBit);
}
}
/**
* The actual state class
*/
static class SavedState extends View.BaseSavedState
{
public BitSet openItems = null;
public int lastOpenPosition = -1;
SavedState(Parcelable superState)
{
super(superState);
}
private SavedState(Parcel in)
{
super(in);
lastOpenPosition = in.readInt();
openItems = readBitSet(in);
}
#Override
public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeInt(lastOpenPosition);
writeBitSet(out, openItems);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
{
public SavedState createFromParcel(Parcel in)
{
return new SavedState(in);
}
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
public static Animation ExpandOrCollapseView(final View v, final boolean expand)
{
try
{
Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
m.setAccessible(true);
m.invoke(v, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(((View) v.getParent()).getMeasuredWidth(), MeasureSpec.AT_MOST));
}
catch (Exception e)
{
e.printStackTrace();
}
final int initialHeight = v.getMeasuredHeight();
if (expand)
{
v.getLayoutParams().height = 0;
}
else
{
v.getLayoutParams().height = initialHeight;
}
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
int newHeight = 0;
if (expand)
{
newHeight = (int) (initialHeight * interpolatedTime);
}
else
{
newHeight = (int) (initialHeight * (1 - interpolatedTime));
}
v.getLayoutParams().height = newHeight;
v.requestLayout();
if (interpolatedTime == 1 && !expand)
v.setVisibility(View.GONE);
}
#Override
public boolean willChangeBounds()
{
return true;
}
};
a.setDuration(1000);
return a;
}}
Luckily I have found the solution. On pre-kitkat the upperView on list item which was gone does not forcelly animate but on kitkat and so on it forcelly animate from gone to visible.
So as the solution we must give a layer of view between of list item and upper view (Which we have to animate).
In this way it will work like charm.

Android ViewPager when swiping on last screen

Im having trouble figuring out how to capture a swipe event on the last page of a view pager.
Basically the requirement is that when the user is on the last page, and they try to swipe to get to the next page, the activity should close.
I've tried doing this onPageScrolled but I cant seem to differentiate a left or right swipe when in there. When on the last page, the user should still maintain the functionality to move to the previous page.
So basically the case is
When on last page
if swipe prev (do normal behaviour)
if swipe next (finish activity)
Can anyone provide any suggestions?
Thanks
Here is working solution!!
Required variables
....
private boolean isLastPageSwiped;
private int counterPageScroll;
....
Inside onPageScrolled of ViewPager
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Here 6 is last position
if (position == 6 && positionOffset == 0 && !isLastPageSwiped){
if(counterPageScroll != 0){
isLastPageSwiped=true;
//Next Activity here
}
counterPageScroll++;
}else{
counterPageScroll=0;
}
}
You should include an fake blank page to your ViewPager, when user swipes your actual last page, it comes to this fake one. When this fake one is visible finish the Activity.
You should try this way, if it does not work, I can give you an example code.
I am probably really late, but then this might help someone else down the line. I got the snippet from a question on stackoverflow, where in i am just tweaking the code a little bit.
Answer from here.
ViewPager.OnPageChangeListener pagerListener = new ViewPager.OnPageChangeListener() {
boolean lastPageChange = false;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
int lastIdx = mPgScreenAdapter.getCount() - 1;
Log.d(TAG, "pos:" + position);
if(lastPageChange && position == lastIdx) {}
}
#Override
public void onPageSelected( int i) {
pgText.setCurrentItem(i/2);
}
#Override
public void onPageScrollStateChanged(int state) {
int lastIdx = mPgScreenAdapter.getCount() - 1;
int curItem = pgScreen.getCurrentItem();
if(curItem==lastIdx && state==1){
lastPageChange = true;
// i put this here since onPageScroll gets called a couple of times.
finish();
}else {
lastPageChange = false;
}
}
};
int lastPosition;
int currentPosition;
//View pager assign
ViewPager viewPager = findViewById(R.id.viewPager);
//PageIndicatorView external https://github.com/romandanylyk
PageIndicatorView pageIndicatorView = findViewById(R.id.pageIndicatorView);
//customise pageIndicatorView design
pageIndicatorView.setAnimationType(AnimationType.THIN_WORM);
pageIndicatorView.setViewPager(viewPager);
//BannerAdapter is an custom adapter for viewpage i made.
//assign data here
BannerAdapter pagerAdapter = new BannerAdapter(this, banner, new OnClickListener() {
#Override
public void onClick() {
}
});
viewPager.setAdapter(pagerAdapter);
//listener for View Pager
//this is where process of making the infinite loop
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//assign position so that can be manipulated
currentPosition = position;
}
#Override
public void onPageSelected(int position) {
//set pager indicator position here
pageIndicatorView.setSelection(position);
}
#Override
public void onPageScrollStateChanged(int state) {
//check state 0 mean no ongoing process
if(state == 0){
//check currentPosition == lastPosition to detect that scroll cant scroll anymore
if(currentPosition == lastPosition){
int lastitem = banner.size() - 1;
//check position if either last item/first item
//and set viewpager current page as follow
if(currentPosition == lastitem){
viewPager.setCurrentItem(0);
}else if(currentPosition == 0){
viewPager.setCurrentItem(lastitem);
}
}else{
lastPosition = currentPosition;
}
Log.d("ScrollState", "currentPosition: " + currentPosition);
}
/*empty*/
}
});
I also tried my luck to find solution. Here is working code.Hope it will help someone else:-
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int curr = pager.getCurrentItem();
int lastReal = pager.getAdapter().getCount() - 1;
if (curr < lastReal) {
counter = 0;
} else if (curr == lastReal) {
counter++;
if (counter == 2) {
pager.setOffscreenPageLimit(0);
pager.setCurrentItem(0, false);
counter = 0;
}
}
}
}
});
pagerIndicator.setViewPager(pager);
I used counter as global variable.
Thanks!
I created an abstract listener based on the solutions here. I wanted to do something when swiping right on the last page, or left on the first page.
import android.support.v4.view.ViewPager;
abstract class PageChangeToUnavailablePageListener implements ViewPager.OnPageChangeListener {
private int finalPageIndex;
private boolean finalPageVisible = false;
private boolean firstPageVisible = true;
private int lastScrollState = 0;
abstract void onUnavailablePageChangeOnLastPage();
abstract void onUnavailablePageChangeOnFirstPage();
public PageChangeToUnavailablePageListener(int finalPageIndex) {
this.finalPageIndex = finalPageIndex;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int i) {
firstPageVisible = (i == 0);
finalPageVisible = (i == finalPageIndex);
}
#Override
public void onPageScrollStateChanged(int state) {
// The normal state sequence for changing pages is
// DRAGGING (user starts drag), SETTLING (next page is chosen), IDLE (animation is complete).
// No next page is chosen when dragging right on last page, or left on first page.
// In this case the SETTLING state is missing and the sequence is (DRAGGING, IDLE).
if (lastScrollState == ViewPager.SCROLL_STATE_DRAGGING && state == ViewPager.SCROLL_STATE_IDLE) {
// The user scrolled to an unavailable page.
if (finalPageVisible) {
onUnavailablePageChangeOnLastPage();
} else if (firstPageVisible) {
onUnavailablePageChangeOnFirstPage();
}
}
lastScrollState = state;
}
}
Hope you got an answer, i came across the same issue and this is what i did:
Incase you are using a viewpager and an activity with a class extending FragmentStatePagerAdapter you can use the getItem(int position) method to check if you reached the last page. This is also if you declared the NUM_PAGES and you can use it to detect the user has reached the last page.
in Activity:
private static int MAX_LAYOUTS = 3;
private boolean isLastPageSwiped;
private int counterPageScroll;
In onCreate:
ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == MAX_LAYOUTS - 1 && positionOffset == 0 && !isLastPageSwiped) {
if (counterPageScroll != 0) {
isLastPageSwiped = true;
Intent intent = new Intent(OldActivity.this, NewActivity.class);
startActivity(intent);
}
counterPageScroll++;
} else {
counterPageScroll = 0;
}
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
};
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
i have also solution for these(in kotlin),
Example my tabview has 3 pages.
......
private var isLastPageSwiped = false
private var indexPageScroll = 0
......
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
if (position == 2 && positionOffset == 0f && !isLastPageSwiped) {
if (indexPageScroll != 0) {
isLastPageSwiped = true
//Next Activity here
//You can perform your action on last page scroll.
}
indexPageScroll++
} else {
indexPageScroll = 0
}
}

SwipeListview openanimate(postion) method not working

I am working with swipelistview in android where i need to implement onClick in a listview to swipe out the list item. I used openanimate method in OnclickfrontView. But my output result is reverse. I am getting view from right to left instead of left to right. The library which i used is https://github.com/47deg/android-swipelistview. The xml layout for swipelistview is
android:id="#+id/search_screen_listview_sharedData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:layout_marginTop="0dp"
android:cacheColorHint="#00000000"
android:background="#color/white"
android:divider="#color/sliding_menu_listview_divider"
android:dividerHeight="0.2dp"
android:fadingEdgeLength="0dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:listSelector="#color/transparent"
swipe:swipeFrontView="#+id/front"
swipe:swipeBackView="#+id/back"
swipe:swipeOpenOnLongPress="false"
swipe:swipeActionLeft="reveal"
swipe:swipeActionRight="reveal"
swipe:swipeCloseAllItemsWhenMoveList="true"
swipe:swipeDrawableChecked="#color/sliding_menu_selection_blue"
swipe:swipeDrawableUnchecked="#color/white"
swipe:swipeOffsetLeft="200dp"
swipe:swipeOffsetRight="70dp"
swipe:swipeMode="right" />
The sample code for your reference is
swipeListView = (SwipeListView) view
.findViewById(R.id.listview_sharedData);
swipeFrontView = (View) view.findViewById(R.id.front);
if (Build.VERSION.SDK_INT >= 11) {
swipeListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
swipeListView
.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
#Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
mode.setTitle("Selected ("
+ swipeListView.getCountSelected() + ")");
}
#Override
public void onDestroyActionMode(ActionMode mode) {
swipeListView.unselectedChoiceStates();
}
#Override
public boolean onActionItemClicked(ActionMode mode,
android.view.MenuItem item) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onCreateActionMode(ActionMode mode,
android.view.Menu menu) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode,
android.view.Menu menu) {
// TODO Auto-generated method stub
return false;
}
});
}
swipeListView.setSwipeListViewListener(new BaseSwipeListViewListener() {
#Override
public void onOpened(int position, boolean toRight) {
}
#Override
public void onClosed(int position, boolean fromRight) {
}
#Override
public void onListChanged() {
}
#Override
public void onMove(int position, float x) {
}
#Override
public void onStartOpen(int position, int action, boolean right) {
Log.d("swipe", String.format("onStartOpen %d - action %d",
position, action));
}
#Override
public void onStartClose(int position, boolean right) {
Log.d("swipe", String.format("onStartClose %d", position));
}
#Override
public void onClickFrontView(int position) {
Log.d("swipe", String.format("onClickFrontView %d", position));
swipeListView.openAnimate(position);
}
#Override
public void onClickBackView(int position) {
Log.d("swipe", String.format("onClickBackView %d", position));
//swipeListView.closeAnimate(position);
}
#Override
public void onDismiss(int[] reverseSortedPositions) {
/*
* for (int position : reverseSortedPositions) {
* data.remove(position); } adapter.notifyDataSetChanged();
*/
}
});
if (Constant.List != null) {
if (Constant.List.size() > 0) {
swipeListView.setAdapter(new CustomContentListAdapter(
getActivity(), Constant.List, true));
}
}
return view;
}
Please help me how to achieve left to right swipview while i click the listview item. I am struggling with this for a week but still didnt get good solution for it.
public void openAnimate(int position) {
touchListener.openAnimate(position);
}
protected void openAnimate(int position) {
openAnimate(
swipeListView.getChildAt(
position - swipeListView.getFirstVisiblePosition())
.findViewById(swipeFrontView), position);
}
private void openAnimate(View view, int position) {
if (!opened.get(position)) {
generateRevealAnimate(view, true, false, position);
}
}
private void generateRevealAnimate(final View view, final boolean swap,
final boolean swapRight, final int position) {
int moveTo = 0;
if (opened.get(position)) {
if (!swap) {
moveTo = openedRight.get(position) ? (int) (viewWidth - rightOffset)
: (int) (-viewWidth + leftOffset);
}
} else {
if (swap) {
moveTo = swapRight ? (int) (viewWidth - rightOffset)
: (int) (-viewWidth + leftOffset);
}
}
animate(view).translationX(moveTo).setDuration(animationTime)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
swipeListView.resetScrolling();
if (swap) {
boolean aux = !opened.get(position);
opened.set(position, aux);
if (aux) {
swipeListView.onOpened(position, swapRight);
openedRight.set(position, swapRight);
} else {
swipeListView.onClosed(position,
openedRight.get(position));
}
}
resetCell();
}
});
}
private void resetCell() {
if (downPosition != ListView.INVALID_POSITION) {
if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) {
backView.setVisibility(View.VISIBLE);
}
frontView.setClickable(opened.get(downPosition));
frontView.setLongClickable(opened.get(downPosition));
frontView = null;
backView = null;
downPosition = ListView.INVALID_POSITION;
}
}
Edit the openAnimate() method in the source code of the library you downloaded to make it move the other way (assuming the source code is included)
Edit:
Below is the code that determines where to move the view to
int moveTo = 0;
if (opened.get(position)) {
if (!swap) {
moveTo = openedRight.get(position) ? (int) (viewWidth - rightOffset)
: (int) (-viewWidth + leftOffset);
}
} else {
if (swap) {
moveTo = swapRight ? (int) (viewWidth - rightOffset)
: (int) (-viewWidth + leftOffset);
}
}
Because i dont have the full code i dont know what viewWidth or rightOffset and leftOffset are defined as.
I suggest you try something like this:
: (int) (-viewWidth + leftOffset);
to
: (int) (viewWidth - leftOffset);
And do that for both cases. I cant guarantee that will work but try playing around with those values if it doesnt.
You need add some code for:
SwipeListView.java
public void openAnimateRight(int position) {
touchListener.openAnimateRight(position);
}
public void closeAnimateRight(int position) {
touchListener.closeAnimateRight(position);
}
SwipeListViewTouchListener.java
protected void openAnimateRight(int position) {
openAnimateRight(swipeListView.getChildAt(position - swipeListView.getFirstVisiblePosition()).findViewById(swipeFrontView), position);
}
protected void closeAnimateRight(int position) {
closeAnimateRight(swipeListView.getChildAt(position - swipeListView.getFirstVisiblePosition()).findViewById(swipeFrontView), position);
}
private void openAnimateRight(View view, int position) {
if (!opened.get(position)) {
generateRevealAnimate(view, true, true, position);
}
}
private void closeAnimateRight(View view, int position) {
if (opened.get(position)) {
generateRevealAnimate(view, true, true, position);
}
}
and then you can call:
#Override
public void onClickFrontView(int position) {
Log.d("swipe", String.format("onClickFrontView %d", position));
swipe_list_view.openAnimateRight(position);
}
This working for me :)
The same way like in OnclickBackView you have to write swipeListView.closeanimate(postion);
On searching the repositry you provided. the library itself providing the support for swipetoright mode.
public final static int SWIPE_MODE_RIGHT = 2;
https://github.com/47deg/android-swipelistview/blob/20dbffdf3417a7ad0cbba06cc6f1caa7ffb3808d/swipelistview/src/com/fortysevendeg/swipelistview/SwipeListView.java
try that option to do your task...
All the best..

Synchronizing two ViewPagers using OnPageChangeListener

I'm trying to synchronize two ViewPagers, as apparently have quite a lot of people before me, and I've got as far as this:
private ViewPager mNavPager;
private ViewPager mMainPager;
private final OnPageChangeListener mNavPagerListener = new OnPageChangeListener() {
private boolean mNavDragging;
private int mScrollPosition;
#Override
public void onPageSelected(int position) {
mScrollPosition = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(mNavDragging)
mMainPager.scrollTo(positionOffsetPixels, 0);
}
#Override
public void onPageScrollStateChanged(int state) {
switch(state) {
case ViewPager.SCROLL_STATE_DRAGGING:
case ViewPager.SCROLL_STATE_SETTLING:
mNavDragging = true;
break;
case ViewPager.SCROLL_STATE_IDLE:
mNavDragging = false;
break;
}
}
};
private OnPageChangeListener mMainPagerListener = new OnPageChangeListener() {
private boolean mMainDragging;
private int mScrollPosition;
#Override
public void onPageSelected(int position) {
mScrollPosition = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(mMainDragging)
mNavPager.scrollTo(positionOffsetPixels, 0);
}
#Override
public void onPageScrollStateChanged(int state) {
switch(state) {
case ViewPager.SCROLL_STATE_DRAGGING:
case ViewPager.SCROLL_STATE_SETTLING:
mMainDragging = true;
break;
case ViewPager.SCROLL_STATE_IDLE:
mMainDragging = false;
break;
}
}
};
If either one is scrolled manually, the other is slaved to it using the scroll state property. It works beautifully till the items reach their final position; at this point, the slaved pager flicks instantly back to the previously selected item, as though the scrolling hadn't taken place.
I have tried calling ViewPager#setCurrentItem(mScrolledPosition) with a variety of different logic constraints but that doesn't work either, though it does occasionally make it worse. I feel as though there must be something that can be done with that but I'm not sure what.
How can I get the slaved pager to remain in the correct position?
I solved this problem in a much easier (and cleaner) way using the OnPageChangeListener:
mViewPager1.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private int mScrollState = ViewPager.SCROLL_STATE_IDLE;
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
return;
}
mViewPager2.scrollTo(mViewPager1.getScrollX(), mViewPager2.getScrollY());
}
#Override
public void onPageSelected(final int position) {
}
#Override
public void onPageScrollStateChanged(final int state) {
mScrollState = state;
if (state == ViewPager.SCROLL_STATE_IDLE) {
mViewPager2.setCurrentItem(mViewPager1.getCurrentItem(), false);
}
}
});
mViewPager2.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private int mScrollState = ViewPager.SCROLL_STATE_IDLE;
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
return;
}
mViewPager1.scrollTo(mViewPager2.getScrollX(), mViewPager1.getScrollY());
}
#Override
public void onPageSelected(final int position) {
}
#Override
public void onPageScrollStateChanged(final int state) {
mScrollState = state;
if (state == ViewPager.SCROLL_STATE_IDLE) {
mViewPager1.setCurrentItem(mViewPager2.getCurrentItem(), false);
}
}
});
I have Solved the problem without utilizing the Listener.
So you can use the listener for some other stuff and will make code look cleaner.
I know its a question asked a long ago. I was searching for a solution and solved it myself.
This is how my Custom ViewPager code is
public class CustomPager extends ViewPager {
CustomPager mCustomPager;
private boolean forSuper;
public CustomPager(Context context) {
super(context);
}
public CustomPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
if (!forSuper) {
mCustomPager.forSuper(true);
mCustomPager.onInterceptTouchEvent(arg0);
mCustomPager.forSuper(false);
}
return super.onInterceptTouchEvent(arg0);
}
#Override
public boolean onTouchEvent(MotionEvent arg0) {
if (!forSuper) {
mCustomPager.forSuper(true);
mCustomPager.onTouchEvent(arg0);
mCustomPager.forSuper(false);
}
return super.onTouchEvent(arg0);
}
public void setViewPager(CustomPager customPager) {
mCustomPager = customPager;
}
public void forSuper(boolean forSuper) {
this.forSuper = forSuper;
}
#Override
public void setCurrentItem(int item, boolean smoothScroll) {
if (!forSuper) {
mCustomPager.forSuper(true);
mCustomPager.setCurrentItem(item, smoothScroll);
mCustomPager.forSuper(false);
}
super.setCurrentItem(item, smoothScroll);
}
#Override
public void setCurrentItem(int item) {
if (!forSuper) {
mCustomPager.forSuper(true);
mCustomPager.setCurrentItem(item);
mCustomPager.forSuper(false);
}
super.setCurrentItem(item);
}
}
And you have to set pagers like this before adapter is set and just after getting reference using the ID.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
backPager=(CustomPager) findViewById(R.id.BackPager);
frontPager = (CustomPager) findViewById(R.id.frontPager);
frontPager.setViewPager(backPager);
backPager.setViewPager(frontPager);
backPager.setAdapter(your Adapter);
frontPager.setAdapter(your Adapter);
}
Must set ViewPagers before assigning adapter.
I had a similar issue, also needed to combine my syncing with animations in PageTransformer.
Originally posted this answer here: https://stackoverflow.com/a/43638796/2867886
But I will paste it here for convenience.
The solution that worked best for me was to pass MotionEvent in OnTouchListener between ViewPager instances. Tried fake dragging but it was always laggy and buggy - I needed a smooth, parallax-like effect.
So, my advice is to implement a View.OnTouchListener. The MotionEvent has to be scaled to compensate for the difference in width.
public class SyncScrollOnTouchListener implements View.OnTouchListener {
private final View syncedView;
public SyncScrollOnTouchListener(#NonNull View syncedView) {
this.syncedView = syncedView;
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
MotionEvent syncEvent = MotionEvent.obtain(motionEvent);
float width1 = view.getWidth();
float width2 = syncedView.getWidth();
//sync motion of two view pagers by simulating a touch event
//offset by its X position, and scaled by width ratio
syncEvent.setLocation(syncedView.getX() + motionEvent.getX() * width2 / width1,
motionEvent.getY());
syncedView.onTouchEvent(syncEvent);
return false;
}
}
Then set it to your ViewPager
sourcePager.setOnTouchListener(new SyncScrollOnTouchListener(targetPager));
Note that this solution will only work if both pagers have the same orientation. If you need it to work for different orientations - adjust syncEvent Y coordinate instead of X.
There is one more issue that we need to take into account - minimum fling speed and distance that can cause just one pager to change page.
It can be easily fixed by adding an OnPageChangeListener to our pager
sourcePager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
//no-op
}
#Override
public void onPageSelected(int position) {
targetPager.setCurrentItem(position, true);
}
#Override
public void onPageScrollStateChanged(int state) {
//no-op
}
});
This does everything right except it sometimes misses very quick flicks on the slaved view. For some reason including fake drag events during the settling phase causes real problems, though.
private ViewPager mNavPager;
private ViewPager mMainPager;
private final OnPageChangeListener mNavPagerListener = new OnPageChangeListener() {
private int mLastScrollPosition;
private int mLastPagePosition;
#Override
public void onPageSelected(int position) {
mLastPagePosition = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(mMainPager.isFakeDragging()) {
int absoluteOffsetPixels = positionOffsetPixels;
if(mLastPagePosition!=position) {
absoluteOffsetPixels += (position - mLastPagePosition) * mMainPager.getWidth();
mLastPagePosition = position;
}
Log.d(TAG, "fake nav drag by " + (mLastScrollPosition - absoluteOffsetPixels));
mMainPager.fakeDragBy(mLastScrollPosition - absoluteOffsetPixels);
mLastScrollPosition = positionOffsetPixels;
}
}
#Override
public void onPageScrollStateChanged(int state) {
if(!mNavPager.isFakeDragging()) {
switch(state) {
case ViewPager.SCROLL_STATE_DRAGGING:
if(!mMainPager.isFakeDragging())
mMainPager.beginFakeDrag();
break;
case ViewPager.SCROLL_STATE_SETTLING:
case ViewPager.SCROLL_STATE_IDLE:
if(mMainPager.isFakeDragging()) {
mMainPager.endFakeDrag();
mLastScrollPosition = 0;
}
break;
}
}
}
};
private OnPageChangeListener mMainPagerListener = new OnPageChangeListener() {
private int mLastScrollPosition;
private int mLastPagePosition;
#Override
public void onPageSelected(int position) {
mLastPagePosition = position;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(mNavPager.isFakeDragging()) {
int absoluteOffsetPixels = positionOffsetPixels;
if(mLastPagePosition!=position) {
absoluteOffsetPixels += (position - mLastPagePosition) * mMainPager.getWidth();
mLastPagePosition = position;
}
Log.d(TAG, "fake nav drag by " + (mLastScrollPosition - absoluteOffsetPixels));
mNavPager.fakeDragBy(mLastScrollPosition - absoluteOffsetPixels);
mLastScrollPosition = positionOffsetPixels;
}
}
#Override
public void onPageScrollStateChanged(int state) {
if(!mMainPager.isFakeDragging()) {
switch(state) {
case ViewPager.SCROLL_STATE_DRAGGING:
if(!mNavPager.isFakeDragging())
mNavPager.beginFakeDrag();
break;
case ViewPager.SCROLL_STATE_SETTLING:
case ViewPager.SCROLL_STATE_IDLE:
if(mNavPager.isFakeDragging()) {
mNavPager.endFakeDrag();
mLastScrollPosition = 0;
}
break;
}
}
}
};
EDIT
I now believe this to be impossible without some fairly substantial custom code. The reason is essentially that both ViewPagers have a VelocityTracker inside, which controls scrolling. Since the MotionEvents being passed in may not be passed to the VelocityTracker at the same relative times for each pager, the trackers will occasionally reach different conclusions about how to react.
However, it is possible to use a modified PagerTitleStrip to get precise tracking of a ViewPager, and to transfer touch events captured by the strip directly to the ViewPager.
The source for PagerTitleStrip is here.
Broadly, what needs to be done to make this work is as follows: replace mPrevText, mCurrText and mNextText with views of the type you want to use; remove the onAttachedToWindow() and onDetachedFromWindow() functions; remove calls to the PagerAdapter that deal with dataset observers, and add an OnTouchListener to the modified strip that fake drags the main pager. You'll also need to add the modified title strip as an OnPageChangeListener to the ViewPager since the internal listeners aren't visible outside the package.
Is this a gigantic pain? Yes. But it works. I will write it up in more detail soon.
With help from all other answers I've created a Class so it easily can be implemented.
public class OnPageChangeListernerSync implements ViewPager.OnPageChangeListener {
private ViewPager master;
private ViewPager slave;
private int mScrollState = ViewPager.SCROLL_STATE_IDLE;
public OnPageChangeListernerSync(ViewPager master, ViewPager slave){
this.master = master;
this.slave = slave;
}
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
return;
}
this.slave.scrollTo(this.master.getScrollX()*
this.slave.getWidth()/
this.master.getWidth(), 0);
}
#Override
public void onPageSelected(final int position) {
}
#Override
public void onPageScrollStateChanged(final int state) {
mScrollState = state;
if (state == ViewPager.SCROLL_STATE_IDLE) {
this.slave.setCurrentItem(this.master
.getCurrentItem(), false);
}
}
}
...
// In your activity
this.upperPager.addOnPageChangeListener(new OnPageChangeListernerSync(this.upperPager, this.lowerPager));
this.lowerPager.addOnPageChangeListener(new OnPageChangeListernerSync(this.lowerPager, this.upperPager));
All the Answers are approximately currect in some situation. Here I am giving one more answer which will use to slide both the ViewPagers simultaneously whether there size is same or not:
viewPagerBanner.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private int scrollState = ViewPager.SCROLL_STATE_IDLE;
// Indicates that the pager is in an idle, settled state.
// The current page is fully in view and no animation is in progress.
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if (scrollState == ViewPager.SCROLL_STATE_IDLE) {
return;
}
viewPagerTitle.scrollTo(viewPagerBanner.getScrollX()*
viewPagerTitle.getWidth()/
viewPagerBanner.getWidth(), 0);
// We are not interested in Y axis position
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {
scrollState = state;
if (state == ViewPager.SCROLL_STATE_IDLE) {
viewPagerTitle.setCurrentItem(viewPagerBanner.getCurrentItem(), false);
}
}
});

How to know viewpager is scroll left or right?

I am using ViewPager (support library). I want to know every time the ViewPager change the visible page, it is scrolling left or right.
Please give me a solution. Any recommend is welcome also.
Thanks
set setOnPageChangeListener to your ViewPager
keep a variable global as
private int lastPosition = 0;
and in
#Override
public void onPageSelected(int arg0) {
if (lastPosition > position) {
System.out.println("Left");
}else if (lastPosition < position) {
System.out.println("Right");
}
lastPosition = position;
}
It's not a perfect solution but here's a way to check the swipe direction when you start swiping:
new ViewPager.OnPageChangeListener() {
private static final float thresholdOffset = 0.5f;
private boolean scrollStarted, checkDirection;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (checkDirection) {
if (thresholdOffset > positionOffset) {
Log.i(C.TAG, "going left");
} else {
Log.i(C.TAG, "going right");
}
checkDirection = false;
}
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {
if (!scrollStarted && state == ViewPager.SCROLL_STATE_DRAGGING) {
scrollStarted = true;
checkDirection = true;
} else {
scrollStarted = false;
}
}
});
EDIT: there's a more elegant approach that involves using a ViewPager.PageTransformer and checking it's position intervals:
...
myViewPager.setPageTransformer(true, new PageTransformer());
...
public class PageTransformer implements ViewPager.PageTransformer {
public void transformPage(View view, float position) {
if (position < -1) {
// [-00,-1): the page is way off-screen to the left.
} else if (position <= 1) {
// [-1,1]: the page is "centered"
} else {
// (1,+00]: the page is way off-screen to the right.
}
}
}
You can learn more from: Using ViewPager for Screen Slides
This is my simple solution in the onPageScrolled() method of ViewPager.OnPageChangeListener:
Same solution as GuilhE with a minor fix to avoid getting false positives when paging left (swiping right) on the first page(no more pages to the left) in the ViewPager.
It simply does an additional check to see if the swipe has actually moved at all.
new ViewPager.OnPageChangeListener() {
private static final float thresholdOffset = 0.5f;
private static final int thresholdOffsetPixels = 1;
private boolean scrollStarted, checkDirection;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (checkDirection) {
if (thresholdOffset > positionOffset && positionOffsetPixels > thresholdOffsetPixels) {
Log.i(C.TAG, "going left");
} else {
Log.i(C.TAG, "going right");
}
checkDirection = false;
}
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {
if (!scrollStarted && state == ViewPager.SCROLL_STATE_DRAGGING) {
scrollStarted = true;
checkDirection = true;
} else {
scrollStarted = false;
}
}
});
You can keep class member variable to save last visited page
private int mLastVisitedPageIndex = 0;
Then use following function to check direction
#Override
public void onPageSelected(int i) {
boolean isMovingForward = mLastVisitedPageIndex < i?true:false;
//Use isMovingForward variable anywhere now
mLastVisitedPageIndex = i;
}
use that
#Override
public void onPageSelected( int position )
{
mCurrentFragmentPosition = position;
}
#Override
public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels )
{
boolean isGoingToRightPage = position == mCurrentFragmentPosition;
if(isGoingToRightPage)
{
// user is going to the right page
}
else
{
// user is going to the left page
}
}
Use the ViewPager.OnPageChangeListener interface. You can use the position argument passed to onPageSelected and compare it to the previous value to figure out which way the ViewPager was scrolled.
private float sumPositionAndPositionOffset;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
boolean isSwipeToLeft = position + positionOffset > sumPositionAndPositionOffset;
sumPositionAndPositionOffset = position + positionOffset;
}
I solved the issue with this implementation. Hope it helps.
public static final float EPSILON= 0.001f;
#Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
// initial position (positionOffset == 0)
if (positionOffset < EPSILON) {
mIsRight = positionOffset < 0.5;
return;
}
// code here
if (mIsRight) {
} else {
}
}
We can also do this using a Custom Viewpager, which can contain swipeLeft() and swipeRight() methods and its onTouchEvent(MotionEvent event) method can contain ACTION_MOVE and ACTION_CANCEL case.
// This can be the code if helpful.
public class SwiperViewPager extends ViewPager {
SwiperListener mSwiperListener;
private float downX;
private float downY;
private boolean isTouchCaptured;
private float upX1;
private float upY1;
private float upX2;
private float upY2;
public SwiperViewPager(Context context) {
super(context);
}
public SwiperViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
private float x1, x2;
static final int min_distance = 20;
boolean eventSent = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
downX = event.getX();
downY = event.getY();
if (!isTouchCaptured) {
upX1 = event.getX();
upY1 = event.getY();
isTouchCaptured = true;
} else {
upX2 = event.getX();
upY2 = event.getY();
float deltaX = upX1 - upX2;
float deltaY = upY1 - upY2;
//HORIZONTAL SCROLL
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (Math.abs(deltaX) > min_distance) {
// left or right
if (deltaX < 0) {
if(!eventSent && mSwiperListener!=null){
mSwiperListener.onLeftSwipe();
eventSent = true;
}
}
if (deltaX > 0) {
if(!eventSent && mSwiperListener!=null){
if(mSwiperListener.onRightSwipe()){
eventSent = true;
return false;
}
}
}
} else {
//not long enough swipe...
}
}
//VERTICAL SCROLL
else {
if (Math.abs(deltaY) > min_distance) {
// top or down
if (deltaY < 0) {
}
if (deltaY > 0) {
}
} else {
//not long enough swipe...
}
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:{
isTouchCaptured = false;
eventSent = false;
}
}
return super.onTouchEvent(event);
}
public void setmSwiperListener(SwiperListener mSwiperListener) {
this.mSwiperListener = mSwiperListener;
}
public static interface SwiperListener {
public boolean onLeftSwipe();
public boolean onRightSwipe();
}
}
Apologies - had to edit the answer as I found a bug.
Here is improved solution:
The solution compares current page index with one previously selected (previousPageIndex)
newPageIndex represents the page which is about to be scrolled to.
Condition (positionOffset == 0) compares if the scroll finished
private int previousPageIndex = 0;
private int newPageIndex = -1;
private final int MOVE_DIRECTION_NONE = 0;
private final int MOVE_DIRECTION_LEFT = 1;
private final int MOVE_DIRECTION_RIGHT = 2;
private int moveDirection = MOVE_DIRECTION_NONE;
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
if (moveDirection == MOVE_DIRECTION_NONE) {
if (previousPageIndex == position){
moveDirection = MOVE_DIRECTION_LEFT;
if (newPageIndex == -1) newPageIndex = previousPageIndex + 1;
} else {
moveDirection = MOVE_DIRECTION_RIGHT;
if (newPageIndex == -1) newPageIndex = previousPageIndex - 1;
}
}
if (positionOffset == 0) {
System.out.println("Reseting");
previousPageIndex = position;
moveDirection = MOVE_DIRECTION_NONE;
newPageIndex = -1;
}
switch (moveDirection) {
case MOVE_DIRECTION_LEFT:
if (onPageChangingHandler != null) onPageChangingHandler.pageChanging(previousPageIndex, newPageIndex, positionOffset);
System.out.println("Sliding Left | Previous index: " + previousPageIndex + " | New Index: " + newPageIndex + " | offset: " + positionOffset + " | Position: " + position);
break;
case MOVE_DIRECTION_RIGHT:
if (onPageChangingHandler != null) onPageChangingHandler.pageChanging(newPageIndex, previousPageIndex, positionOffset);
System.out.println("Sliding Right | Previous index: " + previousPageIndex + " | New Index: " + newPageIndex + " | offset: " + positionOffset + " | Position: " + position);
break;
case MOVE_DIRECTION_NONE:
System.out.println("Moving NONE");
break;
}
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private int mCurrentSelectedScreen;
private int mNextSelectedScreen;
private static final float thresholdOffset = 0.5f;
private boolean scrollStarted=true, checkDirection=false;
ArrayList<Integer> comp_ary=new ArrayList<Integer>();
#Override
public void onPageSelected(int arg0) {
}
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
//Log.e("positionOffsetPixels : "+positionOffsetPixels, "positionOffset : "+positionOffset);
comp_ary.add(positionOffsetPixels);
if (checkDirection) {
if (comp_ary.get(2) < comp_ary.get(comp_ary.size()-1)) {
Log.e("going left", "going left");
} else
if (comp_ary.get(2) > comp_ary.get(comp_ary.size()-1))
{
Log.e("going right", "going right");
}
checkDirection = false;
comp_ary=new ArrayList<Integer>();
}
}
#Override
public void onPageScrollStateChanged(int arg0) {
if (!scrollStarted && arg0 == ViewPager.SCROLL_STATE_SETTLING) {
scrollStarted = true;
checkDirection = true;
} else {
scrollStarted = false;
}
}
});
dab on this if your worried about position offset suddenly changing to 0 after taking a value > 0.9F
private boolean LEFT_DAB; //left swipe
private float offset; //current position offset
private float take_off; //previous position offset
private static final float quavo = 0.5F; //position offset threshold
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
offset = take_off > 0.9F ? take_off : positionOffset;
take_off = positionOffset;
if (offset > quavo) {
LEFT_DAB = false;//RIGHT_SWIPE
} else {
LEFT_DAB = true;//LEFT_SWIPE
}
}
Here's a way you can know the scroll direction while it's happening. All you have to do is set an OnPageChangeCallback() on the ViewPager. You save the current page of the ViewPager in OnPageSelected() and compare it to the position parameter of OnPageScrolled(). If the current page is less than or equal to the position, you are scrolling to the right, if not, you are scrolling to the left.
var currentPage = 0
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
currentPage = position
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
if (currentPage <= position) {
// We are scrolling right
} else {
// We are scrolling left
}
}
})
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(position == pager.getCurrentItem()){
// Move Right
}
else{
// Move Left
}
}

Categories

Resources