Implementing Circular Scrolling In PagerAdapter
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 page
ie 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 ie 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);
}
}
}
});
链接地址: http://www.djcxy.com/p/91220.html
上一篇: 如何轻扫图像
下一篇: 在PagerAdapter中实现循环滚动