【问题标题】:android - OnClickListener not working for first click in recyclerviewandroid - OnClickListener 不适用于 recyclerview 中的第一次点击
【发布时间】:2026-01-22 19:50:01
【问题描述】:

我正在开发一个 Android 聊天应用程序,我正在使用 RecyclerView 来列出消息。

我已经编写了适配器,但是在检测布局内的元素(在本例中为 TextView)何时被点击时遇到问题。

这是我的适配器:

public class ChatRoomThreadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private class ViewHolder extends RecyclerView.ViewHolder {
        TextView message, timestamp;

        private ViewHolder(View view) {
            super(view);

            message = (TextView) itemView.findViewById(R.id.message); 
            timestamp = (TextView) itemView.findViewById(R.id.timestamp); 
        }

    }

    public ChatRoomThreadAdapter(Context mContext, ArrayList<Message> messageArrayList, String userId) {
        this.mContext = mContext;
        this.messageArrayList = messageArrayList;
        this.userId = userId;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView;
        itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat, parent, false);

        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {

        ((ViewHolder) holder).message.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (((ViewHolder) holder).timestamp.getVisibility() == View.GONE) {
                    ((ViewHolder) holder).timestamp.setVisibility(View.VISIBLE);
                } else {
                    ((ViewHolder) holder).timestamp.setVisibility(View.GONE);
                }

            }
        });

    }

    @Override
    public int getItemCount() {
        return messageArrayList.size();
    }
}

当前的 onClick 有效,但我必须在 message 上单击两次才能触发 onClick。为了解决这个问题,我已经搜索并尝试了 3 天的无尽解决方案,但到目前为止,互联网上的解决方案都没有奏效。

【问题讨论】:

  • 您是否登录以查看onClick 是否实际上两次都发生了,但视图的可见性实际上只起作用了一次。另外,timestamp 视图的原始可见性是什么?如果是INVISIBLE 而不是GONE,那将说明您所看到的。第一次单击将其设置为GONE(看起来没有任何反应),然后第二次将其设置为VISIBLE
  • onClick 只发生一次。我已经记录了。

标签: java android android-recyclerview onclicklistener


【解决方案1】:

确保您在按钮上同时禁用了 focusableInTouchModefocusable。第一次点击获得焦点,第二次点击执行onClickListener。

【讨论】:

  • 我已通过将其设置为 false 来禁用它;我也试过完全删除它,但我仍然必须双击 onClick 才能触发
  • 尝试禁用“focusable”属性。我正在我的 IDE 中对其进行测试,并且我知道我之前必须禁用这两者。我的测试按钮在两个都被禁用的片段内工作。
  • 当您说“按钮上已禁用”时,您指的是什么按钮?
【解决方案2】:

滚动后,recycler view items 不可点击,问题依旧https://issuetracker.google.com/issues/66996774

找到了一种在滚动状态仍为 SCROLL_STATE_SETTLING 时强制点击的方法

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.recyclerview.widget.RecyclerView

class WrapperRecyclerView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
  RecyclerView(context, attributeSet, defStyle) {

  constructor(context: Context) : this(context, null, 0)

  constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)

  override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
    val eventConsumed = super.onInterceptTouchEvent(event)

    when (event.actionMasked) {
      MotionEvent.ACTION_DOWN -> if (scrollState == SCROLL_STATE_SETTLING) {
        parent.requestDisallowInterceptTouchEvent(false)
        // only if it touched the top or the bottom.
        if (!canScrollVertically(-1) || !canScrollVertically(1)) {
          // stop scroll to enable child view to get the touch event
          stopScroll()
          // do not consume the event
          return false
        }
      }
    }

    return eventConsumed
  }
}

【讨论】:

  • 我的问题是回收站视图“卡在”SCROLL_STATE_DRAGGING 中。因此,我仅通过更改 if 条件中的状态名称来重用您的代码。此外,我没有找到您必须检查 if (!canScrollVertically(-1) || !canScrollVertically(1)) 的用例,因此删除了该条件,因此总是强制滚动停止。
【解决方案3】:

以下课程将解决此问题:

class WrapperRecyclerView(context: Context, attributeSet: AttributeSet?, defStyle: Int) :
    RecyclerView(context, attributeSet, defStyle) {

  constructor(context: Context) : this(context, null, 0)

  constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)

  override fun onScrollStateChanged(state: Int) {
    super.onScrollStateChanged(state)
    if (state == RecyclerView.SCROLL_STATE_SETTLING) {
      this.stopScroll();
    }
  }
}

【讨论】:

    【解决方案4】:

    对于那些试图在回收站视图中调试这种滚动状态问题的人的提示。创建您自己的 MyRecyclerView 扩展 RecyclerView,并覆盖 fun onInterceptTouchEvent 以添加日志记录:

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
        Log.d(TAG, "MyRecyclerView intercepted a touch event: ${event!!.action}")
        Log.d(TAG, "MyRecyclerView is in state: ${scrollState}")
        return super.onInterceptTouchEvent(event)
    }
    

    这让我发现我的回收站视图卡在SCROLL_STATE_DRAGGING

    我相信,要让子视图接收来自回收者视图的点击,回收者视图必须处于 SCROLL_STATE_IDLE 状态。

    所以我对问题的解决方案是基于 @Rocky 的建议,

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
    
        val eventConsumed = super.onInterceptTouchEvent(event)
    
        /*
         * Fix the following problem.
         *
         * When the recycler view received a click, it was still in the DRAGGING mode.
         * It consumed the click, instead of passing it down to the child view.
         * The user had to click a child view again, to be able to interact with it.
         */
        when (event!!.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                if (scrollState == SCROLL_STATE_DRAGGING) {
                    stopScroll()
                    return false
                }
            }
        }
    
        return eventConsumed
    }
    

    【讨论】:

      【解决方案5】:

      对我来说 focusableInTouchMode & focusable 不起作用。

      将此行添加到 RecyclerView :

      android:nestedScrollingEnabled="false"
      

      享受…

      【讨论】: