im used this libarary https://github.com/47deg/android-swipelistview
in project im want row only swipe to left and not allow swipe rows
with wiche other and when clicked row not swipeing and open new Activity
public class Aragh extends Activity {
SwipeListView swipelistview;
ItemAdapter adapter;
List<ItemRow> itemData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aragh);
swipelistview=(SwipeListView)findViewById(R.id.example_swipe_lv_list);
itemData=new ArrayList<ItemRow>();
adapter=new ItemAdapter(this,R.layout.custom_row,itemData);
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); //when you touch front view it will open
}
#Override
public void onClickBackView(int position) {
Log.d("swipe", String.format("onClickBackView %d", position));
swipelistview.closeAnimate(position);//when you touch back view it will close
}
#Override
public void onDismiss(int[] reverseSortedPositions) {
}
});
//These are the swipe listview settings. you can change these
//setting as your requirement
swipelistview.setSwipeMode(SwipeListView.SWIPE_MODE_RIGHT); // there are five swiping modes
swipelistview.setSwipeActionLeft(SwipeListView.SWIPE_ACTION_DISMISS); //there are four swipe actions
swipelistview.setSwipeActionRight(SwipeListView.SWIPE_ACTION_DISMISS);
swipelistview.setOffsetLeft(convertDpToPixel(0f)); // left side offset
swipelistview.setOffsetRight(convertDpToPixel(80f)); // right side offset
swipelistview.setAnimationTime(500); // Animation time
swipelistview.setSwipeOpenOnLongPress(false); // enable or disable SwipeOpenOnLongPress
swipelistview.setAdapter(adapter);
for(int i=0;i<10;i++)
{
itemData.add(new ItemRow("Swipe Item"+i,getResources().getDrawable(R.drawable.ic_launcher) ));
}
adapter.notifyDataSetChanged();
}
public int convertDpToPixel(float dp) {
DisplayMetrics metrics = getResources().getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return (int) px;
}
}
I recommend doing it in XML.
in your SwipeListView tag add
swipe:swipeMode="left"
it locks your swipe mode to the left.
you can choose - left / right / both .
You just need to put swipe:swipeMode="left" inside <....SwipeListView /> in your xml layout to allow swiping only in left direction
Related
In my android application i have used viewpager, but i need to change the view of viewpager as like below
My Adapter
pager.setPageTransformer(false, new ViewPager.PageTransformer() {
#Override
public void transformPage(View page, float position) {
// do transformation here
// page.setRotationY(position * -40);
final float normalizedposition = Math.abs(Math.abs(position) - 1);
page.setScaleX(normalizedposition / 2 + 0.4f);
page.setScaleY(normalizedposition / 2 + 0.4f);
}
});
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
pager.setCurrentItem(position);
}
#Override
public void onPageScrolled(int position, float positionOffset,
int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
can anyone suggest how to achieve this
All answers are most welcome
Is it possible to have 2 ViewPagers that simultaniously scroll together, if I start scrolling on one, the other does the exact same scrolling behaviour. Or should I implement somthing other than a ViewPager.
thank you
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 (tried the solution from this thread too - didn't work) - 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
}
});
You can give each an OnPageChangeListsner and implement onPageScrolled (and maybe also onPageSelected for when you change pages without scrolling). Since the logic will be the same, we can write a class for this:
public class ViewPagerScrollSync implements ViewPager.OnPageChangeListener {
private ViewPager actor; // the one being scrolled
private ViewPager target; // the one that needs to be scrolled in sync
public ViewPagerScrollSync(ViewPager actor, ViewPager target) {
this.actor = actor;
this.target = target;
actor.setOnPageChangeListener(this);
}
#Override
public void onPageScrollStateChanged(int state) {
if (actor.isFakeDragging()) {
return;
}
if (state == ViewPager.SCROLL_STATE_DRAGGING) {
// actor has begun a drag
target.beginFakeDrag();
} else if (state == ViewPager.SCROLL_STATE_IDLE) {
// actor has finished settling
target.endFakeDrag();
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (actor.isFakeDragging()) {
return;
}
if (target.isFakeDragging()) {
// calculate drag amount in pixels.
// i don't have code for this off the top of my head, but you'll probably
// have to store the last position and offset from the previous call to
// this method and take the difference.
float dx = ...
target.fakeDragBy(dx);
}
}
#Override
public void onPageSelected(int position) {
if (actor.isFakeDragging()) {
return;
}
// Check isFakeDragging here because this callback also occurs when
// the user lifts his finger on a drag. If it was a real drag, we will
// have begun a fake drag of the target; otherwise it was probably a
// programmatic change of the current page.
if (!target.isFakeDragging()) {
target.setCurrentItem(position);
}
}
}
Then in your Activity/Fragment, you would do this:
ViewPager pager1 = ...
ViewPager pager2 = ...
ViewPagerScrollSync sync1 = new ViewPagerScrollSync(pager1, pager2);
ViewPagerScrollSync sync2 = new ViewPagerScrollSync(pager2, pager1);
you will need to keep an eye on your positions to avoid IndexOutOfBounds errors. Generally:
viewPager1.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
viewPager2.setCurrentItem(position);
}
#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
}
});
This will work assuming that both ViewPagers have the same number of items. Otherwise, you will need to track your positions to synchronize the behavior of both pagers.
To clarify: if your Adapters have a different number of items or you don't want both pagers to behave exactly the same, you will need to check the position of viewPager1 in the onPageSelected() method and then adjust the position to pass to the setCurrentItem() method of viewPager2
Drop this class in your project
import androidx.viewpager.widget.ViewPager;
/**
* Sync Scroll 2 View Pager
*/
public class SyncScrollOnTouchListener implements ViewPager.OnPageChangeListener {
private ViewPager master;
private ViewPager slave;
private int mScrollState = ViewPager.SCROLL_STATE_IDLE;
public SyncScrollOnTouchListener(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);
}
}
}
Usage:
Now you can sync page change of View Pages
masterPager.addOnPageChangeListener(newSyncScrollOnTouchListener(masterPager,slavePager));
//Sync Scroll of Pages
leftPanelPager.addOnPageChangeListener(new SyncScrollOnTouchListener(leftPanelPager,rightPanelPager));
rightPanelPager.addOnPageChangeListener(new SyncScrollOnTouchListener(rightPanelPager,leftPanelPager));
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..
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);
}
}
});
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