ViewPager与固定宽度的子项

5

我需要设计一个 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
@hjpotter92 请问,什么是 VirtualHost - Ali
你不能将ViewPager的宽度从match parent更改为700dp,或者改为wrap_content(如果需要的话),而不是更改子项吗? - maaz
@maaz 这不会起作用,因为 ViewPager 会将自己测量为 MATCH_PARENT - Ali
1
如果ViewPager的子项具有固定宽度,那么在子片段两侧应该有额外的空白空间,还是下一个ViewPager子片段应该从上一个子片段结束的地方继续?期望的行为是什么? - pratsJ
查看此SO答案 - Manish Mulimani
5个回答

6
可以使用FragmentPagerAdapter.getPageWidth来缩放ViewPager中的页面。您需要一个自定义的FragmentPagerAdapter。如果返回0到1之间的数字,则页面会缩小,宽度> 1会相应地缩放页面。但这并不是很好,因为您无法在放大的页面内滚动图像。
如果将ImageView包装在HorizontalScrollView中,情况会稍微好一些,您可以在页面内滚动图像,但是如果您不太快,滑动手势在页面之间被HorizontalScrollView捕获。请参见this video
因此,解决方案是确实使用自定义HorizontalScrollView(请参见InterceptingHorizontalScrollView),该ScrollView禁止拦截onTouch事件,但也允许当用户滚动到末尾时进行拦截(请参见覆盖的onOverScrolled)。请参见this video或下面的图像以了解差异。

编辑:您不需要覆盖onInterceptTouchEvent,因为HorizontalScrollView默认情况下会拦截它们(所以滚动图像比分页具有更高的优先级)。

最后,这是所有的代码:

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>

InterceptingHorizontalScrollView.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);
        }
    }
}

这基本上与我的答案完全相同。 - Paul Burke
@PaulBurke,你错过了一个非常重要的细节!你需要在onOverScrolled中检查clampedX,否则你将无法正常地在页面之间滑动。 - Gyebro
@PaulBurke,我在这里所做的不仅仅是将用户引导到一个自定义视图,这甚至不能解决他/她的问题。我制作了一个示例项目,尝试了不同的技术(ViewPager页面缩放,ViewPager中的普通ScrollView,ViewPager中的自定义ScrollView),并呈现了结果。希望您同意,我无法将所有这些内容都放在您回答下面的评论中。 - Gyebro
你在开玩笑吧?你把onOverScrolled处理程序添加到了我链接的完全相同的类中。你甚至没有改变名称。所有额外的代码都是完全不必要的,实际上会减少简单答案的可读性。你选择了自私的方式,而不是为我的答案增添内容。 - Paul Burke
现在您的答案已经显著不同(而且可能是正确的)。已取消踩负评。 - Paul Burke

1
这里您真正需要的是将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жҳҜж— з”Ёзҡ„пјҢдҪ еҸҜд»Ҙи®ҫзҪ®ImageViewзҡ„widthе’ҢbackgroundгҖӮ - Gyebro
我不对OP的布局做任何假设。只回答他的问题。你假设这是完整的布局XML,而不仅仅是一个片段来说明一个观点。如果OP在运行时添加了额外的子元素呢?那么你的方法是错误的,因为ScrollView只能接受一个子元素。 - Paul Burke

1

-1
  • 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>

代码如下: ViewPager 预览


此外,使用700dp宽度的ImageView似乎是一个不好的主意。与其对他的目标进行假设,不如尝试回答这个问题。 - Paul Burke
@PaulBurke 这不是假设,而是建议。如果 OP 的代码可以改进,为什么不帮助他呢? - Simas
"ViewPager 的子项应始终与其父项宽度相匹配。"--谁说的? Gmail 在线程视图中使用了 ViewPager,允许消息本身水平滚动。你看到了你所做的假设吗?" - Paul Burke
@PaulBurke,我提供了一种替代方法,在我看来,解决了OP的问题。OP可以自由选择任何他喜欢的答案,并且在他的上下文中适合他。希望OP也能评论一下对他无效的答案。 - Simas
@PaulBurke他没有提到孩子的宽度比父母的宽度大。我的答案是对OP可能正在做的事情的逻辑解决方案。我们无法确定,再次让OP决定而不是在评论中炒作和垃圾邮件。 - Simas
显示剩余4条评论

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

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