如何在Android上将视图的onClick事件传递给其父级?

130

我在一个布局中有一个TextView,它的背景是一个Selector。TextView的文本设置为从HTML中生成的Spanned。然后我使用LinkMovementMethod设置了TextView。

现在当我点击TextView时,点击事件不会发送到其父布局以触发Selector。

应该如何解决这个问题?

8个回答

246

通过使用 android:clickable="false"android:focusable="false" 或者 v.setClickable(false)v.setFocusable(false) 来声明您的TextView不可点击/聚焦。现在,点击事件应该会分发到TextView的父级。

注意:

为了实现这一点,您需要将点击添加到其直接的parent,或者将android:clickable="false"android:focusable="false"设置到其直接parent中,以便将监听器传递给更高一级的父级。


1
哦,伙计,如果我能够给这个+100,我会这样做的。我一直在尝试找出如何使点击仅在ViewPager中起作用,而不是在实现onTouch的TouchImageView适配器布局中起作用!这个解决方案像魔法一样奏效。 - JPM
2
场景是:我有一个布局,其中包含EditText、RelativeLayout、Spinner和ListView。当我在屏幕上除EditText和Spinner以外的任何地方触摸时,我想触发父布局的触摸事件。 - SweetWisher ツ
16
需要注意的重点是,如果在 TextView 上使用 TextView#setOnClickListener() 方法,即使它被声明为 android:clickable="false",甚至即使将 ClickListener 设置为 null (setOnclickListener(null)),它也会变得可点击。 - Christian García
1
我有这样的经验,如果我们将 clickable 和 focusable 设置为 false,则任何视图都会返回 false 来处理事件。因此,事件将传递给其父级。如果该父级也不想处理事件,则它们还应将 Focusable 和 Clickable 设置为 false。 - Zar E Ahmer
4
有趣的事实:如果在TextView中有android:inputType属性,即使clickable:false和focusable:false,它也会悄悄地消耗点击事件;在我的情况下,它是在样式中设置的,这让我花了30分钟才找出原因。 - Kirill Karmazin
显示剩余10条评论

53

我认为你需要使用以下方法之一,以便在事件发送到适当的组件之前拦截它:

Activity.dispatchTouchEvent(MotionEvent) - 这允许你的Activity在触摸事件被分派给窗口之前拦截所有触摸事件。

ViewGroup.onInterceptTouchEvent(MotionEvent) - 这允许ViewGroup在事件被分派给子View之前查看事件。

ViewParent.requestDisallowInterceptTouchEvent(boolean) - 调用此方法可通知父View不要在onInterceptTouchEvent(MotionEvent)中拦截触摸事件。

更多信息请参见此处

希望能帮到你。


1
有没有更简单的方法将事件传递到父视图以触发onClick和onLongClick,还是我必须自己实现它们? - shiami
看看这个:http://stackoverflow.com/questions/2136696/pass-event-to-parent-view 也许你想尝试像建议的那样将相同的点击监听器传递给其他实例,虽然我不知道它是否会起作用,但值得一试。 - Luis Miguel Serrano
Answer below is much cleaner - JPM
我应该实例化 MotionEvent 吗?请解释一下,@LuisMiguelSerrano - olegario
@olegario,通常您会使用 @Override 重新定义方法,例如 dispatchTouchEvent,以定义其新行为,这样您只需接收系统提供给您的 Activity(或应用程序的另一个组件)的 motion event,并根据自己的规则对其进行处理和/或转发到任意数量的子组件。类似于:@Override public boolean dispatchTouchEvent(MotionEvent ev) { ... } - Luis Miguel Serrano

25

有时只有这个才能帮助:

View child = parent.findViewById(R.id.btnMoreText);
    child.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            View parent = (View) v.getParent();
            parent.performClick();
        }
    });

另一种变体,不总是有效:

child.setOnClickListener(null);

这对于作为子视图的CardView有效,因为CardView似乎忽略了clickable="false"focusable="false"。然而,使用setOnTouchListener并在父级上调用onTouchEvent允许父级显示触摸反馈(例如涟漪效果)。 - methodsignature
我在下面发布了相关代码:https://dev59.com/zW855IYBdhLWcg3wUScW#52928884。 - methodsignature

16
android:duplicateParentState="true"

在子视图中,它的可绘制状态(聚焦、按下等)取决于直接父视图而不是自身。你可以为父视图设置onclick事件,并通过它来调用子视图上的点击事件。


4
当子项被点击时,OP希望调用父项的点击监听器,而不是反过来。 - Farid

7
如果您的 TextView 存在点击问题,则从 xml 文件中删除 android:inputType=""。

如果你不知道为什么,我建议你不要这样做。只是说说而已,各有所好。 - dell116
你应该将其设置为android:inputType="none" - AZ_
我遇到了同样的问题。在TextView中,我设置了android:inputType="textCapWords" - Nil

5

这个答案与Alexander Ukhov的答案相似,只不过它使用了触摸事件而不是点击事件。这些事件使得父元素能够显示正确的按下状态(例如涟漪效果)。此答案还使用了Kotlin而非Java。

view.setOnTouchListener { view, motionEvent ->
    (view.parent as View).onTouchEvent(motionEvent)
}

感谢您的帮助!但我建议进行以下澄清:1)使用其他名称来表示被触摸的视图 - 例如“touchedView”;2)调用performClick()而不是onTouchEvent()。 - DmitryKanunnikoff

0
如果你想要在父视图和子视图上同时使用OnTouch和OnClick监听器,请使用以下技巧:
  1. 将ScrollView作为父视图,并在其中放置你的子视图(在Relative/LinearLayout中)。

  2. 将父ScrollView设置为android:fillViewport="true",这样视图就不会滚动。

  3. 然后将OnTouch监听器设置给父视图,并将OnClick监听器设置给子视图。 然后就可以同时享受两个监听器的回调了。


0
然后我使用LinkMovementMethod设置了TextView。
TextView.setMovementMethod()内部调用了一个私有方法fixFocusableAndClickableSettings。这是问题的根源:调用setFocusable(FOCUSABLE); setClickable(true); setLongClickable(true);。所以无论你认为你设置了什么可点击性,它都是真的。
这3个标志必须重置为false,才能使视图变为不可点击。 source

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接