Реализация круговой прокрутки в PagerAdapter
Я использую PagerAdapter для горизонтальной прокрутки для показа газетных страниц в своем приложении.
В настоящее время я хочу реализовать круговую прокрутку в этом приложении. Теперь я сделал то, что я сделал, это whenever I am getting on last page I try to set the currentItem to first page
то есть, что функциональность работает для последней страницы на первой странице, но проблема в том, что как я могу перейти на последнюю страницу с первого стр.
Здесь я вставляю свой код, связанный с 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);
}
}
}
});
Также PagerAdapter i.e AwesomweAdapter в моем случае также является следующим: -
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);
}
Пожалуйста, дайте мне любые предложения/изменения в моем коде (если применимо).
Заранее спасибо.
Ответы
Ответ 1
Я мог бы достичь этого, переопределив onPageSelected
метод OnPageChangeListener
. У вас есть три страницы в этом порядке A<->B<->C
. Цель состоит в том, чтобы достичь C
, если мы прокрутимся прямо от A
и аналогичным образом достигнем A
, если прокрутим влево от C
.
Чтобы сделать это, определите, что у вас 5 страниц (3 + 2), и упорядочивайте страницы следующим образом:
C
<->A<->B<->C<->
A
Теперь в методе onPageSelected
проверьте и если позиция if 0
, измените его на 3
(getCount()-2
), и если позиция 4
(getCount()-1
), измените ее на 1
, Обязательно используйте метод:
setCurrentItem(item, smoothScroll)
Вот полный код для CircularPagerAdaptor Класс:
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
}
}
И вот как вы можете его использовать:
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);
Ответ 2
Мне также нужен круговой ViewPager. Это то, что я сделал. Я предполагаю, что вы получаете значение pageCount откуда-то.
...
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) {
}
});
и вот GestureDetector.
Скопировано из здесь
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;
}
}
Ответ 3
Развернувшись на Z0lenDer, при использовании обычного ViewPager
, где вам не нужно освобождать память для каждого связанного представления, более эффективно хранить созданных представлений, а не идентификаторов макета. Это необходимо, если вы хотите избавиться от любой задержки и мерцания при переключении элемента.
Также существует проблема с анимацией при использовании onPageSelected
, так как она не позволяет завершить слайд перед выполнением переключения. Единственный способ избежать этого - только выполнить переключатель, как только состояние прокрутки изменилось на SCROLL_STATE_IDLE
и просто установит текущий элемент в 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);
}
}
}
});
Ответ 4
Попробуйте это
((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
Это должно быть помещено внутри
@Override
public Object instantiateItem(final View container, int position) {}
Ответ 5
Я использовал его таким образом,
фрагменты в адаптере 0 > 1 > 2 > 3 > 4 > 5,
0 и 5 являются фиктивными
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);
}
}
});
Ответ 6
Ну, это помогло
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 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) {
}
}
Это был @tobi_b answer