android - viewpager - tablayout without fragments
ViewPager como una cola/envoltura circular (12)
- El conjunto para los artículos cuenta con un valor grande, digamos 500000.
- Devuelve del adaptador este valor * número de elementos reales.
- En getItem (int i) ajuste i a -> i = i% número de elementos reales.
En onCreate (Bundle savedInstanceState) establezca el elemento actual para la página 500000/2
public class MainActivity extends FragmentActivity { private final static int ITEMS_MAX_VALUE = 500000; private int actualNumCount = 10; private ViewPager mPager = null; private class OptionPageAdapter extends FragmentStatePagerAdapter { public OptionPageAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int i) { try { i = i % OptionType.values().length; OptionPage optionPage = new OptionPage(i); optionPage.id = i; return optionPage; } catch (Exception e) { VayoLog.log(Level.WARNING, "Unable to create OptionFragment for id: " + i, e); } return null; } @Override public int getCount() { return ITEMS_MAX_VALUE * actualNumCount; } } public static class OptionPage extends Fragment { private int id = 0 @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View mainView = inflater.inflate(R.layout.main_page, container, false); return mainView; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mPager = (ViewPager) findViewById(R.id.main_pager); mPager.setOffscreenPageLimit(3); mPager.setAdapter(new OptionPageAdapter(this.getSupportFragmentManager())); mPager.setCurrentItem(ITEMS_MAX_VALUE/2,false); } }
Estoy usando ViewPager
con FragmentStatePagerAdapter
para permitir la navegación entre algunos fragmentos.
Digamos que tengo tres fragmentos: A
, B
y C
El ViewPager muestra inicialmente el Fragmento A, y le permite navegar al Fragmento B deslizando de derecha a izquierda, y luego al Fragmento C al deslizar de nuevo. Esto permite las siguientes rutas de navegación: A <--> B <--> C
Lo que me gustaría es poder pasar de izquierda a derecha en el Fragmento A y hacer que ViewPager muestre el Fragmento C, es decir, que se comporte como una cola circular y permita ... --> C <--> A <--> B <--> C <--> A <-- ...
No quiero duplicar los Fragmentos en otras posiciones (es decir, terminar con más de tres instancias).
¿Es posible esta funcionalidad de envoltura con un ViewPager?
- agregue una copia falsa del último elemento al principio de su lista.
- agregue una copia falsa del primer elemento al final.
seleccione el primer elemento real en algún lugar del código de inicio:
mViewPager.setCurrentItem(1);
modificar setOnPageChangeListener:
@Override public void onPageSelected(int state) { if (state == ITEMS_NUMBER + 1) { mViewPager.setCurrentItem(1, false); } else if (state == 0) { mViewPager.setCurrentItem(ITEMS_NUMBER, false); } }
Esta es una solución sin páginas falsas y funciona como un encanto:
public class CircularViewPagerHandler implements ViewPager.OnPageChangeListener {
private ViewPager mViewPager;
private int mCurrentPosition;
private int mScrollState;
public CircularViewPagerHandler(final ViewPager viewPager) {
mViewPager = viewPager;
}
@Override
public void onPageSelected(final int position) {
mCurrentPosition = position;
}
@Override
public void onPageScrollStateChanged(final int state) {
handleScrollState(state);
mScrollState = state;
}
private void handleScrollState(final int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
setNextItemIfNeeded();
}
}
private void setNextItemIfNeeded() {
if (!isScrollStateSettling()) {
handleSetNextItem();
}
}
private boolean isScrollStateSettling() {
return mScrollState == ViewPager.SCROLL_STATE_SETTLING;
}
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) {
}
}
Solo tiene que configurarlo en su ViewPager como en PageChangeListener y eso es todo: (** obsoleto ahora ** verifique las notas de edición)
viewPager.setOnPageChangeListener(new CircularViewPagerHandler(viewPager));
Para evitar que este brillo azul en el "final" de su ViewPager debe aplicar esta línea a su xml donde se coloca ViewPager:
android:overScrollMode="never"
Hemos mejorado la solución anterior y creamos una pequeña biblioteca en github . No dude en echarle un vistazo :)
Editar :: De acuerdo con el comentario de @Bhavana, solo use addOnPageChangeListener en lugar de setOnPageChangeListener, ya que más tarde está en desuso.
viewPager.addOnPageChangeListener(new CircularViewPagerHandler(viewPager));
Esto casi logra lo que quiere antonyt, pero no te deja ir de A -> C. (No probado enérgicamente, pero parece funcionar bien)
public class MyAdapter extends PagerAdapter {
private List<Object> mItems;
private int mFakeCount = 0;
public MyAdapter(Context context, List<Object> items) {
mItems = items;
mFakeCount = mItems.size()+1;
}
@Override
public int getCount() {
return mFakeCount;
}
@Override
public Object instantiateItem(View collection, int position) {
// make the size larger, and change the position
// to trick viewpager into paging forever
if (position >= mItems.size()-1) {
int newPosition = position%mItems.size();
position = newPosition;
mFakeCount++;
}
// do your layout inflating and what not here.
// position will now refer to a correct index into your mItems list
// even if the user has paged so many times that the view has wrapped
}
}
Idea simple de cómo lograr esto. Cuando tenga su matriz con páginas simplemente agregue esta matriz a otra matriz exactamente en el medio.
Por ejemplo, tienes una matriz con 15 elementos. Así que colóquelo en una matriz de 400 elementos en el medio (posición 200)
A continuación, simplemente llene la matriz a la izquierda y derecha para que pueda moverse.
Imagen de muestra:
¿Como luce?
Deja que el código hable :)
https://gist.github.com/gelldur/051394d10f6f98e2c13d
public class CyclicPagesAdapter extends FragmentStatePagerAdapter {
public CyclicPagesAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return _fragments.get(position).createInstance();
}
@Override
public int getCount() {
return _fragments.size();
}
public void addFragment(FragmentCreator creator) {
_fragments.add(creator);
}
public interface FragmentCreator {
Fragment createInstance();
}
public void clear() {
_fragments.clear();
}
public void add(FragmentCreator fragmentCreator) {
_fragments.add(fragmentCreator);
}
public void finishAdding() {
ArrayList<FragmentCreator> arrayList = new ArrayList<>(Arrays.asList(new FragmentCreator[MAX_PAGES]));
arrayList.addAll(MAX_PAGES / 2, _fragments);
int mrPointer = _fragments.size() - 1;
for (int i = MAX_PAGES / 2 - 1; i > -1; --i) {
arrayList.set(i, _fragments.get(mrPointer));
--mrPointer;
if (mrPointer < 0) {
mrPointer = _fragments.size() - 1;
}
}
mrPointer = 0;
for (int i = MAX_PAGES / 2 + _fragments.size(); i < arrayList.size(); ++i) {
arrayList.set(i, _fragments.get(mrPointer));
++mrPointer;
if (mrPointer >= _fragments.size()) {
mrPointer = 0;
}
}
_fragmentsRaw = _fragments;
_fragments = arrayList;
}
public int getStartIndex() {
return MAX_PAGES / 2;
}
public int getRealPagesCount() {
return _fragmentsRaw.size();
}
public int getRealPagePosition(int index) {
final FragmentCreator fragmentCreator = _fragments.get(index);
for (int i = 0; i < _fragmentsRaw.size(); ++i) {
if (fragmentCreator == _fragmentsRaw.get(i)) {
return i;
}
}
//Fail...
return 0;
}
private static final int MAX_PAGES = 500;
public ArrayList<FragmentCreator> _fragments = new ArrayList<>();
public ArrayList<FragmentCreator> _fragmentsRaw;
}
Implementé ViewPager / PagerAdapter que puede permitir el comportamiento de búsqueda pseudo infinito. Funciona al especificar un número muy grande como el recuento real, pero los asigna al rango real del conjunto de datos / conjunto de páginas. También compensa el comienzo con un número grande para que pueda desplazarse inmediatamente hacia la izquierda desde la ''primera'' página.
No funciona tan bien una vez que llegas a la página 1,000,000 (verás fallas gráficas al desplazarte), pero esto normalmente no es un caso de uso del mundo real. Podría arreglar esto restableciendo el recuento a un número más bajo de vez en cuando, pero lo dejaré como está por ahora.
El InfinitePagerAdapter
envuelve un ViewPager existente, por lo que el uso es bastante transparente. El InfiniteViewPager
hace un poco de trabajo para asegurarse de que puede desplazarse hacia la izquierda y la derecha muchas veces.
Justo ahora vi esta pregunta y encontré una solución. Enlace de solución
Realice los siguientes cambios en su función onPageScrollStateChanged. Considera que tienes 4 pestañas.
private int previousState, currentState;
public void onPageScrollStateChanged(int state) {
int currentPage = viewPager.getCurrentItem(); //ViewPager Type
if (currentPage == 3 || currentPage == 0){
previousState = currentState;
currentState = state;
if (previousState == 1 && currentState == 0){
viewPager.setCurrentItem(currentPage == 0 ? 3 : 0);
}
}
}
Si la variable de estado está en las pestañas existentes, su valor es 1.
Se convierte en 0 una vez que intenta navegar antes de su primera pestaña o después de su última pestaña. Por lo tanto, compare los estados anterior y actual, como se muestra en el código y listo.
Consulte la función onPageScrollStateChanged here .
Espero eso ayude.
Otra forma de hacerlo es agregar una copia falsa del primer elemento en el buscapersonas al final de su adaptador, y una copia falsa del último elemento al principio. Luego, cuando esté viendo el primer / último elemento en el buscapersonas, cambie las páginas sin animar.
Así que, básicamente, los primeros / últimos elementos en su paginador de vista son falsos, solo para producir la animación correcta, y luego cambiar al final / principio. Ejemplo:
list.add(list.get(0)); //add the first item again as the last, to create the wrap effect
list.add(0, list.get(list.size()-2)); //insert a copy of the last item as the new first item, so we can scroll backwards too
viewPager.setAdapter(list);
viewPager.setCurrentItem(1, false); //default to the second element (because the first is now a fake)
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
private void handleScrollState(final int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) { //this is triggered when the switch to a new page is complete
final int lastPosition = viewPager.getAdapter().getCount() - 1;
if (currentPosition == lastPosition) {
viewPager.setCurrentItem(1, false); //false so we don''t animate
} else if (currentPosition == 0) {
viewPager.setCurrentItem(lastPosition -1, false);
}
}
}
Perdón por la demora, pero he visto las respuestas y II no pudo contener, también tengo que contestar :).
En su adaptador, en el método get count, proceda de la siguiente manera:
@Override
public int getCount() {
return myList.size() % Integer.MAX_VALUE;
}
¡Que funcionará!
Puede usar una vista simple de https://github.com/pozitiffcat/cyclicview
Una de las características es:
No duplique la primera y la última vista, utilice las variantes de mapa de bits en su lugar
Ejemplo:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CyclicView cyclicView = (CyclicView) findViewById(R.id.cyclic_view);
cyclicView.setAdapter(new CyclicAdapter() {
@Override
public int getItemsCount() {
return 10;
}
@Override
public View createView(int position) {
TextView textView = new TextView(MainActivity.this);
textView.setText(String.format("TextView #%d", position + 1));
return textView;
}
@Override
public void removeView(int position, View view) {
// Do nothing
}
});
}
}
Tengo una solución que creo que funcionará. No está probado, pero aquí está:
Un contenedor alrededor de FragmentPagerAdapter como superclase:
public abstract class AFlexibleFragmentPagerAdapter extends
FragmentPagerAdapter
{
public AFlexibleFragmentPagerAdapter(FragmentManager fm)
{
super(fm);
// TODO Auto-generated constructor stub
}
public abstract int getRealCount();
public abstract int getStartPosition();
public abstract int transformPosition(int position);
};
A continuación, la subclase de implementación FragmentPagerAdapter estándar esta nueva clase abstracta:
public class SomeFragmentPagerAdapter extends
AFlexibleFragmentPagerAdapter
{
private Collection<Fragment> someContainer;
public SomeFragmentPagerAdapter(FragmentManager fm, Container<Fragment> fs)
{
super(fm);
someContainer = fs;
}
@Override
public int getRealCount() { return someContainer.size(); }
@Override
public int getStartPosition() { return 0; }
@Override
public int transformPosition(int position) { return position; }
};
Y la implementación del Adaptador de radiolocalizador cíclico.
public class SomeCyclicFragmentPagerAdapter extends
SomeFragmentPagerAdapter
{
private int realCount = 0;
private int dummyCount = 0;
private static final int MULTI = 3;
public SomeFragmentPagerAdapter(FragmentManager fm, ...)
{
super(fm);
realCount = super.getCount();
dummyCount *= MULTI;
}
@Override
public int getCount() { return dummyCount; }
@Override
public int getRealCount() { return realCount; }
@Override
public int getStartPosition() { return realCount; }
@Override
public int transformPosition(int position)
{
if (position < realCount) position += realCount;
else if (position >= (2 * realCount)) position -= realCount;
return position;
}
};
Inicialización de ViewPager
this.mViewPager.setCurrentItem(this.mPagerAdapter.getStartPosition());
Y finalmente, la implementación de devolución de llamada:
@Override
public void onPageSelected(int position)
{
this.mTabHost.setCurrentTab(position % mPagerAdapter.getRealCount());
this.mViewPager.setCurrentItem(this.mPagerAdapter
.transformPosition(position));
}
@Override
public void onTabChanged(String tabId)
{
int pos = this.mTabHost.getCurrentTab();
this.mViewPager.setCurrentItem(this.mPagerAdapter
.transformPosition(pos));
}
basado en este enfoque
verdadero bucle, título del buscapersonas, verdadero sin desplazamiento de actualización
private static final int ITEM_NUM = 5 ; // 5 for smooth pager title strip
private void addFragment(String title, String className) {
if (fragments.size() < ITEM_NUM) {
Bundle b = new Bundle();
b.putInt("pos", fragments.size());
fragments.add(Fragment.instantiate(this, className, b));
}
titles.add(title);
itemList.add(className);
}
ver el adaptador de página:
public static class MyFragmentPageAdapter extends FragmentPagerAdapter {
private HashMap<Long, Fragment> mItems = new HashMap<Long, Fragment>();
public MyFragmentPageAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@Override
public long getItemId(int position) {
int id = java.lang.System.identityHashCode(fragments.get(position));
return id;
}
@Override
public Fragment getItem(int position) {
long id = getItemId(position);
if (mItems.get(id) != null) {
return mItems.get(id);
}
Fragment f = fragments.get(position);
return f;
}
@Override
public int getCount() {
return ITEM_NUM;
}
@Override
public CharSequence getPageTitle(int position) {
String t = titles.get(getItem(position).getArguments()
.getInt("pos"));
return t;
}
@Override
public int getItemPosition(Object object) {
for (int i = 0; i < ITEM_NUM; i++) {
Fragment item = (Fragment) fragments.get(i);
if (item.equals((Fragment) object)) {
return i;
}
}
return POSITION_NONE;
}
}
utilizando:
itemList = new ArrayList<String>();
fragments = new ArrayList<Fragment>();
titles = new ArrayList<String>();
addFragment("Title1", Fragment1.class.getName());
...
addFragment("TitleN", FragmentN.class.getName());
mAdapter = new MyFragmentPageAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id...);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(2);
currPage = 2;
visibleFragmentPos = 2;
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int curr) {
dir = curr - currPage;
currPage = curr;
}
private void shiftRight() {
fragments.remove(0);
int pos = visibleFragmentPos + 3;
if (pos > itemList.size() - 1)
pos -= itemList.size();
if (++visibleFragmentPos > itemList.size() - 1)
visibleFragmentPos = 0;
insertItem(4, pos);
}
private void shiftLeft() {
fragments.remove(4);
int pos = visibleFragmentPos - 3;
if (pos < 0)
pos += itemList.size();
if (--visibleFragmentPos < 0)
visibleFragmentPos = itemList.size() - 1;
insertItem(0, pos);
}
private void insertItem(int datasetPos, int listPos) {
Bundle b = new Bundle();
b.putInt("pos", listPos);
fragments.add(datasetPos,
Fragment.instantiate(ctx, itemList.get(listPos), b));
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (dir == 1) {
shiftRight();
} else if (dir == -1) {
shiftLeft();
}
mAdapter.notifyDataSetChanged();
currPage = 2;
}
}
});