Android ViewPager-显示左右页面预览

我用的是安卓的 ViewPager。我想要做的是在左侧和右侧显示页面的预览。我已经看到在哪里我可以使用负 pageMargin来显示右侧的预览。

setPageMargin(-100);

有没有什么方法可以让我展示一下左侧的预览呢?它基本上类似于我正在寻找的图库小部件。

99378 次浏览

使用这个片段适配器和类在左右 scroll.add 中查看视图页面。

package com.rmn.viewpager;


import java.util.List;


import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;


/**
* The <code>PagerAdapter</code> serves the fragments when paging.
* @author mwho
*/
public class PagerAdapter extends FragmentPagerAdapter {


private List<Fragment> fragments;
/**
* @param fm
* @param fragments
*/
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
/* (non-Javadoc)
* @see android.support.v4.app.FragmentPagerAdapter#getItem(int)
*/
@Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}


/* (non-Javadoc)
* @see android.support.v4.view.PagerAdapter#getCount()
*/
@Override
public int getCount() {
return this.fragments.size();
}
}






package com.manishkpr.viewpager;




import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;


public class ViewPagerAdapter extends FragmentPagerAdapter {
private Context _context;


public ViewPagerAdapter(Context context, FragmentManager fm) {
super(fm);
_context=context;


}
@Override
public Fragment getItem(int position) {
Fragment f = new Fragment();
switch(position){
case 0:
f=LayoutOne.newInstance(_context);
break;
case 1:
f=LayoutTwo.newInstance(_context);
break;
}
return f;
}
@Override
public int getCount() {
return 2;
}


}

若要显示左页和右页的预览,请设置以下两个值

  1. viewpager.setClipToPadding(false);
  2. viewpager.setPadding(left,0,right,0);

如果在视图页中两个页面之间需要空间,请添加

viewpager.setPageMargin(int);

更新:-ViewPager 2.0

请使用以下方法

viewPager2.setPageTransformer(new MarginPageTransformer(1500));

再看看 Android ViewPager2 setPageMargin 未解决

老答案

@ JijuInducoodan 给出的答案是完美的、有效的。然而,由于我对 Android 相对较新,我花了一段时间才理解和设置正确。所以,我把这个问题的答案发布出来,以供将来参考,并帮助任何与我处境相同的人。

if (viewPager == null)
{
            

// Initializing view pager
viewPager = (ViewPager) findViewById(R.id.vpLookBook);
            

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

不需要在适配器中为 ViewPager's页面设置任何宽度。在 ViewPager中不需要额外的代码来查看上一页和下一页。但是,如果您想在每个页面的顶部和底部添加空白,您可以将以下代码设置为 ViewPager's子页面的父布局。

android:paddingTop="20dp"
android:paddingBottom="20dp"

Final ViewPager

这将是 ViewPager 的最终外观。

在2017年,这种行为还可以通过使用 RecyclerView寻呼机快照助手(在 v7支持库的25.1.0版本中添加)轻松实现: 

enter image description here

不久前,我需要这种类似浏览器的功能,并准备了一个小型库:

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

它主要由一个类文件 Metal迴 clViewPager.java(以及两个 xml: XmlXml)组成。

希望它能帮到某些人,节省一些时间:)

如果有人仍然在寻找解决方案,我已经定制了 ViewPage 来实现它,而不使用负边距,在这里找到一个示例项目。这里是一个 https://github.com/44kksharma/android-viewpager-carousel-ui 它应该工作在大多数情况下,但您仍然可以定义页边距 mPager.setPageMargin(margin in pixel);

对于那些想在不同的屏幕上看到这些的人来说,

mPager.setClipToPadding(false);
DisplayMetrics displayMetrics = new DisplayMetrics();
self.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
int paddingToSet = width/4; //set this ratio according to how much of the next and previos screen you want to show.
mPager.setPadding(paddingToSet,0,paddingToSet,0);


您可以在 xml 文件中执行此操作,只需使用以下代码:

 android:clipToPadding="false"
android:paddingLeft="XX"
android:paddingRight="XX"

例如:

<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingLeft="30dp"
android:paddingRight="30dp" />

注意: 如果需要页面之间的空间,请将填充/边距设置为子片段

请注意,当设置没有零补丁到一个视图页面,视图页面的 edgeeeffect 布局是混乱的,这是一个错误的视图页面,见这里: 视图页边缘效应错误

您可以通过将 android:overScrollMode="never"设置为视图页面来禁用边缘效应,或者使用稍微修改一下版本的 EdgeEffect 类: ViewPagerEdgeeffect 修复来修复 bug

使用新的 ViewPager2解决方案

现在你应该考虑使用 ViewPager2“取代了 ViewPager,解决了其前身的大部分问题”:

  • 基于回收视图
  • RTL (从右到左)布局支持
  • 垂直方向支撑
  • 可靠的片段支持(包括处理对基础片段集合的更改)
  • 数据集更改动画(包括支持 DiffUtil)

结果

enter image description here

密码

在活动/片段中,设置 ViewPager2:

// MyRecyclerViewAdapter is an standard RecyclerView.Adapter :)
viewPager2.adapter = MyRecyclerViewAdapter()


// You need to retain one page on each side so that the next and previous items are visible
viewPager2.offscreenPageLimit = 1


// Add a PageTransformer that translates the next and previous items horizontally
// towards the center of the screen, which makes them visible
val nextItemVisiblePx = resources.getDimension(R.dimen.viewpager_next_item_visible)
val currentItemHorizontalMarginPx = resources.getDimension(R.dimen.viewpager_current_item_horizontal_margin)
val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float ->
page.translationX = -pageTranslationX * position
// Next line scales the item's height. You can remove it if you don't want this effect
page.scaleY = 1 - (0.25f * abs(position))
// If you want a fading effect uncomment the next line:
// page.alpha = 0.25f + (1 - abs(position))
}
viewPager2.setPageTransformer(pageTransformer)


// The ItemDecoration gives the current (centered) item horizontal margin so that
// it doesn't occupy the whole screen width. Without it the items overlap
val itemDecoration = HorizontalMarginItemDecoration(
context,
R.dimen.viewpager_current_item_horizontal_margin
)
viewPager2.addItemDecoration(itemDecoration)

再加上 HorizontalMarginItemDecoration,它只是一个简单的 ItemDecoration:

/**
* Adds margin to the left and right sides of the RecyclerView item.
* Adapted from https://stackoverflow.com/a/27664023/4034572
* @param horizontalMarginInDp the margin resource, in dp.
*/
class HorizontalMarginItemDecoration(context: Context, @DimenRes horizontalMarginInDp: Int) :
RecyclerView.ItemDecoration() {


private val horizontalMarginInPx: Int =
context.resources.getDimension(horizontalMarginInDp).toInt()


override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
) {
outRect.right = horizontalMarginInPx
outRect.left = horizontalMarginInPx
}


}

添加控制前/下一项可见程度的尺寸,以及当前项的水平边距:

<dimen name="viewpager_next_item_visible">26dp</dimen>
<dimen name="viewpager_current_item_horizontal_margin">42dp</dimen>

enter image description here

最后,将 ViewPager2添加到您的布局中:

<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

一件重要的事情: ViewPager2项必须有 layout_height="match_parent"(否则它会抛出一个 IllegalStateException 异常) ,因此您应该执行以下操作:

<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" <-- this!
app:cardCornerRadius="8dp"
app:cardUseCompatPadding="true">


<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="280dp">


<!-- ... -->


</androidx.constraintlayout.widget.ConstraintLayout>


</androidx.cardview.widget.CardView>

页面转换器示例

Google 在 ViewPager2上增加了一个指南,其中包含2个 PageTransformer实现,你可以将它们作为灵感来源: https://developer.android.com/training/animation/screen-slide-2

关于新的 ViewPager2

 override fun getPageWidth(position: Int): Float {
return 0.7f
}

把这个添加到适配器类文件中,这对我很有用

基于 Jiju 的回答,如果您想为 VIEWPAGER 2设置页面间距,请使用以下代码:

viewpager.setPageTransformer(new MarginPageTransformer(14));

Kotlin + ViewPager2

enter image description here

我参加聚会迟到了,但上面的回答对我不起作用。所以我在这里帮助其他仍然在寻找答案的人。

  1. 你的 XML:

         <androidx.viewpager2.widget.ViewPager2
    android:id="@+id/newsHomeViewPager"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
    
  2. 你的项目布局根视图应该是这样的:

         <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingStart="@dimen/dp_10"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical" />
    
  3. 你的活动/片段:

在设置视图页面适配器之前添加这个

        newsHomeViewPager.apply {
clipToPadding = false   // allow full width shown with padding
clipChildren = false    // allow left/right item is not clipped
offscreenPageLimit = 2  // make sure left/right item is rendered
}


//increase this offset to show more of left/right
val offsetPx =
resources.getDimension(R.dimen.dp_30).toInt().dpToPx(resources.displayMetrics)
newsHomeViewPager.setPadding(0, 0, offsetPx, 0)


//increase this offset to increase distance between 2 items
val pageMarginPx =
resources.getDimension(R.dimen.dp_5).toInt().dpToPx(resources.displayMetrics)
val marginTransformer = MarginPageTransformer(pageMarginPx)
newsHomeViewPager.setPageTransformer(marginTransformer)
  1. Dp 到像素函数:

    Fun Int.dpToPx (displayMetrics: DisplayMetrics) : Int = (this * displayMetrics.password) . toInt ()

一般注释:

  1. 我只显示下一页,所以在 setPadding方法中,左边保留0,右边保留 offsetPx。因此,在 setPadding方法中,如果您想显示右侧的项目,也可以将 offsetPx添加到右侧。

  2. 我已经使用了维度文件中的 dp,您可以使用自己的。

谢谢,编程愉快。

在 XML 文件中,应该这样设置填充: enter image description here

在碎片中:

int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());

要想搞定一切,就用这个

myViewPager.setPageMargin(-margin);

应该可以:)

感谢 这篇文章的作者,这是我找到的最好的解决方案。

只要在 Helpers类中创建一个函数,例如:

fun ViewPager2.showHorizontalPreview(offsetDpLeft : Int, offsetDpRight : Int, marginBtwItems : Int){
this.apply {
clipToPadding = false   // allow full width shown with padding
clipChildren = false    // allow left/right item is not clipped
offscreenPageLimit = 2  // make sure left/right item is rendered
}


// increase this offset to show more of left/right
val offsetPxLeft = offsetDpLeft.toPx()
val offsetPxRight = offsetDpRight.toPx()
this.setPadding(offsetPxLeft, 0, offsetPxRight, 0)


// increase this offset to increase distance between 2 items
val pageMarginPx = marginBtwItems.toPx()
val marginTransformer = MarginPageTransformer(pageMarginPx)
this.setPageTransformer(marginTransformer)
}

就像这样:

mViewPager.showHorizontalPreview(40,40,5)

在你的活动/片段

ViewPager2的 Kotlin 扩展解决方案


  1. 活动或片段中的单行实现。

    binding.viewpager2.setPreviewBothSide(R.dimen._20sdp,R.dimen._35sdp)
    
  2. 在项目的 HorizontalMarginItemDecoration.kt文件中添加以下代码

    import android.content.Context
    import android.graphics.Rect
    import android.view.View
    import androidx.annotation.DimenRes
    import androidx.recyclerview.widget.RecyclerView
    import androidx.viewpager2.widget.ViewPager2
    
    
    class HorizontalMarginItemDecoration(context: Context, @DimenRes horizontalMarginInDp: Int) :
    RecyclerView.ItemDecoration() {
    private val horizontalMarginInPx: Int =
    context.resources.getDimension(horizontalMarginInDp).toInt()
    
    
    override fun getItemOffsets(
    outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
    ) {
    outRect.right = horizontalMarginInPx
    outRect.left = horizontalMarginInPx
    }
    }
    
    
    fun ViewPager2.setPreviewBothSide(@DimenRes nextItemVisibleSize: Int,@DimenRes currentItemHorizontalMargin: Int) {
    this.offscreenPageLimit = 1
    val nextItemVisiblePx = resources.getDimension(nextItemVisibleSize)
    val currentItemHorizontalMarginPx = resources.getDimension(currentItemHorizontalMargin)
    val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
    val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float ->
    page.translationX = -pageTranslationX * position
    page.scaleY = 1 - (0.25f * kotlin.math.abs(position))
    }
    this.setPageTransformer(pageTransformer)
    
    
    val itemDecoration = HorizontalMarginItemDecoration(
    context,
    currentItemHorizontalMargin
    )
    this.addItemDecoration(itemDecoration)
    }
    
  3. 是的,在这里你得到了这个惊人的输出

    enter image description here

此答案优化版本的 阿尔伯特 · 维拉 · 卡尔沃的答案