android: ViewPager 和 Horizo​​ntalScrollVIew

发布于 2024-11-27 17:59:28 字数 297 浏览 3 评论 0原文

我的 ViewPager 中有一个 Horizo​​ntalScrollView。我设定 requestDisallowInterceptTouchEvent(true); 对于 Horizo​​ntalScrollViewViewPager 有时仍然会拦截触摸事件。是否还有另一个命令可以用来防止视图的父级和祖先拦截触摸事件?

注意:Horizo​​ntalScrollView 仅占据屏幕的一半。

I have a HorizontalScrollView inside my ViewPager. I set
requestDisallowInterceptTouchEvent(true); for the HorizontalScrollView but the ViewPager is still sometimes intercepting touch events. Is there another command I can use to prevent a View's parent and ancestors from intercepting touch events?

note: the HorizontalScrollView only occupies half the screen.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

苍暮颜 2024-12-04 17:59:28

我也有同样的问题。我的解决方案是:

  1. 创建 ViewPager 的子类并添加一个名为 childId 的属性。
  2. childId 属性创建一个 setter,并设置 Horizo​​ntalScrollView 的 id。
  3. 覆盖 ViewPager 子类中的 onInterceptTouchEvent(),如果 childId 属性大于 0,则获取该子级,并且如果事件位于 中>Horizo​​ntalScrollView 区域返回 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);

希望有帮助

I had the same problem. My solution was:

  1. Make a subclass of ViewPager and add a property called childId.
  2. Create a setter for the childId property and set the id of the HorizontalScrollView.
  3. Override onInterceptTouchEvent() in the subclass of ViewPager and if the childId property is more than 0 get that child and if the event is in HorizontalScrollView area return false.

Code

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

In onCreate() method

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

Hope this help

魂ガ小子 2024-12-04 17:59:28

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

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

Thanks for the reply. I modified your solution a bit and managed to make nested ViewPagers work:

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;
    }
}
装迷糊 2024-12-04 17:59:28

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

我分析了ViewPager的源代码,发现他们使用这个名为“canScroll”的方法来确定子视图是否可以滚动。幸运的是,它不是静态的也不是最终的,所以我可以覆盖它。在该方法中,调用了“boolean ViewCompat.canScrollHorizo​​ntally(View, int)”(对于 SDK < 14 总是返回 false 的方法)。我的解决方案是:

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 为 true,这就是为什么我们也应该检查这一点。

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

So, I know this question is pretty old, but I've come with a solution which I think has some extra advantages over the one posted here. One situation that could happen is that I could have more than one HorizontalScrollView inside my ViewPager (which I do), and then I would need to provide tons of Ids to the ViewPager so it can always check for child touch conflicts.

I analyzed the source from the ViewPager, and found that they use this method called "canScroll" to determine if a child view can be scrolled. Luckily, it wasn't made static nor final, so I could just override it. Inside that method that the "boolean ViewCompat.canScrollHorizontally(View, int)" was called (the one that always returns false for SDK < 14). My solution then was:

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

So, if you have a different class of View inside your ViewPager that should also receive horizontal drags, just change the customCanScroll(View) so you can return if the touch should not be intercepted or not.

The boolean checkV is true if the current view should also be checked for 'scrollability', that's why we should also check for that.

Hope this helps future issues like this (or if there is a better solution, or an official one from the Android platform, let me know).

筱武穆 2024-12-04 17:59:28

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

其实如果你阅读了关于在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;
}

I don't know if you've solved the problem already or not. But none of the answers above work properly. So I think it's necessary for those people who might be searching the solutions with suffering.

In fact, the solution is quite simple if you read the official document about handling touch events in ViewGroup.

First you need to understand the usage of

ViewParent.requestDisallowInterceptTouchEvent(boolean), Call this upon a parent View to indicate that it should not intercept touch events with onInterceptTouchEvent(MotionEvent).

Then you need to intercept the events and make sure that the parent ViewGroup of your HorizontalScrollView won't dispatch the events any further.

So, set an OnToushListener for you HorizontalScrollView. Detecting the touch down and move events, then set the requestDisallowInterceptTouchEvent(true) to it's parent(ViewPager or sth else) to make sure the events would be handled by the HorizontalScrollView.

Of course you could put these code below into your CustomHorizontalScrollView components so that it can be reused.

Hoping it helps. ;-)

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;
}
蓝戈者 2024-12-04 17:59:28

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

创建一个自定义 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;
    }

});

Here is another solution that worked for me,

Create a custom 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;
    }



}

And then add OnTouchListener to your item, whether it was a HorizontalScrollView or Gallery or anything else that you don't want the viewpager to scroll when it is touched.

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

});
轻拂→两袖风尘 2024-12-04 17:59:28

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

Fede's solution didn't work very well for me. My HorizontalScrollVIew had areas where it didn't catch any inputs (click, swipe..). I guess getHitRect() have to do something with that. After replacing it with following code everything worked fine.

It is possible to add multiple HorizontalScrollVIew views or any other views that requires focus too.

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

}

In XML you will have something like this:

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

In onCreate() method:

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

In OnTouchListener() method for view you have added to CustomViewPager children:

case MotionEvent.ACTION_DOWN:
     v.setPressed(true);
     .
     .
     .
case MotionEvent.ACTION_UP:
     v.setPressed(false);
葬シ愛 2024-12-04 17:59:28

我在查看页面组中第一页底部的小画廊视图时遇到问题。滑动图库视图焦点将在移动图库项目或视图分页器之间跳转。

重写上面列出的 ViewPager 并设置 requestDisallowInterceptTouchEvent 解决了我的问题。滑动图库视图时页面不再更改。对于那些使用 galleryviews 和 viewpager 的人来说,它是一个重要的解决方案。

I was having trouble with a small gallery view on the foot of my first page in a viewpager group. On swiping the gallery view focus would jump between moving the gallery items or the viewpager.

Overriding ViewPager as listed above and setting requestDisallowInterceptTouchEvent solved my problem. The pages no longer change when swiping the galleryview. Think its an important solution for those using galleryviews with viewpager.

愁杀 2024-12-04 17:59:28

因此,我修改了 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;
    }
}

}

So, I modified Fede's answer because it was disabling the touch in the same area in the pages around the one I wanted. Also, I made it able to add multiple views because I'm dealing with a lot of graphics and interactive views that use horizontal drag/drop interactivity. This way you can add

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

}

℡Ms空城旧梦 2024-12-04 17:59:28

经过大量测试后,我发现了这一点。
在 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);
}

after test a lot of solution, I find this.
in 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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文