具有前一页和后一页边界的ViewPager

150

我正在设计一个包含多个页面的视图,希望能够像下面一样显示前一页和后一页的边缘,并且可以通过双指滑动来切换页面。

图片描述

我尝试使用 ViewPager 并设置负页边距,就像这里所建议的那样,但只能同时在屏幕上显示其中一个页面的边缘,无法同时显示前后两个页面的边缘。

或者,是否有任何方式可以将我的视图部分位置超出屏幕以实现类似于 ViewPager 的效果?

应该如何操作?谢谢!


仅在屏幕上显示一个边缘,而不是同时显示两个。你是否在第0页,只看到第1页的一部分?也许你需要使用循环翻页器,例如并将页面始终设置为“中间”位置。请参见此帖子和评论:https://dev59.com/jGsz5IYBdhLWcg3w9ssQ#8304474 - logray
8个回答

118

我有一个类似的解决方案:

在viewpager上设置左右padding,例如20dp。还要在viewpager上设置页面边距,例如pager padding的一半。不要忘记禁用clip padding。

tilePager.setPadding(defaultGap, 0, defaultGap, 0);
tilePager.setClipToPadding(false);
tilePager.setPageMargin(halfGap);

3
提供了一个好的解决方案。 - akash89
最简单和最好的方法 - HannahCarney
这是考虑命名值的最佳答案,xd。 - silentsudo
1
附注:这将无法与自定义视图页面转换器一起使用。 - voytez
@voytez,Transformer 的解决方案有吗? - Alex

102

以下是我从关于此主题的博客文章中引用自己的话:

第三种方法来自Dave Smith,他是备受推崇的书籍Android Recipes的合著者。他采用了非常不同的方式,使用一个自定义容器禁用子项裁剪以显示多个页面。

他的发布的示例代码展示了整个过程。他的容器(com.example.pagercontainer.PagerContainer)包装了ViewPager并在自身上调用setClipChildren(false); ,因此即使ViewPager专注于所选的一个页面,其他页面如果其坐标超出了ViewPager边界但仍适合于PagerContainer,则仍然可见。通过将ViewPager的大小设置为小于PagerContainer的大小,ViewPager可以将其页面大小调整为该大小,为其他页面腾出空间。不过,PagerContainer还需要在触摸事件方面帮助一下,因为ViewPager只会处理其自身可见范围内的滑动事件,而忽略任何在旁边可见的页面。

输入图像描述


1
通过使用这个,我能够像上面的图像一样展示前一页和后一页的部分内容,但现在我不想在图像上显示锐利的边缘。我希望它们向边缘模糊...请指导我如何使用z-index来实现相同的效果。 - Shruti
2
@Shruti - 只需添加一个具有所需效果的覆盖图像。 - Daniel L.
2
我也是这样做的,但它会禁用最后一个项目的过度滚动效果。有什么线索吗? - Swayam
1
@CommonsWare:先生,我尝试了您的解决方案!它运行得非常好。现在唯一的问题是下拉滚动有了,但接下来的卡片可以看到,而前面的卡片却看不到。也就是说,如果我在第2页,我可以看到第3页,但看不到第1页。我可能做错了什么? - Swayam
2
@Swayam:我不知道。 - CommonsWare
显示剩余20条评论

76
  1. 为整个项目视图设置左右内边距。示例 XML (page_item.xml):

  2. <?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>
    
  3. 然后将PageView的负页面边距设置为2 *(前一个视图填充)

  4. int margin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());
    mViewPager.setPageMargin(-margin);
    
  5. 可选。在第一个项目中设置零左填充,并在最后一个项目中设置零右填充以隐藏空边缘。您可以在PageAdapterPage片段类中完成此操作。


@Sergey,我无法使用您的解决方案使其工作,您能否发布一个示例?谢谢。 - Marckaraujo
12
添加一条注释:使用这种解决方案,当您从页面1滑动到页面2时,页面3不会保留在内存中,因此会出现延迟。要解决这个问题,只需添加 - yourViewPager.setOffscreenPageLimit(2); - José Barbosa
我也是这样做的,但它会禁用最后一个项目的过度滚动效果。有什么线索吗? - Swayam
我似乎也无法让它工作...如果我使用设置为居中裁剪的图像,边距似乎会随机显示。 有人可以分享一个可行的代码示例吗? - kenyee
2
如何触摸第一个和最后一个项目?通过在OnPageListener中检查页面索引吗? - hardik9850
当我尝试缩放中心布局时,它会缩放到顶部和底部。由于填充,左侧和右侧不可见。请参见以下问题: https://stackoverflow.com/questions/52710076/android-pinch-to-zoom-layout-inside-a-viewpager-with-padding-left-and-right - Anooj Krishnan G

49
要显示左右页面的预览,请设置以下两个值。
viewpager.setClipToPadding(false)
viewpager.setPadding(left,0,right,0)

如果您需要在ViewPager中的两个页面之间添加空白,则可以添加viewpager.setPageMargin(int)。 Android ViewPager-在左侧和右侧显示页面预览

3
这应该是正确的答案。我认为在以前的ViewPager版本中可能无法使用,但现在可以使用。 - Greg Ennis
它在第一页的左侧和最后一页的右侧添加了相同的边距。有什么解决方法吗? - Umesh Aawte
2
简短且更清晰的答案。 - Imran Ahmed

10
如果有人仍在寻找解决方案,我已经定制了ViewPage以实现没有使用负边距的效果,在此处可以找到一个示例项目https://github.com/44kksharma/Android-ViewPager-Carousel-UI。 在大多数情况下它应该能够工作,但你还可以用mPager.setPageMargin(像素中的边距);来定义页面边距。


谢谢。如何增加页面之间的间距? - Saeid Z
mPager.setPageMargin(边距像素值); - 44kksharma

1

从这里下载源代码(带有前后页边界的ViewPager)

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;
    }
}

1
这段代码没有正常工作,它显示左侧页面比右侧页面大一点。 - Chirag Joshi

1

有一段时间前,我需要这样的功能并准备了一个小型库,它使用了 RecyclerViewPagerSnapHelper(在 v7 支持库的 25.1.0 版本中添加)代替传统的 ViewPager

MetalRecyclerPagerView - 您可以在那里找到所有代码和示例。

主要由一个单独的类文件组成:MetalRecyclerViewPager.java(以及两个 xml 文件:attrs.xmlids.xml)。

希望能对某些人有所帮助 :)


0

轮播 ViewPager 片段

    ViewPager viewPager = findViewById(R.id.viewPager);
    TabPagerAdapter tabPagerAdapter = new TabPagerAdapter(this,getSupportFragmentManager());
    viewPager.setAdapter(tabPagerAdapter);
    // Disable clip to padding
    viewPager.setClipToPadding(false);
    // set padding manually, the more you set the padding the more you see of prev & next page
    viewPager.setPadding(40, 0, 40, 0);
    // sets a margin b/w individual pages to ensure that there is a gap b/w them
    viewPager.setPageMargin(20);

Carousel ViewPager


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接