【问题标题】:ViewPager with fixed width childs具有固定宽度子项的 ViewPager
【发布时间】:2014-09-20 21:31:42
【问题描述】:

我需要设计一个ViewPager,它能够通过固定宽度的孩子(例如宽度为700dp的孩子),不幸的是,当前版本的ViewPager会自动将所有孩子的宽度设置为MATCH_PARENT,有什么办法吗要将此功能添加到ViewPager

我的ViewPager 布局:

<?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="wrap_content"
    android:orientation="vertical" >

    <android.support.v4.view.ViewPager
        android:id="@+id/some_id"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:overScrollMode="never" />

</LinearLayout>

ViewPager子布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/banner_main_layout_container"
    android:layout_width="700dp"
    android:layout_height="match_parent"
    android:background="#fff"
    android:gravity="center"
    android:orientation="vertical" >

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="some images"/>

</LinearLayout>

在此先感谢...

【问题讨论】:

  • 您可以在 VirtualHost 部分设置此选项
  • @hjpotter92 请问VirtualHost是什么@!?
  • 您不能将 viewpager 宽度从匹配父级更改为 700dp 或更改为 wrap_content(如果需要的话)而不是子级吗??
  • @maaz 这将不起作用,因为ViewPager 将自己测量为MATCH_PARENT
  • 如果viewpager子节点的宽度是固定的,那么子fragment的两边是否应该有额外的空白空间,或者下一个viewpager子fragment应该从最后一个子fragment完成的地方继续?期望的行为是什么?

标签: android android-viewpager


【解决方案1】:

可以使用FragmentPagerAdapter.getPageWidth 在 ViewPager 中缩放页面。您将需要一个自定义 FragmentPagerAdapter。如果返回一个介于 0 和 1 之间的数字,则页面会缩小,宽度 > 1 会相应地放大页面。但这并不是很好,因为您无法在放大后的页面中滚动图像。

如果您将 ImageView 包装在 HorizontalScrollView 中,情况会好一些,您可以在页面内滚动图像,但如果您不是很快,页面之间的滑动手势会被 Horizo​​ntalScrollView 捕获。见this video

因此,真正的解决方案是使用自定义的 Horizo​​ntalScrollView(参见 InterceptingHorizontalScrollView),它不允许拦截 onTouch 事件,但在用户滚动到末尾时也允许它(参见覆盖的 onOverScrolled)。请参阅this video 或下图了解区别。

编辑你不需要重写onInterceptTouchEvent,因为Horizo​​ntalScrollView默认拦截它们(所以滚动图片比分页优先级更高。)

最后,所有代码如下:

MainActivity.java

public class MainActivity extends Activity {

    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Set up the ViewPager
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setPageMargin(30);
        mViewPager.setAdapter(new ImagePagerAdapter(getFragmentManager()));
    }

    private class ImagePagerAdapter extends FragmentPagerAdapter {

        public ImagePagerAdapter(FragmentManager fm) {
            super(fm);
        }
        @Override
        public Fragment getItem(int i) {
            switch(i) {
                case 0:
                    return ImageFragment.newInstance(R.drawable.forest1);
                case 1:
                    return ImageFragment.newInstance(R.drawable.forest2);
                case 2:
                    return ImageFragment.newInstance(R.drawable.forest3);
                default:
                    return ImageFragment.newInstance(R.drawable.ic_launcher);
            }
        }
        @Override
        public int getCount() {
            return 3;
        }
        @Override
        public float getPageWidth(int position)
        {
            // Here it is possible to scale page width
            return super.getPageWidth(position);
        }
    }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

ImageFragment.java

public class ImageFragment extends Fragment {
    private static final String ARG_PARAM1 = "image_resid";
    private int mImageResId;

    public static ImageFragment newInstance(int image_resid) {
        ImageFragment fragment = new ImageFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_PARAM1, image_resid);
        fragment.setArguments(args);
        return fragment;
    }
    public ImageFragment() {
        // Required empty public constructor
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mImageResId = getArguments().getInt(ARG_PARAM1);
        }
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_image, container, false);
        ImageView imageView = (ImageView)v.findViewById(R.id.imageView);
        imageView.setImageResource(mImageResId);
        return v;
    }
}

fragment_image.xml

<com.gyebro.viewpagertest.InterceptingHorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="600dp"
    android:layout_height="match_parent"
    tools:context="com.gyebro.viewpagertest.ImageFragment">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:id="@+id/imageView"
            android:src="@drawable/forest1" />
</com.gyebro.viewpagertest.InterceptingHorizontalScrollView>

InterceptingHorizo​​ntalScrollView.java

public class InterceptingHorizontalScrollView extends HorizontalScrollView {

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

    /*@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (getParent() != null) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }*/

    @Override
    protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX,scrollY,clampedX,clampedY);
        // if clampedX == true, we've reached the end of the HorizontalScrollView so
        // allow parent to intercept
        if(clampedX) {
            Log.d("InterceptingHorizontalScrollView", "Reached the end, allowing interception");
            getParent().requestDisallowInterceptTouchEvent(false);
        }
    }
}

【讨论】:

  • 这与我的答案基本相同。
  • @PaulBurke 您遗漏了一个非常重要的细节!需要在onOverScrolled中勾选clampedX,否则无法正常在页面之间滑动。
  • @PaulBurke 我在这里所做的不仅仅是将用户引导到自定义视图,这甚至不能解决他/她的问题。我做了一个示例项目,尝试了不同的技术(ViewPager 页面缩放、ViewPager 中的普通 ScrollViews、ViewPager 中的自定义 ScrollViews),并展示了结果。希望您同意,我无法将所有这些都放入您答案下方的评论中。
  • 你一定是在开玩笑吧?您将onOverScrolled 处理添加到我链接的同一类中。你连名字都没改。所有额外的代码都是完全没有必要的,实际上是从简单的答案中删除的。您采取了自私的路线,而不是添加到我的答案中。
  • 现在您的答案大不相同(而且可能是正确的)。删除了反对票。
【解决方案2】:

您真正想要的是HorizontalScrollView 内的ViewPager。这需要自定义触摸处理,因此您需要使用此类:InterceptingHorizontalScrollView

要使InterceptingHorizontalScrollViewViewPager 中工作,您必须覆盖onOverScrolled

@Override
protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX,scrollY,clampedX,clampedY);
    if(clampedX) {
        getParent().requestDisallowInterceptTouchEvent(false);
    }
}

感谢 Gyebro 的提示。^

您的 ViewPager 子布局如下所示:

<com.tumblr.widget.InterceptingHorizontalScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:id="@+id/banner_main_layout_container"
        android:layout_width="700dp"
        android:layout_height="match_parent"
        android:background="#fff"
        android:gravity="center"
        android:orientation="vertical" >

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="some images"/>

    </LinearLayout>
</com.tumblr.widget.InterceptingHorizontalScrollView>

【讨论】:

  • 这里的LinearLayout没用,可以设置ImageViewwidthbackground
  • 我没有对 OP 的布局做任何假设。只回答他的问题。您假设这是整个布局 XML,而不仅仅是用于说明一点的 sn-p。如果 OP 在运行时添加额外的孩子怎么办?那么你的方法是错误的,考虑到 ScrollView 需要一个孩子。
【解决方案3】:

您可以覆盖 PagerAdapter 的 getWidth 方法,如果这没有帮助,请查看: http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html

最重要的是试试this example,效果很好! 只需阅读整个示例即可。

【讨论】:

    【解决方案4】:
    • ViewPager 子项将/应该始终与它的父项宽度匹配。
    • 此外,使用 700dp 宽度 ImageView 听起来是个坏主意。在纵向模式下会是什么样子?

    如果您不想让 ViewPager 本身更小,即您希望 ImageViews 从屏幕的绝对一侧滑动,则必须使项目显示更小。

    可以通过创建 2 个额外的 LinearLayouts 来充当间隔符来完成模仿。然后它看起来好像你的项目有一个特定的宽度。

    这是一个示例(带有 TextView):

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <LinearLayout
            android:background="#FFF"
            android:layout_weight="3"
            android:layout_width="0dp"
            android:layout_height="match_parent"/>
    
        <TextView
            android:layout_weight="10"
            android:background="#333"
            android:textColor="#FFF"
            android:textSize="50sp"
            android:gravity="center"
            android:text="HELLO"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    
        <LinearLayout
            android:background="#FFF"
            android:layout_weight="3"
            android:layout_width="0dp"
            android:layout_height="match_parent"/>
    </LinearLayout>
    

    看起来像这样:

    【讨论】:

    • “此外,使用 700dp 宽度的 ImageView 听起来是个坏主意” - 不要假设他的预期目标,而是尝试回答问题。
    • @PaulBurke 这不是假设,而是建议。如果 OP 的代码可以改进,那为什么不也帮助他呢?
    • 你假设用例。在您甚至不知道上下文时提供建议。谁说这是一个显示应该完全可见的照片的消费者应用程序?我会举报你出于恶意对我的回答投了反对票。
    • "ViewPager 子级将/应该始终匹配它的父级宽度。" - 谁说的? Gmail 有一个用于线程视图的 ViewPager,它允许消息本身水平滚动。看到你所做的假设了吗?
    • @PaulBurke 我不明白你的 gmail 示例。线程匹配它的父级(可能是一个片段)的宽度。
    【解决方案5】:

    将视图分页器的页边距设置为负值。这将强制页面推入视图寻呼机。请注意,它也会导致重叠,因此您会在视图分页器中看到下一个/上一个元素的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-22
      • 1970-01-01
      • 2019-04-19
      • 2012-01-26
      • 2014-11-25
      • 2020-11-16
      相关资源
      最近更新 更多