【问题标题】:android: ViewPager and HorizontalScrollVIewandroid: ViewPager 和 Horizo​​ntalScrollVIew
【发布时间】:2011-10-18 16:58:27
【问题描述】:

我的ViewPager 中有一个HorizontalScrollView。我设置 requestDisallowInterceptTouchEvent(true); 用于HorizontalScrollViewViewPager 有时仍会拦截触摸事件。是否有其他命令可以用来防止 View 的父级和祖先级拦截触摸事件?

注意:HorizontalScrollView 只占屏幕的一半。

【问题讨论】:

    标签: android events view scroll touch


    【解决方案1】:

    我遇到了同样的问题。我的解决方案是:

    1. 创建ViewPager 的子类并添加一个名为childId 的属性。
    2. childId 属性创建一个setter 并设置HorizontalScrollView 的ID。
    3. ViewPager 的子类中覆盖onInterceptTouchEvent(),如果childId 属性大于0,则获取该子类,如果事件在HorizontalScrollView 区域中,则返回false。

    代码

    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) {
                View scroll = findViewById(childId);
                if (scroll != null) {
                    Rect rect = new Rect();
                    scroll.getHitRect(rect);
                    if (rect.contains((int) event.getX(), (int) event.getY())) {
                        return false;
                    }
                }
            }
            return super.onInterceptTouchEvent(event);
        }
    
        public void setChildId(int id) {
            this.childId = id;
        }
    }
    

    onCreate()方法中

    viewPager.setChildId(R.id.horizontalScrollViewId);
    adapter = new ViewPagerAdapter(this);
    viewPager.setAdapter(adapter);
    

    希望有帮助

    【讨论】:

    • 谢谢你!不过我确实做了一个修改。我认为您不需要在翻页时更新 setChildId(),或者至少在使用 FragmentPagerAdapter 时需要更新。当用户更改页面时,视图消失了,findViewById(childId) 将消失。因此,您只需调用一次setChildId(),它就会释放onPageChangeListener() 用于其他用途。
    • 我同意,只需 setChildId() 和 ViewPagers 就可以了。此页面上的其他人在忽略您放入视图的第一次触摸方面存在问题。并记得更新您的 xml,以便 viewpager 指向您的包。
    • @libathos 你需要恢复 ImageView 对象(以 findViewById(imageViewId) 为例)然后调用 imageViewObject.getHitRect(rect) (是 View 类的方法)
    • @感谢您这么快回复我,我会试一试并告诉您!
    【解决方案2】:

    感谢您的回复。我稍微修改了您的解决方案并设法使嵌套的 ViewPagers 工作:

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

    【讨论】:

    • 你毕竟不是一个菜鸟 :) - 很好的解决方案。
    【解决方案3】:

    我在浏览器组的第一页脚下遇到了一个小的画廊视图问题。在滑动图库视图时,焦点会在移动图库项目或查看器之间跳转。

    覆盖上面列出的 ViewPager 并设置 requestDisallowInterceptTouchEvent 解决了我的问题。滑动图库视图时页面不再更改。认为它对于那些使用带有 viewpager 的画廊视图的人来说是一个重要的解决方案。

    【讨论】:

      【解决方案4】:

      这也适用于滚动视图中的 viewpager:https://stackoverflow.com/a/2655740/969325

      【讨论】:

        【解决方案5】:

        所以,我知道这个问题已经很老了,但我提出了一个解决方案,我认为它比这里发布的问题有一些额外的优势。可能发生的一种情况是,我的 ViewPager 中可能有多个 Horizo​​ntalScrollView(我确实这样做了),然后我需要向 ViewPager 提供大量 Id,以便它始终可以检查子触摸冲突。

        我从ViewPager分析了源码,发现他们使用了这个叫做“canScroll”的方法来判断一个子视图是否可以滚动。幸运的是,它既不是静态的也不是最终的,所以我可以覆盖它。在该方法中调用了“boolean ViewCompat.canScrollHorizo​​ntally(View, int)”(对于 SDK

        public class CustomViewPager extends ViewPager {
        
            public CustomViewPager(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
            public CustomViewPager(Context context) {
                super(context);
            }
            protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
                return super.canScroll(v, checkV, dx, x, y) || (checkV && customCanScroll(v));
            }
            protected boolean customCanScroll(View v) {
                if (v instanceof HorizontalScrollView) {
                    View hsvChild = ((HorizontalScrollView) v).getChildAt(0);
                    if (hsvChild.getWidth() > v.getWidth())
                        return true;
                }
                return false;
           }
        }
        

        因此,如果您的 ViewPager 中有不同类别的 View 也应该接收水平拖动,只需更改 customCanScroll(View) 以便在不应该拦截触摸时返回。

        如果还应该检查当前视图的“可滚动性”,则布尔值 checkV 为真,这就是为什么我们也应该检查它。

        希望这对未来的此类问题有所帮助(或者如果有更好的解决方案,或者来自 Android 平台的官方解决方案,请告诉我)。

        【讨论】:

        • 我实现的方式实际上并不是因为它显然是 Android 上的默认设置。默认情况下不检查子项和滚动视图的宽度,因此它可能只是类似于“return v instanceof Horizo​​ntalScrollView;”
        • 此解决方案非常适合另一个 ViewPager 中的 ViewPager。谢谢
        【解决方案6】:

        这是另一个对我有用的解决方案,

        创建自定义 ViewPager

        public class NoScrollViewPager extends ViewPager {
        
            private boolean scrollDisable = false;
        
        
            public NoScrollViewPager(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
        
            @Override
            public boolean onInterceptTouchEvent(MotionEvent ev) {
                if(scrollDisable) {
                    return false;
                } else {
                    return super.onInterceptTouchEvent(ev);
                }
            }
        
            public void disableScroll() {
                scrollDisable = true;
            }
        
            public void enableScroll() {
                scrollDisable = false;
            }
        
        
        
        }
        

        然后将 OnTouchListener 添加到您的项目中,无论是 Horizo​​ntalScrollView 还是 Gallery 或其他任何您不希望 viewpager 在被触摸时滚动的东西。

        gf.setOnTouchListener(new OnTouchListener() {
        
            @Override
            public boolean onTouch(View arg0,
                    MotionEvent ev) {
                switch(ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    viewPager.disableScroll();
                    break;
                case MotionEvent.ACTION_UP:
                    viewPager.enableScroll();
                    break;
                }
                return false;
            }
        
        });
        

        【讨论】:

          【解决方案7】:

          我发现如果您在 ScrollView 中有一个 ViewPager 并且您希望它始终保持焦点,当您触摸它并且 ScrollView 永远不会获得焦点时,这样做。

              mViewPager= (ViewPager) findViewById(R.id.pager);
              mViewPager.setAdapter(adapter);
              mViewPager.setOnTouchListener(new View.OnTouchListener() {
          
                  @Override
                  public boolean onTouch(View v, MotionEvent event) {
                      mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
                      return false;
                  }
              });
          

          【讨论】:

            【解决方案8】:

            Fede 的解决方案对我来说效果不佳。我的 Horizo​​ntalScrollVIew 有没有捕捉到任何输入的区域(单击、滑动……)。我猜 getHitRect() 必须对此做些什么。用以下代码替换它后,一切正常。

            可以添加多个 Horizo​​ntalScrollVIew 视图或任何其他需要焦点的视图。

            public class CustomViewPager extends ViewPager {
            
                private ArrayList<Integer> children; 
            
                public CustomViewPager(Context context, AttributeSet attrs) {
                    super(context, attrs);
                    children = new ArrayList<Integer>();
                }   
            
                @Override
                public boolean onInterceptTouchEvent(MotionEvent event) {
                    if (children.size() > 0) {
                        for(int i=0; i<children.size(); i++){
                            View scroll = findViewById(children.get(i));
                            if (scroll != null & scroll.isPressed()) {
                                return false;
                            }
                        }
                    }
                    return super.onInterceptTouchEvent(event);
                }
            
                public void addChildId(int id) {
                   children.add(id);
                }
            
            }
            

            在 XML 中你会有这样的东西:

            <form.paginator.lib.CustomViewPager
                  android:id="@+id/pager"
                  android:layout_width="fill_parent"
                  android:layout_height="150dp" />
            

            在 onCreate() 方法中:

            mPager = (CustomViewPager) findViewById(R.id.pager);
            mPager.setAdapter(mAdapter);
            mPager.addChildId(R.id.avatarImageView);
            

            在视图的 OnTouchListener() 方法中,您已添加到 CustomViewPager 子项:

            case MotionEvent.ACTION_DOWN:
                 v.setPressed(true);
                 .
                 .
                 .
            case MotionEvent.ACTION_UP:
                 v.setPressed(false);
            

            【讨论】:

              【解决方案9】:

              我不知道你是否已经解决了这个问题。但是上面的答案都没有正常工作。所以我认为对于那些可能正在苦苦寻找解决方案的人来说这是必要的。

              其实解决方法很简单,看官方文档ViewGroup处理触摸事件。

              首先你需要了解

              的用法

              ViewParent.requestDisallowInterceptTouchEvent(boolean),在父视图上调用此函数以指示它不应使用onInterceptTouchEvent(MotionEvent) 拦截触摸事件。

              然后你需要拦截事件并确保你的 Horizo​​ntalScrollView 的父 ViewGroup 不会再分派事件。

              所以,为你的 Horizo​​ntalScrollView 设置一个 OnToushListener。检测触摸和移动事件,然后将 requestDisallowInterceptTouchEvent(true) 设置为其父级(ViewPager 或其他)以确保事件将由 Horizo​​ntalScrollView 处理。

              当然,您可以将下面的这些代码放入您的 CustomHorizo​​ntalScrollView 组件中,以便重复使用。

              希望对您有所帮助。 ;-)

              horizontalScrollView.setOnTouchListener(new View.OnTouchListener() {
              float rawX;
              int mTouchSlop =  ViewConfiguration.get(getActivity()).getScaledTouchSlop();
              
              @Override
              public boolean onTouch(View v, MotionEvent event) {
                  switch (event.getActionMasked()) {
                      case MotionEvent.ACTION_DOWN:
                          v.getParent().requestDisallowInterceptTouchEvent(true);
                          rawX = event.getRawX();
                          break;
                      case MotionEvent.ACTION_CANCEL:
                      case MotionEvent.ACTION_UP:
                          v.getParent().requestDisallowInterceptTouchEvent(false);
                          rawX = 0f;
                          break;
                      case MotionEvent.ACTION_MOVE:
                          if (Math.abs(rawX - event.getRawX()) > mTouchSlop)
                              v.getParent().requestDisallowInterceptTouchEvent(true);
                          break;
                  }
                  return false;
              }
              

              【讨论】:

              • 结果:无法滚动
              【解决方案10】:

              在测试了很多解决方案后,我发现了这个。 在 MyPagerView 中:

              @Override
              public boolean onTouchEvent(MotionEvent event) {
                  if (childId > 0) {
                      View scroll = findViewById(childId);
                      if (scroll != null && mGestureDetector.onTouchEvent(event)) {
                          return false;
                  }
              }
              return super.onTouchEvent(event);
              }
              
              @Override
              public boolean onInterceptTouchEvent(MotionEvent event) {
                  if (childId > 0) {
                          HorizontalScrollView scroll = (HorizontalScrollView)findViewById(childId);
                          if (scroll != null && mGestureDetector.onTouchEvent(event)) {
                              return false;
                          }
                  }
                  return super.onInterceptTouchEvent(event);
              }
              

              【讨论】:

                【解决方案11】:

                所以,我修改了 Fede 的答案,因为它禁用了我想要的页面中同一区域的触摸。此外,我让它能够添加多个视图,因为我正在处理大量使用水平拖放交互性的图形和交互视图。这样你就可以添加

                public class CustomViewPager extends ViewPager {
                
                private List<ViewFocus> lateralTouchViews = new ArrayList<>();
                public CustomViewPager(Context context, AttributeSet attrs) {
                    super(context, attrs);
                }
                
                @Override
                public boolean onInterceptTouchEvent(MotionEvent event) {
                    for(int i = 0; i< lateralTouchViews.size(); i++){
                        View scroll = findViewById(lateralTouchViews.get(i).id);
                        if (scroll != null && getCurrentItem() == lateralTouchViews.get(i).itemPosition) {
                            Rect rect = new Rect();
                            scroll.getHitRect(rect);
                            if (rect.contains((int) event.getX(), (int) event.getY())) {
                                return false;
                            }
                        }
                    }
                    return super.onInterceptTouchEvent(event);
                }
                
                public void addConflict(int id, int pageIndex) {
                    lateralTouchViews.add(new ViewFocus(id, pageIndex));
                }
                public class ViewFocus{
                    int id;
                    int itemPosition;
                
                    public ViewFocus(int id, int itemPosition){
                        this.id = id;
                        this.itemPosition = itemPosition;
                    }
                }
                

                }

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-09-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-03-31
                  • 1970-01-01
                  • 2014-02-13
                  相关资源
                  最近更新 更多