【问题标题】:ViewPager inside ViewPagerViewPager 内的 ViewPager
【发布时间】:2011-10-29 06:10:35
【问题描述】:

我想创建一个 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


【解决方案1】:

覆盖父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);
}

【讨论】:

  • +1,谢谢您节省了我的时间。我需要相同的解决方案来滚动 ViewPager 中的图库。它做到了。
  • 谢谢。我使用其中的一部分回答了我自己的问题,其中我在 ViewPager 页面中有 ScrollViews 并且在 ScrollViews 内部有一个 ViewPager。如果有人需要这样做:stackoverflow.com/questions/17053270/…
  • 这行得通,但我想做这样的。如果子viewpager在最后,滚动父viewpager。
【解决方案2】:

试试这个:

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

【讨论】:

  • 掌握 onInterceptTouchEvent 是所有嵌套可滚动视图的秘密。
  • 效果很好。如果您在 xml 中构建查看器,请确保将 android.support.v4.view.ViewPager 更改为 -> mypackage.CustomViewPager。唯一的问题是第一次触摸事件没有切换,所以......
  • 不是必须传递的父(而不是子)寻呼机ID吗?为了使它工作,我必须得到这样的寻呼机: ViewPager pager = (ViewPager)((Activity)mContext).findViewById(mParentPagerId);否则非常感谢,它为我节省了几个小时!
  • 我的 mainActivity 中有一个带有 4 个片段的 viewPager,我实现了 customViewPager 来禁用滑动,它工作正常。但是,我的第一个片段内部有一个 viewPager,我还为此实现了 customViewPager 并且它仍然滑动?有任何想法吗?提前致谢!
  • 通过为 Fragment 1 中的嵌套 viewPager 设置 setPagingEnabled(false) 解决了这个问题。
【解决方案3】:

我搜索了很长时间以使另一个 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;
    }

}

希望这对将来的某人有所帮助!

【讨论】:

  • 嗯,如何为 ViewPager 页面中的项目设置 OnClickListener?现在设置一个会禁用 ViewPager 的所有滑动。
  • 我遇到了同样的问题,并尝试在此 ViewPager 中执行自定义点击侦听器。这就是我所做的:我将 x 和 y 位置保存在 ACTION_DOWN 上。然后在移动时检查当前 x 或 y 值是否离起始位置太远。如果是,则将布尔值设置为 false。然后在 ACTION_UP 上检查布尔值,如果为真,则单击,如果为假,则滚动..如果您找到其他解决方案,我会很高兴听到它 :)
  • 嗨,这个解决方案我也有类似的问题。我在另一个视图寻呼机中有一个视图寻呼机。为了在子 viewpager 中启用页面更改,我使用了 aove 代码并且工作正常。但我在子查看器中有一个按钮。如果用户尝试通过触摸按钮来滑动子视图分页器,则父视图分页器页面将更改,而不是子视图分页器。请给我一些解决方案。
【解决方案4】:

如果子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);
}

【讨论】:

    【解决方案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 方法的返回值(布尔值)将告诉您更改 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" />
    

    【讨论】:

      【解决方案6】:

      【讨论】:

      • 这对我来说是 viewpager2 中可滚动元素的完美解决方案。谢谢@Westy92
      • 这是处理嵌套滚动的最佳方式
      • 你把我从战争中救了出来。
      【解决方案7】:

      我通过创建两个自定义 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);
          }
      }
      

      仅当内部页面位于最后一页时,外部页面才会开始向左滚动。反之亦然。

      【讨论】:

        【解决方案8】:

        我只是测试这个案例,你可以不用额外的工作,下面是我的演示

        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 和在另一个 LinearLayout 中的内部 ViewPager

        【讨论】:

          【解决方案9】:

          我不明白您为什么不只创建 1 个视图寻呼机并创建实例化项目逻辑以从不同来源获取数据,这将使您同样达到目标 我没有看到你需要 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 逻辑,你可以自定义它而不是我只是给你的想法

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

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

          【讨论】:

            猜你喜欢
            • 2015-08-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-04-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多