ViewPager com limites de página anterior e seguinte

144

Estou projetando uma exibição com várias páginas. Quero que as bordas das páginas anteriores e seguintes sejam mostradas como abaixo e implemente um furto de dois dedos para alternar entre as páginas.

insira a descrição da imagem aqui

Tentei usar ViewPagercom margem de página negativa, conforme sugerido aqui, mas isso mostra apenas uma das bordas da tela, não as duas simultaneamente.

Como alternativa, existe alguma maneira de posicionar parte da minha visão fora da tela e animá-la, dando-lhe um ViewPagerefeito de tipo.

Como devo fazer isso? Obrigado !

Gaurav Arora
fonte
"mostra apenas uma das bordas da tela, não as duas simultaneamente." Você está na página 0 e vê apenas parte da página 1? Talvez você precise usar um pager circular, por exemplo, e depois definir sua página sempre para a posição "intermediária". Veja esta postagem e o comentário: stackoverflow.com/a/8304474/1851478
logray

Respostas:

100

Citando-me de uma postagem de blog sobre este assunto :

A terceira abordagem vem de Dave Smith, co-autor do conceituado livro Android Recipes. Ele foi em uma direção muito diferente, usando um contêiner personalizado que desativava o recorte de crianças para mostrar mais de uma página por vez.

Seu código de amostra publicado mostra tudo em ação. Seu container ( com.example.pagercontainer.PagerContainer) envolve ViewPagere chama setClipChildren(false);a si próprio, portanto, embora o ViewPagerfoco esteja em uma página selecionada, outras páginas que têm coordenadas além dos ViewPagerlimites ainda são visíveis, desde que se encaixem no PagerContainer. Ao dimensionar ViewPagerpara que seja menor que o PagerContainer, a ViewPagerlata pode dimensionar suas páginas para esse tamanho, deixando espaço para outras páginas serem vistas. PagerContainer, no entanto, precisa ajudar um pouco com os eventos de toque, pois ViewPagersó manipulará eventos de furto em seus próprios limites visíveis, ignorando as páginas visíveis para os lados.

insira a descrição da imagem aqui

CommonsWare
fonte
1
usando isso, sou capaz de mostrar parte da página anterior e da página seguinte, como mostra a imagem acima, mas agora não quero mostrar bordas nítidas nas imagens. i usar z-índice para atingir o mesmo
Shruti
2
@Shruti - basta adicionar uma imagem sobreposta com o efeito desejado
Daniel L.
2
Eu faço o mesmo, mas desativa o efeito de rolagem excessiva para o último item. Alguma pista sobre isso?
Swayam
1
@ CommmonsWare: Senhor, eu tentei sua solução! Funcionou muito bem. A superlotação está lá. O único problema agora é que o próximo cartão é exibido, mas não o anterior. Ou seja, se eu estiver na página 2, posso ver a página 3 aparecendo, mas não a página 1. Onde eu poderia estar errado?
Swayam
2
@Swayam: Não faço ideia.
CommonsWare
110

Eu tenho uma solução semelhante:

No visor, defina o preenchimento esquerdo e direito, por exemplo, 20dp. Defina também a margem da página no visor, por exemplo, metade do preenchimento do pager. E não se esqueça de desativar o preenchimento de clipe.

tilePager.setPadding(defaultGap, 0, defaultGap, 0);
tilePager.setClipToPadding(false);
tilePager.setPageMargin(halfGap);
Thomas R.
fonte
2
Boa solução fornecida.
akash89
mais fácil e melhor maneira
HannahCarney
Esta é a resposta besta fera sim para considerar valores de nomeação xd
silentsudo
1
nota: este não vai funcionar com um transformador de exibição personalizada pager
voytez
@voytez alguma solução para transformador?
Alex
76
  1. Defina o preenchimento esquerdo e direito para a exibição de itens inteiros. Exemplo de 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. Em seguida, defina a margem negativa da página para PageViewigual a 2 * (preenchimento de visualização anterior)

    int margin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());
    mViewPager.setPageMargin(-margin);
    
  3. Opcional. Defina zero preenchimento esquerdo para o primeiro item e zero preenchimento direito como último item para ocultar bordas vazias. Você pode fazer isso na classe PageAdapterou Pagefragment.

SergeyA
fonte
@ Emery, não posso fazer isso funcionar com sua solução, você poderia postar um exemplo? thx
Marckaraujo
12
basta adicionar uma observação: com esta solução quando você desliza da página 1 para a página 2, a página 3 não está na memória e, portanto, aparece com um atraso. para corrigir isso basta adicionar - yourViewPager.setOffscreenPageLimit (2);
José Barbosa
Eu faço o mesmo, mas desativa o efeito de rolagem excessiva para o último item. Alguma pista sobre isso?
Swayam
Também não consigo fazer com que isso funcione ... as margens parecem ser exibidas aleatoriamente se eu usar imagens com escala definida para cortar o centro. Alguém tem um exemplo de código funcional que pode compartilhar?
Kenyee
2
Como tocar no primeiro e no último item? Verificando o índice da página no OnPageListener?
Hardik9850
47

Para mostrar a visualização das páginas esquerda e direita, defina os dois valores a seguir

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

Se você precisar de espaço entre duas páginas no viewpager, adicione viewpager.setPageMargin (int)

Android ViewPager - Mostrar pré-visualização da página à esquerda e à direita

molu2008
fonte
3
Esta deve ser a resposta correta. Eu acho que talvez isso não funcionou em versões anteriores do viewpager, mas funciona agora.
Greg Ennis
É adicionar a mesma margem no lado esquerdo da primeira e no lado direito da última página. Qualquer correção
Umesh Aawte
1
Resposta curta e mais clara.
Imran Ahmed
10

se alguém ainda está procurando a solução, eu personalizei o ViewPage para alcançá-lo sem usar margem negativa, encontre um projeto de exemplo aqui https://github.com/44kksharma/Android-ViewPager-Carousel-UI, ele deve funcionar na maioria dos casos, mas você ainda pode definir a margem da página com mPager.setPageMargin(margin in pixel);

44kksharma
fonte
1

Faça o download do código fonte aqui ( ViewPager com os limites da página anterior e da próxima )

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
fonte
1
Este código não está funcionando corretamente, a sua página lado esquerdo mostrando pouco maior do que o lado direito
Chirag Joshi
1

Algum tempo atrás, eu precisava desse recurso e preparei uma pequena biblioteca que usa RecyclerViewcom o PagerSnapHelper (adicionada na versão 25.1.0 da biblioteca de suporte da v7) em vez da clássica ViewPager:

MetalRecyclerPagerView - você pode encontrar todo o código junto com exemplos.

Principalmente, consiste de um único arquivo de classe: MetalRecyclerViewPager.java (e dois xmls: attrs.xml e ids.xml ).

Espero que ajude alguém :)

Alexander Bilchuk
fonte