ViewPager z granicami poprzedniej i następnej strony

144

Projektuję widok z wieloma stronami. Chcę, aby krawędzie poprzednich i następnych stron były wyświetlane jak poniżej i zaimplementuj przesunięcie 2 palcami, aby przełączać się między stronami.

wprowadź opis obrazu tutaj

Próbowałem używać ViewPagerz negatywnym marginesem strony, jak sugerowano tutaj, ale pokazuje to tylko jedną krawędź na ekranie, a nie obie jednocześnie.

Alternatywnie, czy istnieje sposób, w jaki mogę umieścić część widoku poza ekranem, a następnie ożywić go, nadając mu ViewPagerefekt czcionki.

Jak mam się do tego zabrać? Dzięki !

Gaurav Arora
źródło
„pokazuje tylko jedną krawędź na ekranie, a nie obie jednocześnie”. Czy jesteś na stronie 0 i widzisz tylko część strony 1? Być może będziesz musiał użyć np. Okrągłego pagera, a następnie ustawiać stronę zawsze w „środkowej” pozycji. Zobacz ten post i komentarz: stackoverflow.com/a/8304474/1851478
logray

Odpowiedzi:

100

Cytując siebie z wpisu na blogu na ten temat :

Trzecie podejście pochodzi od Dave'a Smitha, współautora cenionej książki Android Recipes. Poszedł w zupełnie innym kierunku, używając niestandardowego kontenera, który uniemożliwiał dzieciom wyświetlanie wycinania więcej niż jednej strony na raz.

Jego opublikowany przykładowy kod pokazuje całość w akcji. Jego kontener ( com.example.pagercontainer.PagerContainer) zawija ViewPageri wywołuje setClipChildren(false);siebie, więc nawet jeśli ViewPagerjest skoncentrowany na jednej wybranej stronie, inne strony, które mają współrzędne poza ViewPagergranicami, są nadal widoczne, o ile mieszczą się w PagerContainer. Przez doborze ViewPagerbyć mniejsza niż PagerContainerThe ViewPagerrozmiar może jej strony do tej wielkości, pozostawiając miejsce na innych stronach widać. PagerContainerjednak musi trochę pomóc w przypadku zdarzeń dotykowych, ponieważ ViewPagerobsługuje tylko zdarzenia przesunięcia we własnych widocznych granicach, ignorując wszystkie strony widoczne po bokach.

wprowadź opis obrazu tutaj

CommonsWare
źródło
1
Używając tego, jestem w stanie pokazać część poprzedniej i następnej strony, jak pokazano na powyższym obrazku, ale teraz nie chcę pokazywać ostrych krawędzi na obrazach, chcę, aby rozmazały się w kierunku krawędzi ... proszę, poprowadź mnie, w jaki sposób Używam indeksu z do osiągnięcia tego samego
Shruti,
2
@Shruti - po prostu dodaj obraz nakładki z efektem, który chcesz
Daniel L.
2
Robię to samo, ale wyłącza efekt przewijania ostatniego elementu. Jakieś wskazówki na ten temat?
Swayam
1
@CommonsWare: Sir, wypróbowałem twoje rozwiązanie! Działało całkiem nieźle. Jest tam przeskok. Jedynym problemem jest teraz to, że wyświetla się następna karta, ale nie poprzednia. To znaczy, jeśli jestem na stronie 2, widzę, jak wygląda strona 3, ale nie strona 1. Gdzie mogłem się pomylić?
Swayam
2
@Swayam: Nie mam pojęcia.
CommonsWare
110

Mam podobne rozwiązanie:

W viewpager ustaw dopełnienie lewe i prawe, np. 20dp. Ustaw również margines strony na stronie podglądu, np. Połowę wypełnienia pagera. I nie zapomnij wyłączyć wypełniania klipów.

tilePager.setPadding(defaultGap, 0, defaultGap, 0);
tilePager.setClipToPadding(false);
tilePager.setPageMargin(halfGap);
Thomas R.
źródło
2
Zapewniono dobre rozwiązanie.
akash89
najłatwiejszy i najlepszy sposób
HannahCarney
To jest bestia, tak, bestia, na którą należy
zwrócić
1
uwaga boczna: to nie zadziała z niestandardowym transformatorem pagera widoku
voytez
@voytez jakieś rozwiązanie dla transformatora?
Alex
76
  1. Ustaw dopełnienie z lewej i prawej strony dla widoku całego elementu. Przykład 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. Następnie ustaw ujemny margines strony na PageViewrówny 2 * (dopełnienie poprzedniego widoku)

    int margin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());
    mViewPager.setPageMargin(-margin);
  3. Opcjonalny. Ustaw zero dopełnienia z lewej strony dla pierwszego elementu i zerowe dopełnienie z prawej strony dla ostatniego elementu, aby ukryć puste krawędzie. Możesz to zrobić w klasie PageAdapterlub Pagefragment.

Siergiej A.
źródło
@Sergey, nie mogę tego zrobić z twoim rozwiązaniem, czy możesz opublikować przykład? thx
Marckaraujo
12
po prostu dodawanie notatki: dzięki temu rozwiązaniu podczas przesuwania się ze strony 1 na stronę 2 strona 3 nie jest w pamięci, więc pojawi się z opóźnieniem. aby to naprawić po prostu dodaj - yourViewPager.setOffscreenPageLimit (2);
José Barbosa
Robię to samo, ale wyłącza efekt przewijania ostatniego elementu. Jakieś wskazówki na ten temat?
Swayam
Wydaje się, że to też nie działa ... marginesy wydają się być wyświetlane losowo, jeśli używam obrazów ze skalą ustawioną na środkowe przycięcie. Czy ktoś ma działający przykład kodu, którym może się podzielić?
kenyee
2
Jak dotknąć pierwszej i ostatniej pozycji? Sprawdzając indeks strony w OnPageListener?
Hardik9850
47

Aby wyświetlić podgląd lewej i prawej strony, ustaw następujące dwie wartości

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

Jeśli potrzebujesz odstępu między dwiema stronami w viewpager, dodaj viewpager.setPageMargin (int)

Android ViewPager - Pokaż podgląd strony po lewej i prawej stronie

molu2008
źródło
3
To powinna być poprawna odpowiedź. Myślę, że może to nie działało we wcześniejszych wersjach programu Viewpager, ale teraz działa.
Greg Ennis
Dodaje również ten sam margines po lewej stronie pierwszej i prawej strony ostatniej strony. Wszelkie poprawki
Umesh Aawte
1
Krótka i bardziej jasna odpowiedź.
Imran Ahmed
10

jeśli ktoś wciąż szuka rozwiązania, dostosowałem ViewPage, aby osiągnąć to bez użycia ujemnego marginesu, przykładowy projekt znajdziesz tutaj https://github.com/44kksharma/Android-ViewPager-Carousel-UI powinien działać w większości przypadków ale Ty nadal można zdefiniować margines strony za pomocą mPager.setPageMargin(margin in pixel);

44kksharma
źródło
1

Pobierz kod źródłowy stąd ( ViewPager z granicami poprzedniej i następnej strony )

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
źródło
1
Ten kod nie działa poprawnie, jego lewa strona jest nieco większa niż prawa
Chirag Joshi
1

Jakiś czas temu potrzebowałem takiej funkcji i przygotowałem malutką bibliotekę, która używa RecyclerViewz PagerSnapHelper (dodana w wersji 25.1.0 biblioteki obsługi v7) zamiast klasycznej ViewPager:

MetalRecyclerPagerView - możesz tam znaleźć cały kod wraz z przykładami.

Głównie składa się z jednego pliku klasy: MetalRecyclerViewPager.java (oraz dwóch plików xml: attrs.xml i ids.xml ).

Mam nadzieję, że to komuś pomoże :)

Alexander Bilchuk
źródło