ViewPager中的ViewPager

问题描述 投票:37回答:8

我想创建一个ViewPager(有三个项目),其中每个视图是另一个ViewPager(有两个项目)。用户然后滑动这样的项目:

ViewPager1[0] ViewPager2[0]
ViewPager1[0] ViewPager2[1]
ViewPager1[1] ViewPager2[0]
ViewPager1[1] ViewPager2[1]
ViewPager1[2] ViewPager2[0]
ViewPager1[2] ViewPager2[1]

怎么可能呢?

android android-viewpager swipe swipe-gesture android-nested-fragment
8个回答
56
投票

覆盖父ViewPager中的canScroll:

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
   if(v != this && v instanceof ViewPager) {
      return true;
   }
   return super.canScroll(v, checkV, dx, x, y);
}

28
投票

试试这个:

public class CustomViewPager extends ViewPager {
    private int childId;

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
     @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (childId > 0) {          
            ViewPager pager = (ViewPager)findViewById(childId);

            if (pager != null) {           
                pager.requestDisallowInterceptTouchEvent(true);
            }

        }

        return super.onInterceptTouchEvent(event);
    }

    public void setChildId(int id) {
        this.childId = id;
    }
}

12
投票

我搜索了很长时间在另一个ViewPager工作中创建一个ViewPager,并在这里找到了“Android Noob”的解决方案。非常感谢你!

我也想分享我的解决方案。一旦到达内部ViewPager中的最后一个(最右边)元素,我添加了将滑动管理切换到周围ViewPager的可能性。为了防止毛刺,我还保存了最后一个元素的第一个滑动方向:即如果你向左滑动,最小向右滑动不会重置滚动状态。

public class GalleryViewPager extends ViewPager {

    /** the last x position */
    private float   lastX;

    /** if the first swipe was from left to right (->), dont listen to swipes from the right */
    private boolean slidingLeft;

    /** if the first swipe was from right to left (<-), dont listen to swipes from the left */
    private boolean slidingRight;

    public GalleryViewPager(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public GalleryViewPager(final Context context) {
        super(context);
    }

    @Override
    public boolean onTouchEvent(final MotionEvent ev) {
        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                // Disallow parent ViewPager to intercept touch events.
                this.getParent().requestDisallowInterceptTouchEvent(true);

                // save the current x position
                this.lastX = ev.getX();

                break;

            case MotionEvent.ACTION_UP:
                // Allow parent ViewPager to intercept touch events.
                this.getParent().requestDisallowInterceptTouchEvent(false);

                // save the current x position
                this.lastX = ev.getX();

                // reset swipe actions
                this.slidingLeft = false;
                this.slidingRight = false;

                break;

            case MotionEvent.ACTION_MOVE:
                /*
                 * if this is the first item, scrolling from left to
                 * right should navigate in the surrounding ViewPager
                 */
                if (this.getCurrentItem() == 0) {
                    // swiping from left to right (->)?
                    if (this.lastX <= ev.getX() && !this.slidingRight) {
                        // make the parent touch interception active -> parent pager can swipe
                        this.getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        /*
                         * if the first swipe was from right to left, dont listen to swipes
                         * from left to right. this fixes glitches where the user first swipes
                         * right, then left and the scrolling state gets reset
                         */
                        this.slidingRight = true;

                        // save the current x position
                        this.lastX = ev.getX();
                        this.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                } else
                /*
                 * if this is the last item, scrolling from right to
                 * left should navigate in the surrounding ViewPager
                 */
                if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
                    // swiping from right to left (<-)?
                    if (this.lastX >= ev.getX() && !this.slidingLeft) {
                        // make the parent touch interception active -> parent pager can swipe
                        this.getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        /*
                         * if the first swipe was from left to right, dont listen to swipes
                         * from right to left. this fixes glitches where the user first swipes
                         * left, then right and the scrolling state gets reset
                         */
                        this.slidingLeft = true;

                        // save the current x position
                        this.lastX = ev.getX();
                        this.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }

                break;
        }

        super.onTouchEvent(ev);
        return true;
    }

}

希望这有助于未来的人!


11
投票

如果子视图搜索器位于末尾,则滚动父视图

protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if(v != this && v instanceof ViewPager) {
        int currentItem = ((ViewPager) v).getCurrentItem();
        int countItem = ((ViewPager) v).getAdapter().getCount();
        if((currentItem==(countItem-1) && dx<0) || (currentItem==0 && dx>0)){
            return false;
        }
        return true;
    }
    return super.canScroll(v, checkV, dx, x, y);
}

5
投票

首先以这种方式创建自定义ViewPager类:

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if(v instanceof ViewPager) {
            return true;
        }
        return super.canScroll(v, checkV, dx, x, y);
    }
}

方法canScroll的return(boolean)将告诉您ViewPager的更改页面的水平手势是否需要位于片段的右边界或左边界(true),或者它是否适用于完整片段屏幕(false)。例如,如果你想要只有你的第一个片段使用右边框移动到下一个片段,因为第一个片段有另一个水平滚动事件,这将是覆盖方法canScroll的代码:

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if(v instanceof ViewPager) {
        int currentItem = ((ViewPager) v).getCurrentItem();
        if((currentItem==0)){
            return true;
        }
        return false;
    }
    return super.canScroll(v, checkV, dx, x, y);
}

最后一步将在您的主类中使用您的CustomViewPager类:

ViewPager myPager= (CustomViewPager)myContext.findViewById(R.id.myCustomViewPager);

和xml:

<my.cool.package.name.CustomViewPager
        android:id="@+id/myCustomViewPager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

0
投票

我通过创建两个自定义ViewPager的继承者来解决此任务。在我的情况下 - OuterViewPager和InnerViewPager。

public class InnerViewPager extends ViewPager
{
    private int mPrevMoveX;

    public InnerViewPager(Context context)
    {
        super(context);
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mPrevMoveX = (int) event.getX();

                return super.onTouchEvent(event);

            case MotionEvent.ACTION_MOVE:
                int distanceX = mPrevMoveX - (int) event.getX();
                mPrevMoveX = (int) event.getX();

                boolean canScrollLeft = true;
                boolean canScrollRight = true;

                if(getCurrentItem() == getAdapter().getCount() - 1)
                {
                    canScrollLeft = false;
                }

                if(getCurrentItem() == 0)
                {
                    canScrollRight = false;
                }

                if(distanceX > 0)
                {
                    return canScrollRight;
                }
                else
                {
                    return canScrollLeft;
                }
        }

        return super.onInterceptTouchEvent(event);
    }

    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                mPrevMoveX = (int) event.getX();

                return super.onTouchEvent(event);

            case MotionEvent.ACTION_MOVE:
                int distanceX = mPrevMoveX - (int) event.getX();
                mPrevMoveX = (int) event.getX();

                boolean canScrollLeft = true;
                boolean canScrollRight = true;

                if(getCurrentItem() == getAdapter().getCount() - 1)
                {
                    canScrollLeft = false;
                }

                if(getCurrentItem() == 0)
                {
                    canScrollRight = false;
                }

                if(distanceX > 0)
                {
                    super.onTouchEvent(event);
                    return canScrollLeft;
                }
                else
                {
                    super.onTouchEvent(event);
                    return canScrollRight;
                }
        }

        return super.onTouchEvent(event);
    }
}


public class OuterViewPager extends ViewPager
{
    private int mPrevMoveX;


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

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

    private void init()
    {
        setOnPageChangeListener(new CustomPageChangeListener());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        switch (ev.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mPrevMoveX = (int) ev.getX();
                return super.onInterceptTouchEvent(ev);

            case MotionEvent.ACTION_MOVE:

                /*there you should get currentInnerPager - instance of InnerPager on current page of instance of OuterPager*/

                int distanceX = mPrevMoveX - (int) ev.getX();
                mPrevMoveX = (int) ev.getX();

                boolean canScrollLeft = true;
                boolean canScrollRight = true;

                if(currentInnerPager.getCurrentItem() == currentInnerPager.getAdapter().getCount() - 1)
                {
                    canScrollLeft = false;
                }

                if(currentInnerPager.getCurrentItem() == 0)
                {
                    canScrollRight = false;
                }

                if(distanceX > 0)
                {
                    return !canScrollLeft;
                }
                else
                {
                    return !canScrollRight;
                }
        }

        return super.onInterceptTouchEvent(ev);
    }
}

外部寻呼机仅在最后一页的内部寻呼机开始向左滚动。反之亦然。


0
投票

我只是测试这个案例,你可以在没有额外工作的情况下完成它,下面是我的演示

public class MainActivity extends AppCompatActivity {

    public static final String TAG = "TAG";
    ViewPager parentPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        initViews();
        initData();

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    private void initViews() {
        parentPager = (ViewPager) findViewById(R.id.parent_pager);
    }

    private void initData() {
        List<ViewPager> pagers = new ArrayList<ViewPager>();
        for(int j = 0; j < 3; j++) {
            List<LinearLayout> list = new ArrayList<LinearLayout>();
            for (int i = 0; i < 5; i++) {
                LinearLayout layout = new LinearLayout(this);
                TextView textView = new TextView(this);
                textView.setText("This is the" + i + "th page in PagerItem" + j);
                layout.addView(textView);
                textView.setGravity(Gravity.CENTER);
                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) textView.getLayoutParams();
                params.gravity = Gravity.CENTER;
                list.add(layout);
            }
            MyViewPagerAdapter adapter = new MyViewPagerAdapter(list);
            final ViewPager childPager = (ViewPager) LayoutInflater.from(this).inflate(R.layout.child_layout, null).findViewById(R.id.child_pager);
            childPager.setAdapter(adapter);
            childPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    Log.d(TAG, "onPageScrolled: position: " + position + ",   positionOffset: " + positionOffset);
                }

                @Override
                public void onPageSelected(int position) {

                }

                @Override
                public void onPageScrollStateChanged(int state) {

                }
            });
            pagers.add(childPager);
        }
        MyParentViewPagerAdapter parentAdapter = new MyParentViewPagerAdapter(pagers);
        parentPager.setAdapter(parentAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    class MyViewPagerAdapter extends PagerAdapter {

        private List<LinearLayout> data;

        public MyViewPagerAdapter(List<LinearLayout> data) {
            this.data = data;
        }

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

        @Override
        public int getItemPosition(Object object) {
            return data.indexOf(object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            LinearLayout linearLayout = data.get(position);
            container.addView(linearLayout);
            return data.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            LinearLayout layout = data.get(position);
            container.removeView(layout);
            layout = null;
        }

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

    class MyParentViewPagerAdapter extends PagerAdapter {

        private List<ViewPager> data;

        public MyParentViewPagerAdapter(List<ViewPager> data) {
            this.data = data;
        }

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

        @Override
        public int getItemPosition(Object object) {
            return data.indexOf(object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ViewPager pager = data.get(position);
            if(pager.getParent() != null) {
                ((ViewGroup) pager.getParent()).removeView(pager);
            }
            container.addView(pager);
            return data.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ViewPager pager = data.get(position);
            container.removeView(pager);
            pager = null;
        }

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

xml很简单,外部ViewPager在我的主要布局和内部ViewPager在另一个LinearLayout


0
投票

我不明白为什么你不创建一个视图寻呼机并使实例化项目逻辑从不同来源获取数据,这将使你达到你的目标同样我没有看到你需要2 viewpagers的情况

ViewPager1[0] ViewPager2[0] = page 0  (position/2) = 0
ViewPager1[0] ViewPager2[1] = page 1  ((position-1)/2) = 0
ViewPager1[1] ViewPager2[0] = page 2  (position/2) = 1
ViewPager1[1] ViewPager2[1] = page 3 ((position-1)/2) = 1
ViewPager1[2] ViewPager2[0] = page 4  (position/2) = 2
ViewPager1[2] ViewPager2[1] = page 5 ((position-1)/2) = 2

并在代码中:

@Override
public Object instantiateItem(View collection, int position) {
    LayoutInflater inflater = THISCLASSNAME.this.getLayoutInflater();
    View v = null;
    if(position%2 == 0) {
         // viewpager 1 code
         int vp1pos = position/2;
         v = inlater.inflate(R.layout.somelayout, collection, false);
         Button b = (Button)v.findViewById(R.id.somebutton);
         b.setText(array1[vp1pos]);
    } else {
         int vp2pos = (position-1)/2;
         v = inlater.inflate(R.layout.somelayout, collection, false);
         Button b = (Button)v.findViewById(R.id.somebutton);
         b.setText(array2[vp2pos]);
    }

    ((DirectionalViewPager) collection).addView(v, 0);

    return v;
}

这样你几乎有2个viewpagers逻辑,你可以定制它比我给你的想法更多

附:我在这里编码,所以如果有字符错误或拼写错误原谅我。

希望这有帮助,如果你得到更具体的,需要更多的帮助来添加我的答案的评论,我会修改它

© www.soinside.com 2019 - 2024. All rights reserved.