Changer ViewPager pour activer le défilement infini de la page
Jon Willis a posté sur comment activer un scrolling infini avec son code. Là, il a dit qu'il a fait quelques changements dans la classe ViewPager dans la bibliothèque de soutien android. Quelles modifications ont été apportées et comment est-il possible de "recompiler" la bibliothèque avec le changement de ViewPager?
9 réponses
j'ai résolu ce problème très simplement en utilisant un petit hack dans l'adaptateur. Voici mon code:
public class MyPagerAdapter extends FragmentStatePagerAdapter
{
public static int LOOPS_COUNT = 1000;
private ArrayList<Product> mProducts;
public MyPagerAdapter(FragmentManager manager, ArrayList<Product> products)
{
super(manager);
mProducts = products;
}
@Override
public Fragment getItem(int position)
{
if (mProducts != null && mProducts.size() > 0)
{
position = position % mProducts.size(); // use modulo for infinite cycling
return MyFragment.newInstance(mProducts.get(position));
}
else
{
return MyFragment.newInstance(null);
}
}
@Override
public int getCount()
{
if (mProducts != null && mProducts.size() > 0)
{
return mProducts.size()*LOOPS_COUNT; // simulate infinite by big number of products
}
else
{
return 1;
}
}
}
et puis, dans le ViewPager, nous mettons la page actuelle au milieu:
mAdapter = new MyPagerAdapter(getSupportFragmentManager(), mProducts);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(mViewPager.getChildCount() * MyPagerAdapter.LOOPS_COUNT / 2, false); // set current item in the adapter to middle
Merci pour votre réponse Shereef.
j'ai résolu un peu différemment.
j'ai changé le code de la classe ViewPager de la bibliothèque de soutien android. La méthode setCurrentItem(int)
modifie la page avec l'animation. Cette méthode appelle une méthode interne qui nécessite l'index et d'un indicateur permettant de défilement lisse. Ce drapeau est boolean smoothScroll
.
L'extension de cette méthode avec un deuxième paramètre boolean smoothScroll
résolu m'.
Appeler cette méthode setCurrentItem(int index, boolean smoothScroll)
m'a permis de la faire défiler indéfiniment.
Voici un exemple complet:
veuillez considérer que seule la page centrale est affichée. De plus, est-ce que j'ai stocké les pages séparément, ce qui m'a permis de les traiter avec plus de facilité.
private class Page {
View page;
List<..> data;
}
// page for predecessor, current, and successor
Page[] pages = new Page[3];
mDayPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (mFocusedPage == 0) {
// move some stuff from the
// center to the right here
moveStuff(pages[1], pages[2]);
// move stuff from the left to the center
moveStuff(pages[0], pages[1]);
// retrieve new stuff and insert it to the left page
insertStuff(pages[0]);
}
else if (mFocusedPage == 2) {
// move stuff from the center to the left page
moveStuff(pages[1], pages[0]);
// move stuff from the right to the center page
moveStuff(pages[2], pages[1]);
// retrieve stuff and insert it to the right page
insertStuff(pages[2]);
}
// go back to the center allowing to scroll indefinitely
mDayPager.setCurrentItem(1, false);
}
}
});
cependant, sans le Code de Jon Willis, Je ne l'aurais pas résolu moi-même.
EDIT: voici un blogpost about ceci:
pageur de vue infinie en surpassant 4 méthodes d'adaptateur dans votre classe d'adaptateur existante
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public CharSequence getPageTitle(int position) {
String title = mTitleList.get(position % mActualTitleListSize);
return title;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
int virtualPosition = position % mActualTitleListSize;
return super.instantiateItem(container, virtualPosition);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
int virtualPosition = position % mActualTitleListSize;
super.destroyItem(container, virtualPosition, object);
}
Tout ce que vous devez faire est de regarder l'exemple ici
vous constaterez qu'à la ligne 295 la page est toujours positionnée à 1 de sorte qu'elle soit scrollable
et que le nombre de pages est de 3 dans la méthode getCount()
.
ce sont les 2 choses principales que vous devez changer, le reste est votre logique et vous pouvez les gérer différemment.
il suffit de faire un compteur personnel qui compte la page réelle que vous êtes sur parce que la position ne sera plus utilisable après avoir toujours réglé la page courante à 1 sur la ligne 295.
p. S. ce code n'est pas le mien il a été référencé dans la question que vous avez liée dans votre question
en fait, j'ai regardé les différentes façons de faire cette pagination "infinie", et même si la notion humaine du temps est qu'il est infini (même si nous avons une notion du début et de la fin du temps), les ordinateurs traitent dans le discret. Il y a un temps minimum et un temps maximum (qui peuvent être ajustés au fil du temps, vous vous souvenez de la base de la peur de L'an 2000?).
de toute façon, le point de cette discussion est qu'il est / devrait être suffisant pour soutenir un plage de dates infinies à travers une plage de dates réellement finie. Un bon exemple de cela est la mise en œuvre CalendarView
D'Android framework, et le WeeksAdapter
à l'intérieur de celui-ci. La date minimale par défaut est en 1900 et la date maximale par défaut est en 2100, cela devrait couvrir 99% de l'utilisation du calendrier de n'importe qui dans un rayon de 10 ans autour d'aujourd'hui facilement.
ce qu'ils font dans leur mise en œuvre (axée sur les semaines) est de calculer le nombre de semaines entre le minimum et le maximum date. Cela devient le nombre de pages du pager. Rappelez-vous que le pager n'a pas besoin de maintenir toutes ces pages simultanément ( setOffscreenPageLimit(int)
), il a juste besoin d'être en mesure de créer la page basée sur le numéro de page (ou index/position). Dans ce cas, l'indice est le nombre de semaines que la semaine est à partir de la date minimale. Avec cette approche, vous avez juste à maintenir la date minimale et le nombre de pages (distance à la date maximale), puis pour toute page, vous pouvez facilement calculer la semaine associé à cette page. Pas de danse autour du fait que ViewPager
ne supporte pas la boucle (A. K. une pagination infinie), et essayer de la forcer à se comporter comme elle peut défiler à l'infini.
new FragmentStatePagerAdapter(getFragmentManager()) {
@Override
public Fragment getItem(int index) {
final Bundle arguments = new Bundle(getArguments());
final Calendar temp_calendar = Calendar.getInstance();
temp_calendar.setTimeInMillis(_minimum_date.getTimeInMillis());
temp_calendar.setFirstDayOfWeek(_calendar.getStartOfWeek());
temp_calendar.add(Calendar.WEEK_OF_YEAR, index);
// Moves to the first day of this week
temp_calendar.add(Calendar.DAY_OF_YEAR,
-UiUtils.modulus(temp_calendar.get(Calendar.DAY_OF_WEEK) - temp_calendar.getFirstDayOfWeek(),
7));
arguments.putLong(KEY_DATE, temp_calendar.getTimeInMillis());
return Fragment.instantiate(getActivity(), WeekDaysFragment.class.getName(), arguments);
}
@Override
public int getCount() {
return _total_number_of_weeks;
}
};
puis WeekDaysFragment
peut facilement afficher la semaine à partir de la date passée dans ses arguments.
alternativement, il semble qu'une version de L'application Calendrier sur Android utilise un ViewSwitcher
(ce qui signifie qu'il n'y a que 2 pages, celle que vous voir et la page cachée). Il modifie ensuite l'animation de transition en fonction de la façon dont l'Utilisateur a glissé et rend la page suivante/précédente en conséquence. De cette façon, vous obtenez pagination infinie parce qu'il suffit de passer entre deux pages infiniment. Cela nécessite d'utiliser un View
pour la page cependant, qui est la façon dont je suis allé avec la première approche.
en général, si vous voulez une" pagination infinie", c'est probablement parce que vos pages sont basées sur des dates ou des heures. Si c'est le cas, envisagez d'utiliser un sous-ensemble fini de temps qui est relativement infini à la place. C'est ainsi que CalendarView
est mis en œuvre par exemple. Ou vous pouvez utiliser l'approche ViewSwitcher
. L'avantage de ces deux approches est que ni l'un ni l'autre ne fait quelque chose de particulièrement inhabituel avec le ViewSwitcher
ou ViewPager
, et ne nécessite pas de trucs ou de réimpression pour les forcer à se comporter à l'infini ( ViewSwitcher
est déjà conçu pour basculer entre les vues à l'infini, mais ViewPager
est conçu pour travailler sur un fini, mais pas nécessairement constante, ensemble de pages).
squelette d'adaptateur de curseur infini basé sur des échantillons précédents
quelques questions essentielles:
- se souvenir de la position initiale (relative) dans la vue page (étiquette utilisée dans l'échantillon), nous allons donc regarder cette position pour définir la position relative de la vue. sinon l'ordre de l'enfant dans le pager est mixte
- doit remplir pour la première fois la vue absolue à l'intérieur de l'adaptateur. (le reste du temps ce remplissage ne sera pas valide) trouvé aucun moyen de le forcer remplir de pager gestionnaire. les temps de repos de la vue absolue seront annulés du gestionnaire de pager avec les valeurs correctes.
- lorsque les pages sont glissées rapidement, la page latérale (en fait à gauche) n'est pas remplie à partir du pager handler. pas de solution de contournement pour le moment, il suffit d'utiliser la vue vide, elle sera remplie de valeurs réelles Lorsque la traînée est arrêtée. upd: solution rapide: désactiver le destroyItem de l'adaptateur.
vous pouvez regarder le logcat pour comprendre ce qui se passe dans ce échantillon
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/calendar_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:padding="5dp"
android:layout_gravity="center_horizontal"
android:text="Text Text Text"
/>
</RelativeLayout>
et ensuite:
public class ActivityCalendar extends Activity
{
public class CalendarAdapter extends PagerAdapter
{
@Override
public int getCount()
{
return 3;
}
@Override
public boolean isViewFromObject(View view, Object object)
{
return view == ((RelativeLayout) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position)
{
LayoutInflater inflater = (LayoutInflater)ActivityCalendar.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View viewLayout = inflater.inflate(R.layout.layout_calendar, container, false);
viewLayout.setTag(new Integer(position));
//TextView tv = (TextView) viewLayout.findViewById(R.id.calendar_text);
//tv.setText(String.format("Text Text Text relative: %d", position));
if (!ActivityCalendar.this.scrolledOnce)
{
// fill here only first time, the rest will be overriden in pager scroll handler
switch (position)
{
case 0:
ActivityCalendar.this.setPageContent(viewLayout, globalPosition - 1);
break;
case 1:
ActivityCalendar.this.setPageContent(viewLayout, globalPosition);
break;
case 2:
ActivityCalendar.this.setPageContent(viewLayout, globalPosition + 1);
break;
}
}
((ViewPager) container).addView(viewLayout);
//Log.i("instantiateItem", String.format("position = %d", position));
return viewLayout;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
((ViewPager) container).removeView((RelativeLayout) object);
//Log.i("destroyItem", String.format("position = %d", position));
}
}
public void setPageContent(View viewLayout, int globalPosition)
{
if (viewLayout == null)
return;
TextView tv = (TextView) viewLayout.findViewById(R.id.calendar_text);
tv.setText(String.format("Text Text Text global %d", globalPosition));
}
private boolean scrolledOnce = false;
private int focusedPage = 0;
private int globalPosition = 0;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calendar);
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setOnPageChangeListener(new OnPageChangeListener()
{
@Override
public void onPageSelected(int position)
{
focusedPage = position;
// actual page change only when position == 1
if (position == 1)
setTitle(String.format("relative: %d, global: %d", position, globalPosition));
Log.i("onPageSelected", String.format("focusedPage/position = %d, globalPosition = %d", position, globalPosition));
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
//Log.i("onPageScrolled", String.format("position = %d, positionOffset = %f", position, positionOffset));
}
@Override
public void onPageScrollStateChanged(int state)
{
Log.i("onPageScrollStateChanged", String.format("state = %d, focusedPage = %d", state, focusedPage));
if (state == ViewPager.SCROLL_STATE_IDLE)
{
if (focusedPage == 0)
globalPosition--;
else if (focusedPage == 2)
globalPosition++;
scrolledOnce = true;
for (int i = 0; i < viewPager.getChildCount(); i++)
{
final View v = viewPager.getChildAt(i);
if (v == null)
continue;
// reveal correct child position
Integer tag = (Integer)v.getTag();
if (tag == null)
continue;
switch (tag.intValue())
{
case 0:
setPageContent(v, globalPosition - 1);
break;
case 1:
setPageContent(v, globalPosition);
break;
case 2:
setPageContent(v, globalPosition + 1);
break;
}
}
Log.i("onPageScrollStateChanged", String.format("globalPosition = %d", globalPosition));
viewPager.setCurrentItem(1, false);
}
}
});
CalendarAdapter calendarAdapter = this.new CalendarAdapter();
viewPager.setAdapter(calendarAdapter);
// center item
viewPager.setCurrentItem(1, false);
}
}
Son piraté par CustomPagerAdapter :
activité principale.java :
import android.content.Context;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<String> numberList = new ArrayList<String>();
private CustomPagerAdapter mCustomPagerAdapter;
private ViewPager mViewPager;
private Handler handler;
private Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
numberList.clear();
for (int i = 0; i < 10; i++) {
numberList.add(""+i);
}
mViewPager = (ViewPager)findViewById(R.id.pager);
mCustomPagerAdapter = new CustomPagerAdapter(MainActivity.this);
EndlessPagerAdapter mAdapater = new EndlessPagerAdapter(mCustomPagerAdapter);
mViewPager.setAdapter(mAdapater);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int modulo = position%numberList.size();
Log.i("Current ViewPager View's Position", ""+modulo);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
handler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
mViewPager.setCurrentItem(mViewPager.getCurrentItem()+1);
handler.postDelayed(runnable, 1000);
}
};
handler.post(runnable);
}
@Override
protected void onDestroy() {
if(handler!=null){
handler.removeCallbacks(runnable);
}
super.onDestroy();
}
private class CustomPagerAdapter extends PagerAdapter {
Context mContext;
LayoutInflater mLayoutInflater;
public CustomPagerAdapter(Context context) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return numberList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.row_item_viewpager, container, false);
TextView textView = (TextView) itemView.findViewById(R.id.txtItem);
textView.setText(numberList.get(position));
container.addView(itemView);
return itemView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
private class EndlessPagerAdapter extends PagerAdapter {
private static final String TAG = "EndlessPagerAdapter";
private static final boolean DEBUG = false;
private final PagerAdapter mPagerAdapter;
EndlessPagerAdapter(PagerAdapter pagerAdapter) {
if (pagerAdapter == null) {
throw new IllegalArgumentException("Did you forget initialize PagerAdapter?");
}
if ((pagerAdapter instanceof FragmentPagerAdapter || pagerAdapter instanceof FragmentStatePagerAdapter) && pagerAdapter.getCount() < 3) {
throw new IllegalArgumentException("When you use FragmentPagerAdapter or FragmentStatePagerAdapter, it only supports >= 3 pages.");
}
mPagerAdapter = pagerAdapter;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (DEBUG) Log.d(TAG, "Destroy: " + getVirtualPosition(position));
mPagerAdapter.destroyItem(container, getVirtualPosition(position), object);
if (mPagerAdapter.getCount() < 4) {
mPagerAdapter.instantiateItem(container, getVirtualPosition(position));
}
}
@Override
public void finishUpdate(ViewGroup container) {
mPagerAdapter.finishUpdate(container);
}
@Override
public int getCount() {
return Integer.MAX_VALUE; // this is the magic that we can scroll infinitely.
}
@Override
public CharSequence getPageTitle(int position) {
return mPagerAdapter.getPageTitle(getVirtualPosition(position));
}
@Override
public float getPageWidth(int position) {
return mPagerAdapter.getPageWidth(getVirtualPosition(position));
}
@Override
public boolean isViewFromObject(View view, Object o) {
return mPagerAdapter.isViewFromObject(view, o);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (DEBUG) Log.d(TAG, "Instantiate: " + getVirtualPosition(position));
return mPagerAdapter.instantiateItem(container, getVirtualPosition(position));
}
@Override
public Parcelable saveState() {
return mPagerAdapter.saveState();
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
mPagerAdapter.restoreState(state, loader);
}
@Override
public void startUpdate(ViewGroup container) {
mPagerAdapter.startUpdate(container);
}
int getVirtualPosition(int realPosition) {
return realPosition % mPagerAdapter.getCount();
}
PagerAdapter getPagerAdapter() {
return mPagerAdapter;
}
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="180dp">
</android.support.v4.view.ViewPager>
</RelativeLayout>
row_item_viewpager.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txtItem"
android:textAppearance="@android:style/TextAppearance.Large"/>
</LinearLayout>
fait
pour le défilement infini avec des jours il est important que vous avez le bon fragment dans le pager donc j'ai écrit ma réponse sur cette page ( Viewpager dans Android pour passer entre les jours sans fin )
Ça marche très bien! Les réponses ci-dessus n'ont pas fonctionné pour moi car je voulais qu'il fonctionne.
j'ai construit une bibliothèque qui peut faire N'importe quel ViewPager, pagerAdapter (ou FragmentStatePagerAdapter), et TabLayout en option le défilement infini.
https://github.com/memorex386/infinite-scroll-viewpager-w-tabs