ViewPager可以在每个页面中拥有多个视图吗?

46

尝试过相册和水平滚动视图之后,我发现 View Pager 可以满足我的需求,但有一点小问题。View Pager 是否可以每页显示多个视图?

我知道 View Pager 每次滑动只能显示一个视图/页。我想知道是否可以限制我的视图宽度,这样紧随其后的第二个视图也会显示出来?

例如:我有3个视图,我希望屏幕显示视图1和部分视图2,这样用户就知道还有更多内容,可以向右滑动查看视图2。

|view 1|view 2|view 3|
|screen   |

你尝试过做一个模型来看看这是否有效吗?似乎只需要编码并尝试一下就不会花费太长时间。 - dymmeh
View Pager 本身将每个视图限制为整个屏幕。 我发布这篇文章的原因是想了解是否有人对 View Pager 有经验,并且是否可以在同一屏幕上显示两个以上的视图。 - CLDev
1
使用此链接,并覆盖方法onInterceptTouchEvent,在那里返回false。 - android developer
9个回答

61

我发现了一个可能更简单的解决方案,可以通过为ViewPager指定负边距来实现。我在GitHub上创建了MultiViewPager项目,您可能想要查看一下:

https://github.com/Pixplicity/MultiViewPager

尽管MultiViewPager需要一个子视图来指定维度,但其原则在于设置页面边距。
ViewPager.setPageMargin(
    getResources().getDimensionPixelOffset(R.dimen.viewpager_margin));

然后我在我的 dimens.xml 文件中指定了这个尺寸:

<dimen name="viewpager_margin">-64dp</dimen>

为了补偿重叠页面,每个页面的内容视图都有相反的边距:
android:layout_marginLeft="@dimen/viewpager_margin_fix"
android:layout_marginRight="@dimen/viewpager_margin_fix"

再次在 dimens.xml 中:

<dimen name="viewpager_margin_fix">32dp</dimen>

注意,viewpager_margin_fix 维度是绝对 viewpager_margin 维度的一半。
我们在荷兰报纸应用程序 De Telegraaf Krant 中实现了这个

Phone example in De Telegraaf KrantTablet example


1
我无法使用您的解决方案使其工作,您能否提供一个示例?谢谢。 - Marckaraujo
请注意,您正在指定“重叠”而不是“间隙”。该维度以绝对值的形式提供。如果您希望在设备类之间指定不同数量的重叠,请使用资源限定符进行设置。 - Paul Lammertsma
3
很棒的技术,但不幸的是适配器的itemSelected(int position)方法总是在我点击居中页面两侧的任一页面时触发,其位置始终为居中页面的位置。 - Oliver Pearmain
@HaggleLad 你需要自己编写切换页面的逻辑。ViewPager 只是显示片段;它无法猜测你想要做什么。一个简单的解决方案是拦截触摸事件,检查点击区域是否属于当前居中的页面。 - Paul Lammertsma
1
@NullPointer,很高兴你喜欢它!随意分享! - Paul Lammertsma
显示剩余18条评论

32

Mark Murphy在他的一篇有关这个问题的博客文章中提出了一个有趣的解决方案。虽然我最终在这个线程中使用了我自己的解决方案,但值得看看Dave Smith的代码,这也是Mark在博客文章中提到的:

https://gist.github.com/8cbe094bb7a783e37ad1/

警告!在采取这种方法之前,请注意此方法存在一些非常严重的问题,这些问题在本文末尾和下面的评论中都有提到。

最终实现效果如下:

Dave Smith的PagerContainer屏幕截图

它有效地通过将ViewPager包装在FrameLayout的子类中,并将其设置为特定大小,并调用setClipChildren(false)来阻止Android剪切超出ViewPager边界的视图,从而实现了你想要的效果。

在XML中,非常简单:

<com.example.pagercontainer.PagerContainer
    android:id="@+id/pager_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#CCC">
    <android.support.v4.view.ViewPager
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal" />
</com.example.pagercontainer.PagerContainer>

加入一些代码来处理来自 ViewPager 外部的触摸事件,并在滚动时使显示无效,就完成了。

话虽如此,虽然这通常很好用,但我注意到有一个边缘情况无法通过这种相当简单的构造解决:当在 ViewPager 上调用 setCurrentPage() 时。我找到的唯一解决方法是子类化 ViewPager 并让其 invalidate() 函数也使 PagerContainer 无效。


@Pual Lammertsma:你能告诉我在包含ViewPager的Activity中何时调用invalidate()吗?如果你能分享一下你的ViewPager代码就更好了。 - Shruti
这种技术存在各种问题,包括触摸事件。很抱歉我不能分享代码,因为它是专有项目,但我建议您选择另一种解决方案 - Paul Lammertsma
1
根据我的经验,这种技术的动画/响应并不十分流畅。我不确定是否有什么东西被多次重绘了。我也不认为我使用了非常复杂的内容视图。这似乎是一个非常简单的视图/组件... 令人失望的是,Android没有本地的非废弃解决方案 :( - loeschg
非常好的文档博客。帮助我找到了我需要的某些变化。@Paul Lammertsma感谢您将我重定向到那个博客。真的很感激。 - Jimit Patel
嗯...为什么你不使用RecyclerView呢?你可以将它设置为纵向或横向。 - Abdalrhman Alkhulaqi
显示剩余5条评论

18

可以在同一屏幕上显示多个页面。其中一种方法是通过覆盖PagerAdapter中的getPageWidth()方法来实现。 getPageWidth()返回0和1之间的浮点数,表示Viewpager应该占用页面的宽度比例。默认情况下设置为1。所以您可以将其更改为所需的宽度。
您可以在这里GitHub项目了解更多信息。


6
这个方法可行,但请注意它不会将页面居中显示在您的视图中;也就是说,最左边的页面将与ViewPager的左侧齐平。 - Paul Lammertsma
那么,我们可以在中心做吗? - gaurav414u
简单的一行代码解决了我的问题。在我的情况下,我想显示3个项目。所以pagewidth=0.33... 这是未来用户的示例代码@Override public float getPageWidth(int position) {return 0.33f;} - Ranjithkumar

17

这是我得到它的方式:

<android.support.v4.view.ViewPager
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_gravity="center"
    android:layout_marginBottom="8dp"
    android:clipToPadding="false"
    android:gravity="center"
    android:paddingLeft="36dp"
    android:paddingRight="36dp"/>

在活动中,我使用这个:

markPager.setPageMargin(64);

希望对您有所帮助!


工作正常,不需要设置活动 setPagerMargin :) - Pelanes

9
我也遇到了同样的问题,只不过我需要同时显示三个页面(上一页、当前页和下一页)。经过漫长的研究,我认为我找到了最好的解决方案。
这个解决方案是在这里几个答案的基础上混合而成的:如@Paul Lammertsma的答案所指出的 - Dave Smith在Mark Murphy的博客中的代码是解决方案的基础。对我来说唯一的问题是ViewPager只出现在屏幕顶部,因为他们在xml文件中给它设定了一个固定大小。
 android:layout_width="150dp"
 android:layout_height="100dp"

这对我的目的不好,因为我正在寻找能够在整个屏幕上展开的东西。所以我将其更改为像您在此处看到的那样包装内容:

  <com.example.nutrino_assignment.PagerContainer
    android:id="@+id/pager_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#CCC">
    <android.support.v4.view.ViewPager
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
  </com.example.nutrino_assignment.PagerContainer>

现在我失去了教程试图做的所有效果。使用@andro的答案,我能够同时显示多页:确切地说是2页!当前页和下一页。 通过以下方式覆盖实现:

        @Override 
    public float getPageWidth(int position) {
        return(0.9f); 
    }

那差不多是我需要的......(尽管我认为对于你所要求的内容已经足够了),但对于其他可能需要像我一样的东西的人: 对于解决方案的最后一部分,我使用了@Paul Lammertsma在这个答案中提出的想法。 在戴夫·史密斯的代码中,您将在onCreate方法中找到此行:

   //A little space between pages
    pager.setPageMargin(15);

我使用以下内容进行替换:
   //A little space between pages
    pager.setPageMargin(-64);

现在在第一页看起来:

|view 1|view 2|view 3|
|screen   |

当在第二页时,页面看起来如下:

|view 1|view 2|view 3|
     |screen    |

希望这可以帮助有需要的人!我浪费了大约两天的时间... 祝好运。

1
viewPager.setPageMargin(-18);// adjust accordingly ,-means less gap

在ImageAdapter中。
private class ImagePagerAdapter2 extends PagerAdapter {
    private int[] mImages = new int[] {

            R.drawable.add1,
            R.drawable.add3,
            R.drawable.add4,
            R.drawable.add2,
    };
    @Override
    public float getPageWidth(int position) {
        return .3f;
    }

调整返回值...较小的意味着更多的图像......0.3 表示每次至少有 3 张图像。


这是一个适用于同时处理多个页面的好解决方案,但并不完整。最好查看此帖子:https://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html。但主要问题是,它没有回答 OP 的问题。 - Arash
现在使用水平或垂直模式的RecyleView替换ViewPager会更好。 - Raktim Bhattacharya

0
LayoutParams lp = new LayoutParams(width,height);
viewpager.setLayoutParams(lp);

0

在 XML 文件中使用此代码(主活动)

                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="130dp"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:orientation="vertical"
                    android:weightSum="1">
                    <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="130dp">
                        <com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer
                            android:id="@+id/pager_container"
                            android:layout_width="match_parent"
                            android:layout_height="fill_parent">
                            <android.support.v4.view.ViewPager
                                android:id="@+id/viewpager"
                                android:layout_width="100dip"
                                android:layout_height="100dip"/>
                      </com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer>
                    </RelativeLayout>
                </LinearLayout>

0

主活动 XML 文件中添加此代码

      <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="130dp"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:orientation="vertical"
                    android:weightSum="1">
                    <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="130dp">
                        <com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer
                            android:id="@+id/pager_container"
                            android:layout_width="match_parent"
                            android:layout_height="fill_parent">
                            <android.support.v4.view.ViewPager
                                android:id="@+id/viewpager"
                                android:layout_width="100dip"
                                android:layout_height="100dip"/>
                      </com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer>
                    </RelativeLayout>
                </LinearLayout>

主活动代码

public class MainActivity extends Activity{
 final Integer[] XMEN2= {R.mipmap.bookticket,R.mipmap.safty,R.mipmap.privacy};
 private ArrayList<Integer> XMENArray2 = new ArrayList<Integer>();
 PagerContainer mContainer;
 int currentPage2 = 0;
 private static int NUM_PAGES2 = 0;
 ViewPager mPager2;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initData2();}
 private void initViews() {
  mPager2 = (ViewPager)findViewById(R.id.viewpager);
  mContainer = (PagerContainer)findViewById(R.id.pager_container);
  mPager2.setOffscreenPageLimit(5);
  mPager2.setPageMargin(15);
  mPager2.setClipChildren(false);
 }

   private void initData2() {

        for(int i=0;i<XMEN2.length;i++)
            XMENArray2.add(XMEN2[i]);
        mPager2.setAdapter(new Sliding_Adaptertwo(getActivity(),XMENArray2));
        NUM_PAGES2 =XMEN2.length;
        final Handler handler = new Handler();
        final Runnable Update = new Runnable() {
            public void run() {
                if (currentPage2 == NUM_PAGES2) {
                    currentPage2= 0;
                }mPager2.setCurrentItem(currentPage2++, true);
            }
        };
        Timer swipeTimer = new Timer();
        swipeTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.post(Update);
            }
        }, 3000, 3000);

    }



}

Pager View pagercontainer 类


import android.content.Context;

import android.graphics.Point;

import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import android.widget.FrameLayout;


public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {

    private ViewPager mPager;
    boolean mNeedsRedraw = false;

    public PagerContainer(Context context) {
        super(context);
        init();
    }

    public PagerContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        //Disable clipping of children so non-selected pages are visible
        setClipChildren(false);

        //Child clipping doesn't work with hardware acceleration in Android 3.x/4.x
        //You need to set this value here if using hardware acceleration in an
        // application targeted at these releases.
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        try {
            mPager = (ViewPager) getChildAt(0);
            mPager.setOnPageChangeListener(this);
        } catch (Exception e) {
            throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
        }
    }

    public ViewPager getViewPager() {
        return mPager;
    }

    private Point mCenter = new Point();
    private Point mInitialTouch = new Point();

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mCenter.x = w / 2;
        mCenter.y = h / 2;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //We capture any touches not already handled by the ViewPager
        // to implement scrolling from a touch outside the pager bounds.
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mInitialTouch.x = (int)ev.getX();
                mInitialTouch.y = (int)ev.getY();
            default:
                ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
                break;
        }

        return mPager.dispatchTouchEvent(ev);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        //Force the container to redraw on scrolling.
        //Without this the outer pages render initially and then stay static
        if (mNeedsRedraw) invalidate();
    }

    @Override
    public void onPageSelected(int position) { }

    @Override
    public void onPageScrollStateChanged(int state) {
        mNeedsRedraw = (state != ViewPager.SCROLL_STATE_IDLE);
    }
}

和它的适配器

public class Sliding_Adaptertwo extends PagerAdapter {


    private ArrayList<Integer> IMAGES;
    private LayoutInflater inflater;
    private Context context;


    public Sliding_Adaptertwo(Context context, ArrayList<Integer> IMAGES) {
        this.context = context;
        this.IMAGES=IMAGES;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    @Override
    public int getCount() {
        return IMAGES.size();
    }

    @Override
    public Object instantiateItem(ViewGroup view, int position) {
        View imageLayout = inflater.inflate(R.layout.sliding_layout, view, false);

        assert imageLayout != null;
        final ImageView imageView = (ImageView) imageLayout
                .findViewById(R.id.image);


        imageView.setImageResource(IMAGES.get(position));

        view.addView(imageLayout, 0);

        return imageLayout;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view.equals(object);
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
    }

    @Override
    public Parcelable saveState() {
        return null;
    }


}

适配器类的XML文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    >

    <ImageView
        android:id="@+id/image"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:adjustViewBounds="true"
        android:layout_gravity="center"
        android:scaleType="fitXY"
        android:src="@drawable/ad1"
       />
</FrameLayout>

它运行良好


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