ViewPager con límites de página anteriores y siguientes

144

Estoy diseñando una vista con varias páginas. Quiero que los bordes de las páginas anteriores y siguientes se muestren como a continuación e implemente deslizar dos dedos para cambiar de página.

ingrese la descripción de la imagen aquí

Intenté usar ViewPagercon un margen de página negativo como se sugiere aquí, pero eso solo muestra uno de los bordes en la pantalla, no ambos simultáneamente.

Alternativamente, ¿hay alguna manera de que pueda colocar parte de mi vista fuera de la pantalla y luego animarla dándole un ViewPagerefecto de tipo?

¿Cómo debo hacerlo? Gracias !

Gaurav Arora
fuente
"solo muestra uno de los bordes en la pantalla, no ambos simultáneamente". ¿Estás en la página 0 y solo ves parte de la página 1? Quizás necesite usar un buscapersonas circular, por ejemplo, y luego establecer su página siempre en la posición "intermedia". Vea esta publicación y el comentario: stackoverflow.com/a/8304474/1851478
logray el

Respuestas:

100

Citando a mí mismo de una publicación de blog sobre este tema :

El tercer enfoque proviene de Dave Smith, coautor del conocido libro Android Recipes. Se dirigió en una dirección muy diferente, utilizando un contenedor personalizado que deshabilitaba el recorte de los niños para mostrar más de una página a la vez.

Su código de muestra publicado muestra todo en acción. Su contenedor ( com.example.pagercontainer.PagerContainer) envuelve el ViewPagery se llama setClipChildren(false);a sí mismo, por lo que a pesar de que ViewPagerestá enfocado en una página seleccionada, otras páginas que tienen coordenadas más allá de los ViewPagerlímites siguen siendo visibles, siempre que se ajusten dentro del PagerContainer. Al dimensionar el tamaño ViewPagerpara que sea más pequeño que el PagerContainer, ViewPagerpueden cambiar el tamaño de sus páginas a ese tamaño, dejando espacio para que se vean otras páginas. PagerContainer, sin embargo, debe ayudar un poco con los eventos táctiles, ya ViewPagerque solo manejará los eventos de deslizamiento en sus propios límites visibles, ignorando las páginas visibles a los lados.

ingrese la descripción de la imagen aquí

CommonsWare
fuente
1
Al usar esto, puedo mostrar parte de la página anterior y siguiente como se muestra en la imagen de arriba, pero ahora no quiero mostrar bordes nítidos en las imágenes. Quiero que se desdibujen hacia los bordes ... por favor, guíenme sobre cómo uso el índice z para lograr lo mismo
Shruti
2
@Shruti: solo agregue una imagen superpuesta con el efecto que desee
Daniel L.
2
Hago lo mismo pero deshabilita el efecto de desplazamiento excesivo para el último elemento. ¿Alguna pista sobre eso?
Swayam
1
@CommonsWare: Señor, ¡probé su solución! Funcionó bastante bien. El desplazamiento está ahí. El único problema ahora es que se muestra la siguiente carta, pero no la anterior. Es decir, si estoy en la página 2, puedo ver la página 3 asomándose, pero no la página 1. ¿Dónde podría haber ido mal?
Swayam
2
@Swayam: No tengo idea.
CommonsWare
110

Tengo una solución similar:

En el visor, configure el relleno izquierdo y derecho, por ejemplo, 20dp. También configure el margen de la página en el visor, por ejemplo, la mitad del relleno del localizador. Y no olvide deshabilitar el relleno del clip.

tilePager.setPadding(defaultGap, 0, defaultGap, 0);
tilePager.setClipToPadding(false);
tilePager.setPageMargin(halfGap);
Thomas R.
fuente
2
Buena solución proporcionada.
akash89
la manera más fácil y mejor
HannahCarney
Esta es la respuesta de bestia sí bestia para considerar nombrar valores xd
silentsudo
1
nota al margen: esto no funcionará con un transformador de buscapersonas de vista personalizada
voytez
@voytez alguna solución para transformador?
Alex
76
  1. Establezca el relleno izquierdo y derecho para ver todo el elemento. Ejemplo xml (page_item.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:orientation="vertical"
    android:paddingLeft="20dp"
    android:paddingRight="20dp"/>
    
    <TextView
        android:id="@+id/text1"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    
    </LinearLayout>
  2. Luego establezca un margen de página negativo PageViewigual a 2 * (relleno de vista anterior)

    int margin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());
    mViewPager.setPageMargin(-margin);
  3. Opcional. Establezca cero margen izquierdo para el primer elemento y cero margen derecho para el último elemento para ocultar los bordes vacíos. Puedes hacer esto en elPageAdapterPage clase o fragment.

SergeyA
fuente
@Sergey, no puedo hacer que esto funcione con tu solución, ¿podrías publicar un ejemplo? thx
Marckaraujo
12
simplemente agregando una nota: con esta solución cuando se desliza de la página 1 a la página 2, la página 3 no está en la memoria, por lo que aparecerá con retraso. para arreglar esto simplemente agregue - yourViewPager.setOffscreenPageLimit (2);
José Barbosa
Hago lo mismo pero deshabilita el efecto de desplazamiento excesivo para el último elemento. ¿Alguna pista sobre eso?
Swayam
Parece que tampoco puedo hacer que esto funcione ... los márgenes parecen mostrarse al azar si uso imágenes con escala ajustada para recortar al centro. ¿Alguien tiene un código de trabajo que pueda compartir?
kenyee
2
¿Cómo tocar el primer y último elemento? ¿Al verificar el índice de la página en OnPageListener?
Hardik9850
47

Para mostrar la vista previa de las páginas izquierda y derecha, configure los siguientes dos valores

viewpager.setClipToPadding(false)
viewpager.setPadding(left,0,right,0)

Si necesita espacio entre dos páginas en el visor, agregue viewpager.setPageMargin (int)

Android ViewPager: muestra la vista previa de la página a izquierda y derecha

molu2008
fuente
3
Esta debería ser la respuesta correcta. Creo que tal vez esto no funcionó en versiones anteriores de viewpager pero funciona ahora.
Greg Ennis
También agrega el mismo margen en el lado izquierdo del primer y el lado derecho de la última página. Cualquier solución
Umesh Aawte
1
Respuesta corta y más clara.
Imran Ahmed
10

si alguien sigue buscando una solución, había personalizado el ViewPage para lograrlo sin usar un margen negativo, encuentre un proyecto de muestra aquí https://github.com/44kksharma/Android-ViewPager-Carousel-UI debería funcionar en la mayoría de los casos, pero usted todavía puede definir el margen de la página con mPager.setPageMargin(margin in pixel);

44kksharma
fuente
1

Descargue el código fuente desde aquí ( ViewPager con límites de página anteriores y siguientes )

MainActivity.java

package com.deepshikha.viewpager;

import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends FragmentActivity {

    ViewPager pager;
    MyPageAdapter obj_adapter;
    String str_device;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();


    }

    private void init() {
        pager = (ViewPager) findViewById(R.id.viewpager);
        differentDensityAndScreenSize(getApplicationContext());
        List<Fragment> fragments = getFragments();
        pager.setAdapter(obj_adapter);
        pager.setClipToPadding(false);


        if (str_device.equals("normal-hdpi")){
            pager.setPadding(160, 0, 160, 0);
        }else if (str_device.equals("normal-mdpi")){
            pager.setPadding(160, 0, 160, 0);
        }else if (str_device.equals("normal-xhdpi")){
            pager.setPadding(160, 0, 160, 0);
        }else if (str_device.equals("normal-xxhdpi")){
            pager.setPadding(180, 0, 180, 0);
        }else if (str_device.equals("normal-xxxhdpi")){
            pager.setPadding(180, 0, 180, 0);
        }else if (str_device.equals("normal-unknown")){
            pager.setPadding(160, 0, 160, 0);
        }else {

        }

        obj_adapter = new MyPageAdapter(getSupportFragmentManager(), fragments);
        pager.setPageTransformer(true, new ExpandingViewPagerTransformer());
        pager.setAdapter(obj_adapter);
    }

    class MyPageAdapter extends FragmentPagerAdapter {

        private List<Fragment> fragments;

        public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {

            super(fm);

            this.fragments = fragments;

        }

        @Override

        public Fragment getItem(int position) {

            return this.fragments.get(position);

        }

        @Override

        public int getCount() {

            return this.fragments.size();

        }

    }

    private List<Fragment> getFragments() {

        List<Fragment> fList = new ArrayList<Fragment>();

        fList.add(MyFragment.newInstance("Fragment 1",R.drawable.imags));
        fList.add(MyFragment.newInstance("Fragment 2",R.drawable.image1));
        fList.add(MyFragment.newInstance("Fragment 3",R.drawable.image2));
        fList.add(MyFragment.newInstance("Fragment 4",R.drawable.image3));
        fList.add(MyFragment.newInstance("Fragment 5",R.drawable.image4));

        return fList;

    }

    public int differentDensityAndScreenSize(Context context) {
        int value = 20;
        String str = "";
        if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    str = "small-ldpi";
                    // Log.e("small 1","small-ldpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    str = "small-mdpi";
                    // Log.e("small 1","small-mdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    str = "small-hdpi";
                    // Log.e("small 1","small-hdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    str = "small-xhdpi";
                    // Log.e("small 1","small-xhdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    str = "small-xxhdpi";
                    // Log.e("small 1","small-xxhdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    str = "small-xxxhdpi";
                    //Log.e("small 1","small-xxxhdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    str = "small-tvdpi";
                    // Log.e("small 1","small-tvdpi");
                    value = 20;
                    break;
                default:
                    str = "small-unknown";
                    value = 20;
                    break;
            }

        } else if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    str = "normal-ldpi";
                    // Log.e("normal-ldpi 1","normal-ldpi");
                    str_device = "normal-ldpi";
                    value = 82;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    // Log.e("normal-mdpi 1","normal-mdpi");
                    str = "normal-mdpi";
                    value = 82;
                    str_device = "normal-mdpi";
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    // Log.e("normal-hdpi 1","normal-hdpi");
                    str = "normal-hdpi";
                    str_device = "normal-hdpi";
                    value = 82;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    //Log.e("normal-xhdpi 1","normal-xhdpi");
                    str = "normal-xhdpi";
                    str_device = "normal-xhdpi";
                    value = 90;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    // Log.e("normal-xxhdpi 1","normal-xxhdpi");
                    str = "normal-xxhdpi";
                    str_device = "normal-xxhdpi";
                    value = 96;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    //Log.e("normal-xxxhdpi","normal-xxxhdpi");
                    str = "normal-xxxhdpi";
                    str_device = "normal-xxxhdpi";
                    value = 96;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    //Log.e("DENSITY_TV 1","normal-mdpi");
                    str = "normal-tvdpi";
                    str_device = "normal-tvmdpi";
                    value = 96;
                    break;
                default:
                    // Log.e("normal-unknown","normal-unknown");
                    str = "normal-unknown";
                    str_device = "normal-unknown";
                    value = 82;
                    break;
            }
        } else if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    str = "large-ldpi";
                    // Log.e("large-ldpi 1","normal-ldpi");
                    value = 78;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    str = "large-mdpi";
                    //Log.e("large-ldpi 1","normal-mdpi");
                    value = 78;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    //Log.e("large-ldpi 1","normal-hdpi");
                    str = "large-hdpi";
                    value = 78;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    // Log.e("large-ldpi 1","normal-xhdpi");
                    str = "large-xhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    //Log.e("large-ldpi 1","normal-xxhdpi");
                    str = "large-xxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    // Log.e("large-ldpi 1","normal-xxxhdpi");
                    str = "large-xxxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    //Log.e("large-ldpi 1","normal-tvdpi");
                    str = "large-tvdpi";
                    value = 125;
                    break;
                default:
                    str = "large-unknown";
                    value = 78;
                    break;
            }

        } else if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    // Log.e("large-ldpi 1","normal-ldpi");
                    str = "xlarge-ldpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    // Log.e("large-ldpi 1","normal-mdpi");
                    str = "xlarge-mdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    //Log.e("large-ldpi 1","normal-hdpi");
                    str = "xlarge-hdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    // Log.e("large-ldpi 1","normal-hdpi");
                    str = "xlarge-xhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    // Log.e("large-ldpi 1","normal-xxhdpi");
                    str = "xlarge-xxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    // Log.e("large-ldpi 1","normal-xxxhdpi");
                    str = "xlarge-xxxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    //Log.e("large-ldpi 1","normal-tvdpi");
                    str = "xlarge-tvdpi";
                    value = 125;
                    break;
                default:
                    str = "xlarge-unknown";
                    value = 125;
                    break;
            }
        }

        return value;
    }
}
Deepshikha Puri
fuente
1
Este código no funciona correctamente, muestra la página del lado izquierdo un poco más grande que el lado derecho
Chirag Joshi
1

Hace algún tiempo necesitaba esa característica y preparé una pequeña biblioteca que se usa RecyclerViewcon PagerSnapHelper (agregado en la versión 25.1.0 de la biblioteca de soporte v7) en lugar del clásico ViewPager:

MetalRecyclerPagerView : puede encontrar todo el código junto con ejemplos allí.

Principalmente consiste en un único archivo de clase: MetalRecyclerViewPager.java (y dos xmls: attrs.xml y ids.xml ).

Espero que ayude a alguien :)

Alexander Bilchuk
fuente