简体   繁体   English

ViewPager 内的 ViewPager

[英]ViewPager inside ViewPager

I would like to create a ViewPager (with three items) where each of its view is another ViewPager (with two items).我想创建一个 ViewPager(包含三个项目),其中每个视图都是另一个 ViewPager(包含两个项目)。 User then swipe items like this:用户然后像这样滑动项目:

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

How would that be possible?这怎么可能?

override canScroll in the parent ViewPager:在父 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);
}

Try this:尝试这个:

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

I searched a long time to make a ViewPager inside another ViewPager work and found the solution by "Android Noob" here.我搜索了很长时间以使另一个 ViewPager 中的 ViewPager 工作,并在这里通过“Android Noob”找到了解决方案。 Thank you very much for that!非常感谢你!

I wanted to share my solution, too.我也想分享我的解决方案。 I added the possibility to switch the swipe management to the surrounding ViewPager once the last (most right) element in the inner ViewPager is reached.一旦到达内部 ViewPager 中的最后一个(最右侧)元素,我添加了将滑动管理切换到周围 ViewPager 的可能性。 To prevent glitches, i also save the first swipe direction for the last elemen: ie if you swipe left first, a minimal right swipe doesnt reset the scroll state.为了防止出现故障,我还为最后一个元素保存了第一个滑动方向:即,如果您先向左滑动,则最小的向右滑动不会重置滚动 state。

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

}

Hope this helps someone in the future!希望这对将来的人有所帮助!

If the child viewpager is at the end, scroll the parent如果子viewpager在最后,滚动父viewpager

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

First create a custom ViewPager class in this way:首先这样创建一个自定义的ViewPager class:

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

The return (boolean) of the method canScroll will tell you if the horizontal gesture to change page for ViewPager needs to be in the right or left border of the fragment(true) or if it works for full fragment screen (false).方法 canScroll 的返回(布尔值)将告诉您更改 ViewPager 页面的水平手势是否需要位于片段的右边框或左边框(true),或者它是否适用于整个片段屏幕(false)。 If you want, for example, that only your first fragment use the right border to move to the next fragment because the first fragment has another horizontal scrolling event, this will be the code to overriding the method canScroll:例如,如果你希望只有你的第一个片段使用右边框移动到下一个片段,因为第一个片段有另一个水平滚动事件,这将是覆盖方法 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);
}

The Last Step will be to use your CustomViewPager class in your main class:最后一步是在主 class 中使用您的 CustomViewPager class:

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

and the xml:和 xml:

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

I solve this task by creating two custom ViewPager's inheritors.我通过创建两个自定义 ViewPager 的继承者来解决这个任务。 In my case - OuterViewPager and InnerViewPager.就我而言 - 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);
    }
}

Outer pager starts scroll left only when inner pager on last page.仅当内部寻呼机在最后一页时,外部寻呼机才开始向左滚动。 And vice versa.反之亦然。

I just test this case, you can make it without extra working, below is my demo我只是测试这个案例,你可以不用额外的工作,下面是我的演示

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

The xml is simple, outer ViewPager in my main layout and the inner ViewPager in another LinearLayout xml 很简单,我的主布局中的外部ViewPager和另一个LinearLayout中的内部ViewPager

I don't understand why don't you just create 1 view pager and make the instantiate item logic to get data from different sources, which would make you reach your goal equally I do not see a case where you need 2 viewpagers我不明白您为什么不只创建 1 个视图寻呼机并创建实例化项目逻辑以从不同来源获取数据,这将使您同样达到目标我看不到您需要 2 个视图viewpagers的情况

Example例子

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

and in the code:并在代码中:

@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;
}

this way you have virtually 2 viewpagers logic, you may customize it more than that I am just giving you ideas这样你几乎有 2 个viewpagers逻辑,你可以自定义它而不是我只是给你的想法

PS I coded this here so if there are character case mistakes or spelling mistakes forgive me. PS我在这里编码,所以如果有字符大小写错误或拼写错误,请原谅我。

hope this helps, if you get more specific and need more help to add a comment on my answer and I will amend it希望这会有所帮助,如果您变得更具体并且需要更多帮助以对我的答案添加评论,我会对其进行修改

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM