所以这是我的布局结构:
- RelativeLayout (full screen)
- FrameLayout (full screen)
- Button (in the middle of the screen)
- RecyclerView (align parent bottom but with very big padding top to
capture the scrolling also in the top of the screen, so it's basically acting like a full screen `RecyclerView`)
是的,这些观点相互重叠。
由于RecyclerView
是最顶层的视图,它会捕获屏幕上的所有触摸事件,并吞下它们,防止任何触摸“通过它”到它下面的底层视图。 (注意:根据基础观点,我不是指RecyclerView
个孩子,而是其他rootview's
个孩子)
我已经阅读了大量关于传播触摸事件和防止吞咽触摸事件等的stackoverflow帖子,即使看起来非常简单的任务,我也无法达到以下效果:
我希望我的RecyclerView
捕获触摸事件,因此它会滚动或者其他任何内容。但我希望rootView
认为RecyclerView
没有捕获事件,并继续将其传递给其他子项(rootview
'
儿童)。
这是我试图做的事情:
1 即可。覆盖dispatchTouchEvent
的{{1}}来执行其逻辑并返回false,因为它没有调度其触摸事件,因此rootview将继续迭代以通过其子视图进行触摸。
RecyclerView
发生了什么:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
return false;
}
仍在运作,但仍会吞下所有触摸事件(与以前一样)
2 即可。覆盖RecyclerView
的{{1}}以执行其逻辑并返回false。 (注意:我知道它似乎不是解决方案,但我试过了)
onTouchEvent
发生了什么:
与#1相同的结果
我已经用同样的想法做了一些调整,但是它们没有那么好用,所以我现在有点无能为力,并希望得到你们的帮助!
答案 0 :(得分:0)
我不确定这是多么高效/有效/好的解决方案,但这就是我想到的:如果你在根视图中监听触摸事件怎么办?(在你的情况下为RelativeLayout
) ,并将触摸事件发送给该布局的所有子项,但RecyclerView
?
public class MyRelativeLayout extends RelativeLayout {
...
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final boolean intercept = super.onInterceptTouchEvent(ev);
// getChildCount() - 1, because `RecyclerView` is the last child
for (int i = 0, size = getChildCount() - 1; i < size; i++) {
View v = getChildAt(i);
v.dispatchTouchEvent(MotionEvent.obtain(ev));
}
return intercept;
}
}
这似乎应该有用。
答案 1 :(得分:0)
我有两个建议:
第一个更简单,但假设您要点击Button
,它可以显示在 RecyclerView
上方,如果是这种情况,您只需更改布局&#39 ;顺序:
- RelativeLayout (full screen)
- FrameLayout (full screen)
- RecyclerView
- Button (in the middle of the screen)
如果你需要更通用的东西,可以这样做:
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int [] pos = new int[2];
recyclerView.getLocationOnScreen(pos);
Rect rect = new Rect();
button.getHitRect(rect);
if (rect.contains((int) event.getX() + pos[0], (int) event.getY() + pos[1])) {
button.onTouchEvent(event);
}
return false;
}
});
您需要单独处理所有视图,这不是很好,但它在我的测试中有效。
注意:如果您已将addOnItemTouchListener
添加到RecyclerView
,则需要将所述功能添加到onInterceptTouchEvent
(或两个地方,取决于您的商品和RecyclerView
)。
答案 2 :(得分:0)
我不太了解您的问题,但如果目标是不允许RecyclerView
使用任何和所有触摸事件,那么只需为android:elevation="1dp"
设置FrameLayout
和Override
dispatchTouchEvent()
一样:
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
Logger.log("CustomFrameLayout : dispatchTouchEvent");
boolean result = super.dispatchTouchEvent(event);
((View) getParent()).findViewById(R.id.recyclerView).dispatchTouchEvent(event);
return result; // This part can be changed according to requirement.
}
这可能会或可能不会产生一些不良的副作用,因为我只是自己提出它,但是我已经检查过它没有。
我测试过的案例:
更新:
为什么FrameLayout需要提升?
android:elevation
提供了任何布局的z轴位置的方法。对于为什么有必要这样做,回答我必须深入研究并给你一个冗长的解释(已经写了很多东西)就足够了假设MotionEvent
按照一些基本规则逐步减少视图:从root到它的子节点,孩子首先获得事件(检查事件是否属于它)逐渐减少它们在 xy轴中的位置的基础,然后是它们在 z轴中的顺序(前一个首先获得事件)。
因此,在您检测到MotionEvent
时,会将其传递给RelativeLayout
(RootView),其中有3个子项,Button
将因位置而被过滤。现在您有2 Views
个FrameLayout
和RecyclerView
。 RelativeLayout
最底层的孩子位于 z轴之上,这意味着RecyclerView
事件将首先发送给它(这将自然消耗事件)。但是,如果出现未使用事件(第一次尝试)的情况,则该事件将被传递给下一个孩子View
,直到不再有子View
为止,此时parent(RelativeLayout
)将假定该事件属于自身并调用其自己的onInterceptTouchEvent()
。在此过程中,如果View
消耗了该事件,则所有后续调用都将直接传递给它(原因是您只能将其中一个调用FrameLayout
或RecyclerView
工作)。
上述所有解释均指规则手册 ,只有一个
View
会使用event
。
我做了什么
我只是提升 FrameLayout
所以它将成为事件的官方接收者。覆盖dispatchTouchEvent()
并将相同的事件发送到其邻近的RecyclerView
,认为该事件已通过常规模式发送给它。
现在解释完毕后
评论//这部分可以根据要求进行更改。
更好,可能没有错误的方法
boolean result2 = ((View) getParent()).findViewById(R.id.recyclerView).dispatchTouchEvent(event);
return result || result2;
因此,如果任何Views
消费了该事件,则 root 认为它已被消费。