ViewPager dentro de ViewPager

Me gustaría crear una ViewPager (con tres elementos) donde cada una de sus vistas es otra ViewPager (con dos elementos). El usuario luego desliza elementos como este:

ViewPager1[0] ViewPager2[0] ViewPager1[0] ViewPager2[1] ViewPager1[1] ViewPager2[0] ViewPager1[1] ViewPager2[1] ViewPager1[2] ViewPager2[0] ViewPager1[2] ViewPager2[1] 

¿Cómo sería eso posible?

anular canScroll en el padre ViewPager:

 @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if(v != this && v instanceof ViewPager) { return true; } return super.canScroll(v, checkV, dx, x, y); } 

Prueba esto:

 public class CustomViewPager extends ViewPager { private int childId; public CustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (childId > 0) { ViewPager pager = (ViewPager)findViewById(childId); if (pager != null) { pager.requestDisallowInterceptTouchEvent(true); } } return super.onInterceptTouchEvent(event); } public void setChildId(int id) { this.childId = id; } } 

Busqué mucho tiempo para hacer una ViewPager dentro de otro trabajo de ViewPager y encontré la solución por “Android Noob” aquí. ¡Muchas gracias por eso!

Yo quería compartir mi solución, también. Agregué la posibilidad de cambiar la administración de deslizamiento a la ViewPager circundante una vez que se alcanza el último elemento (el más adecuado) en el ViewPager interno. Para evitar problemas técnicos, también guardo la primera dirección de deslizamiento para el último elemento: es decir, si desliza el dedo hacia la izquierda primero, un deslizamiento hacia la derecha mínimo no restablece el estado de desplazamiento.

 public class GalleryViewPager extends ViewPager { /** the last x position */ private float lastX; /** if the first swipe was from left to right (->), dont listen to swipes from the right */ private boolean slidingLeft; /** if the first swipe was from right to left (<-), dont listen to swipes from the left */ private boolean slidingRight; public GalleryViewPager(final Context context, final AttributeSet attrs) { super(context, attrs); } public GalleryViewPager(final Context context) { super(context); } @Override public boolean onTouchEvent(final MotionEvent ev) { final int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: // Disallow parent ViewPager to intercept touch events. this.getParent().requestDisallowInterceptTouchEvent(true); // save the current x position this.lastX = ev.getX(); break; case MotionEvent.ACTION_UP: // Allow parent ViewPager to intercept touch events. this.getParent().requestDisallowInterceptTouchEvent(false); // save the current x position this.lastX = ev.getX(); // reset swipe actions this.slidingLeft = false; this.slidingRight = false; break; case MotionEvent.ACTION_MOVE: /* * if this is the first item, scrolling from left to * right should navigate in the surrounding ViewPager */ if (this.getCurrentItem() == 0) { // swiping from left to right (->)? if (this.lastX <= ev.getX() && !this.slidingRight) { // make the parent touch interception active -> parent pager can swipe this.getParent().requestDisallowInterceptTouchEvent(false); } else { /* * if the first swipe was from right to left, dont listen to swipes * from left to right. this fixes glitches where the user first swipes * right, then left and the scrolling state gets reset */ this.slidingRight = true; // save the current x position this.lastX = ev.getX(); this.getParent().requestDisallowInterceptTouchEvent(true); } } else /* * if this is the last item, scrolling from right to * left should navigate in the surrounding ViewPager */ if (this.getCurrentItem() == this.getAdapter().getCount() - 1) { // swiping from right to left (<-)? if (this.lastX >= ev.getX() && !this.slidingLeft) { // make the parent touch interception active -> parent pager can swipe this.getParent().requestDisallowInterceptTouchEvent(false); } else { /* * if the first swipe was from left to right, dont listen to swipes * from right to left. this fixes glitches where the user first swipes * left, then right and the scrolling state gets reset */ this.slidingLeft = true; // save the current x position this.lastX = ev.getX(); this.getParent().requestDisallowInterceptTouchEvent(true); } } break; } super.onTouchEvent(ev); return true; } } 

¡Espero que esto ayude a alguien en el futuro!

Si el viewpager hijo está al final, desplácese al padre

 protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if(v != this && v instanceof ViewPager) { int currentItem = ((ViewPager) v).getCurrentItem(); int countItem = ((ViewPager) v).getAdapter().getCount(); if((currentItem==(countItem-1) && dx<0) || (currentItem==0 && dx>0)){ return false; } return true; } return super.canScroll(v, checkV, dx, x, y); } 

Primero crea una clase ViewPager personalizada de esta manera:

 public class CustomViewPager extends ViewPager { public CustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if(v instanceof ViewPager) { return true; } return super.canScroll(v, checkV, dx, x, y); } } 

El retorno (booleano) del método canScroll le dirá si el gesto horizontal para cambiar la página de ViewPager debe estar en el borde derecho o izquierdo del fragmento (verdadero) o si funciona para la pantalla de fragmento completo (falso). Si quiere, por ejemplo, que solo su primer fragmento use el borde derecho para pasar al siguiente fragmento porque el primer fragmento tiene otro evento de desplazamiento horizontal, este será el código para anular el método canScroll:

 @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if(v instanceof ViewPager) { int currentItem = ((ViewPager) v).getCurrentItem(); if((currentItem==0)){ return true; } return false; } return super.canScroll(v, checkV, dx, x, y); } 

El último paso será usar su clase CustomViewPager en su clase principal:

 ViewPager myPager= (CustomViewPager)myContext.findViewById(R.id.myCustomViewPager); 

y el xml:

  

No entiendo por qué no creas un localizador de 1 vista y creas la lógica del elemento de instanciación para obtener datos de diferentes fonts, lo que te haría alcanzar tu objective igualmente. No veo un caso en el que necesites 2 visualizadores de visualización.

Ejemplo

 ViewPager1[0] ViewPager2[0] = page 0 (position/2) = 0
ViewPager1[0] ViewPager2[1] = page 1 ((position-1)/2) = 0
ViewPager1[1] ViewPager2[0] = page 2 (position/2) = 1
ViewPager1[1] ViewPager2[1] = page 3 ((position-1)/2) = 1
ViewPager1[2] ViewPager2[0] = page 4 (position/2) = 2
ViewPager1[2] ViewPager2[1] = page 5 ((position-1)/2) = 2

y en el código:

 @Override public Object instantiateItem(View collection, int position) { LayoutInflater inflater = THISCLASSNAME.this.getLayoutInflater(); View v = null; if(position%2 == 0) { // viewpager 1 code int vp1pos = position/2; v = inlater.inflate(R.layout.somelayout, collection, false); Button b = (Button)v.findViewById(R.id.somebutton); b.setText(array1[vp1pos]); } else { int vp2pos = (position-1)/2; v = inlater.inflate(R.layout.somelayout, collection, false); Button b = (Button)v.findViewById(R.id.somebutton); b.setText(array2[vp2pos]); } ((DirectionalViewPager) collection).addView(v, 0); return v; } 

De esta forma tienes prácticamente 2 lógica de viewpagers, puedes personalizarla más que eso, pero te estoy dando ideas

PD: codifiqué esto aquí, así que si hay errores de mayúsculas o errores ortográficos, perdónenme.

Espero que esto ayude, si te vuelves más específico y necesitas más ayuda, agrega un comentario sobre mi respuesta y la modificaré

Resuelvo esta tarea creando dos herederos personalizados de ViewPager. En mi caso, OuterViewPager e InnerViewPager.

 public class InnerViewPager extends ViewPager { private int mPrevMoveX; public InnerViewPager(Context context) { super(context); } public InnerViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPrevMoveX = (int) event.getX(); return super.onTouchEvent(event); case MotionEvent.ACTION_MOVE: int distanceX = mPrevMoveX - (int) event.getX(); mPrevMoveX = (int) event.getX(); boolean canScrollLeft = true; boolean canScrollRight = true; if(getCurrentItem() == getAdapter().getCount() - 1) { canScrollLeft = false; } if(getCurrentItem() == 0) { canScrollRight = false; } if(distanceX > 0) { return canScrollRight; } else { return canScrollLeft; } } return super.onInterceptTouchEvent(event); } public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPrevMoveX = (int) event.getX(); return super.onTouchEvent(event); case MotionEvent.ACTION_MOVE: int distanceX = mPrevMoveX - (int) event.getX(); mPrevMoveX = (int) event.getX(); boolean canScrollLeft = true; boolean canScrollRight = true; if(getCurrentItem() == getAdapter().getCount() - 1) { canScrollLeft = false; } if(getCurrentItem() == 0) { canScrollRight = false; } if(distanceX > 0) { super.onTouchEvent(event); return canScrollLeft; } else { super.onTouchEvent(event); return canScrollRight; } } return super.onTouchEvent(event); } } public class OuterViewPager extends ViewPager { private int mPrevMoveX; public OuterViewPager(Context context) { super(context); init(); } public OuterViewPager(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { setOnPageChangeListener(new CustomPageChangeListener()); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mPrevMoveX = (int) ev.getX(); return super.onInterceptTouchEvent(ev); case MotionEvent.ACTION_MOVE: /*there you should get currentInnerPager - instance of InnerPager on current page of instance of OuterPager*/ int distanceX = mPrevMoveX - (int) ev.getX(); mPrevMoveX = (int) ev.getX(); boolean canScrollLeft = true; boolean canScrollRight = true; if(currentInnerPager.getCurrentItem() == currentInnerPager.getAdapter().getCount() - 1) { canScrollLeft = false; } if(currentInnerPager.getCurrentItem() == 0) { canScrollRight = false; } if(distanceX > 0) { return !canScrollLeft; } else { return !canScrollRight; } } return super.onInterceptTouchEvent(ev); } } 

El buscapersonas externo comienza a desplazarse hacia la izquierda solo cuando el buscapersonas interno está en la última página. Y viceversa.

Acabo de probar este caso, puedes hacerlo sin trabajar extra, debajo está mi demo

 public class MainActivity extends AppCompatActivity { public static final String TAG = "TAG"; ViewPager parentPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); initViews(); initData(); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); } private void initViews() { parentPager = (ViewPager) findViewById(R.id.parent_pager); } private void initData() { List pagers = new ArrayList(); for(int j = 0; j < 3; j++) { List list = new ArrayList(); for (int i = 0; i < 5; i++) { LinearLayout layout = new LinearLayout(this); TextView textView = new TextView(this); textView.setText("This is the" + i + "th page in PagerItem" + j); layout.addView(textView); textView.setGravity(Gravity.CENTER); LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams(); params.gravity = Gravity.CENTER; list.add(layout); } MyViewPagerAdapter adapter = new MyViewPagerAdapter(list); final ViewPager childPager = (ViewPager) LayoutInflater.from(this).inflate(R.layout.child_layout, null).findViewById(R.id.child_pager); childPager.setAdapter(adapter); childPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { Log.d(TAG, "onPageScrolled: position: " + position + ", positionOffset: " + positionOffset); } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { } }); pagers.add(childPager); } MyParentViewPagerAdapter parentAdapter = new MyParentViewPagerAdapter(pagers); parentPager.setAdapter(parentAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } class MyViewPagerAdapter extends PagerAdapter { private List data; public MyViewPagerAdapter(List data) { this.data = data; } @Override public int getCount() { return data.size(); } @Override public int getItemPosition(Object object) { return data.indexOf(object); } @Override public Object instantiateItem(ViewGroup container, int position) { LinearLayout linearLayout = data.get(position); container.addView(linearLayout); return data.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { LinearLayout layout = data.get(position); container.removeView(layout); layout = null; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } } class MyParentViewPagerAdapter extends PagerAdapter { private List data; public MyParentViewPagerAdapter(List data) { this.data = data; } @Override public int getCount() { return data.size(); } @Override public int getItemPosition(Object object) { return data.indexOf(object); } @Override public Object instantiateItem(ViewGroup container, int position) { ViewPager pager = data.get(position); if(pager.getParent() != null) { ((ViewGroup) pager.getParent()).removeView(pager); } container.addView(pager); return data.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { ViewPager pager = data.get(position); container.removeView(pager); pager = null; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } } } 

El xml es simple, ViewPager externo en mi diseño principal y ViewPager interno en otro LinearLayout