如何在 RecyclerView 上获得点击(不是 children)
How to get clicks on RecyclerView (NOT the children)
有什么方法可以在 RecyclerView
上设置 onClickListener
吗?
我有一个 RecyclerView
,其中有一些 children,并在 parent RecyclerView
上设置了一个 OnClickListener
。但是,当我单击该视图时,onClick
不会触发。请参阅下面的示例代码——我们希望在 parent 上获得点击, 而不是 children。在这种情况下,我们不关心对项目的点击。
我曾尝试在 children 上执行 setFocusable(false)
、setClickable(false)
和 setOnClickListener(null)
,但均无济于事。在任何情况下,我都不认为 children 从 parent 窃取点击,因为当我点击没有 children 的区域时,点击也不会注册。
package com.formagrid.hellotest;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Arrays;
import java.util.List;
public class HelloActivity extends Activity {
private RecyclerView mRecyclerView;
private RecyclerAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new RecyclerAdapter(Arrays.asList("hi", "this", "is", "some", "text"));
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("patricia", view.toString());
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
}
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.Holder> {
public class Holder extends RecyclerView.ViewHolder {
protected TextView textView;
public Holder(TextView itemView) {
super(itemView);
this.textView = itemView;
}
}
private List<String> contents;
public RecyclerAdapter(List<String> contents) {
this.contents = contents;
}
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
return new Holder(new TextView(parent.getContext()));
}
@Override
public void onBindViewHolder(Holder holder, int position) {
holder.textView.setText(contents.get(position));
}
@Override
public int getItemCount() {
return contents.size();
}
}
}
Is there any way to set an onClickListener
on a RecyclerView
?
没有。也就是说,您 可以 设置一个 OnClickListener
,但 RecyclerView
永远不会调用它。 RecyclerView
拦截所有触摸事件,但从不调用 performClick()
,这就是 View
调用其侦听器的方式。
但是,您可以用 OnTouchListener
和 GestureDetector
来模拟 OnClickListener
。对于 GestureDetector
的侦听器,我们可以使用 SimpleOnGestureListener
,仅实现 onSingleTapUp()
方法。
class ClickListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(HelloActivity.this, "Clicked", 0).show();
return true;
}
};
那么我们只需要从OnTouchListener
中给GestureDetector
提供MotionEvent
,然后检查return来决定是否消费这个事件,这样不干扰滚动、拖动等
final GestureDetector detector = new GestureDetector(HelloActivity.this, new ClickListener());
mRecyclerView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(detector.onTouchEvent(event)) {
return true;
}
return false;
}
}
);
请注意,上面的解决方案几乎只适用于 "simple" RecyclerView
,就像问题中描述和给出的那样。如果您使用更复杂的项目处理,例如拖放或滑动,我们将需要在触摸事件链的更上层处理我们的手势检测。
为此,我们可以子类化RecyclerView
并在dispatchTouchEvent()
方法中执行检测。如果检测到单击,我们只需调用 performClick()
,这将触发 RecyclerView
的 OnClickListener
。
public class ClickableRecyclerView extends RecyclerView {
private final GestureDetectorCompat detector;
public ClickableRecyclerView(Context context) {
this(context, null);
}
public ClickableRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
detector = new GestureDetectorCompat(context, new ClickListener());
}
private class ClickListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
performClick();
return true;
}
};
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
detector.onTouchEvent(e);
return super.dispatchTouchEvent(e);
}
}
只需使用此子类代替您的常规 RecyclerView
,并像往常一样在其上设置 OnClickListener
。可能需要额外的构造函数,具体取决于您如何实例化它。
RecycleView 上的点击事件?像这样尝试:
//set android:descendantFocusability="blocksDescendants" on parent layout
//then setOnClickListener
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
**android:descendantFocusability="blocksDescendants"**
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
这就是我在 Kotlin 风格中的做法
fun RecyclerView.enableClickListener(){
val gesture = object : GestureDetector.SimpleOnGestureListener(){
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
this@enableClickListener.performClick()
return super.onSingleTapConfirmed(e)
}
}
val detector = GestureDetector(this.context, gesture)
this.setOnTouchListener { v, event -> detector.onTouchEvent(event) }
}
这是使用方法
yourRecyclerView.apply {
enableClickListener()
setOnClickListener {
// Do what you want ...
}
}
尽情享受吧:)
这里有技巧,欢迎评论。
一个 FrameLayout
中可以有 2 个子项目。第一个是 RecyclerView
,第二个是 View
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:id="@+id/over_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
因为它是 FrameLayout
,所以 View
将位于 RecyclerView
之上,您可以将 onClickListener
设置为 View
,它会表现得好像是 RecyclerView
被点击了
over_view.setOnClickListener {
.....
}
有什么方法可以在 RecyclerView
上设置 onClickListener
吗?
我有一个 RecyclerView
,其中有一些 children,并在 parent RecyclerView
上设置了一个 OnClickListener
。但是,当我单击该视图时,onClick
不会触发。请参阅下面的示例代码——我们希望在 parent 上获得点击, 而不是 children。在这种情况下,我们不关心对项目的点击。
我曾尝试在 children 上执行 setFocusable(false)
、setClickable(false)
和 setOnClickListener(null)
,但均无济于事。在任何情况下,我都不认为 children 从 parent 窃取点击,因为当我点击没有 children 的区域时,点击也不会注册。
package com.formagrid.hellotest;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Arrays;
import java.util.List;
public class HelloActivity extends Activity {
private RecyclerView mRecyclerView;
private RecyclerAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new RecyclerAdapter(Arrays.asList("hi", "this", "is", "some", "text"));
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("patricia", view.toString());
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
}
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.Holder> {
public class Holder extends RecyclerView.ViewHolder {
protected TextView textView;
public Holder(TextView itemView) {
super(itemView);
this.textView = itemView;
}
}
private List<String> contents;
public RecyclerAdapter(List<String> contents) {
this.contents = contents;
}
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
return new Holder(new TextView(parent.getContext()));
}
@Override
public void onBindViewHolder(Holder holder, int position) {
holder.textView.setText(contents.get(position));
}
@Override
public int getItemCount() {
return contents.size();
}
}
}
Is there any way to set an
onClickListener
on aRecyclerView
?
没有。也就是说,您 可以 设置一个 OnClickListener
,但 RecyclerView
永远不会调用它。 RecyclerView
拦截所有触摸事件,但从不调用 performClick()
,这就是 View
调用其侦听器的方式。
但是,您可以用 OnTouchListener
和 GestureDetector
来模拟 OnClickListener
。对于 GestureDetector
的侦听器,我们可以使用 SimpleOnGestureListener
,仅实现 onSingleTapUp()
方法。
class ClickListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(HelloActivity.this, "Clicked", 0).show();
return true;
}
};
那么我们只需要从OnTouchListener
中给GestureDetector
提供MotionEvent
,然后检查return来决定是否消费这个事件,这样不干扰滚动、拖动等
final GestureDetector detector = new GestureDetector(HelloActivity.this, new ClickListener());
mRecyclerView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(detector.onTouchEvent(event)) {
return true;
}
return false;
}
}
);
请注意,上面的解决方案几乎只适用于 "simple" RecyclerView
,就像问题中描述和给出的那样。如果您使用更复杂的项目处理,例如拖放或滑动,我们将需要在触摸事件链的更上层处理我们的手势检测。
为此,我们可以子类化RecyclerView
并在dispatchTouchEvent()
方法中执行检测。如果检测到单击,我们只需调用 performClick()
,这将触发 RecyclerView
的 OnClickListener
。
public class ClickableRecyclerView extends RecyclerView {
private final GestureDetectorCompat detector;
public ClickableRecyclerView(Context context) {
this(context, null);
}
public ClickableRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
detector = new GestureDetectorCompat(context, new ClickListener());
}
private class ClickListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
performClick();
return true;
}
};
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
detector.onTouchEvent(e);
return super.dispatchTouchEvent(e);
}
}
只需使用此子类代替您的常规 RecyclerView
,并像往常一样在其上设置 OnClickListener
。可能需要额外的构造函数,具体取决于您如何实例化它。
RecycleView 上的点击事件?像这样尝试:
//set android:descendantFocusability="blocksDescendants" on parent layout
//then setOnClickListener
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
**android:descendantFocusability="blocksDescendants"**
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
这就是我在 Kotlin 风格中的做法
fun RecyclerView.enableClickListener(){
val gesture = object : GestureDetector.SimpleOnGestureListener(){
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
this@enableClickListener.performClick()
return super.onSingleTapConfirmed(e)
}
}
val detector = GestureDetector(this.context, gesture)
this.setOnTouchListener { v, event -> detector.onTouchEvent(event) }
}
这是使用方法
yourRecyclerView.apply {
enableClickListener()
setOnClickListener {
// Do what you want ...
}
}
尽情享受吧:)
这里有技巧,欢迎评论。
一个 FrameLayout
中可以有 2 个子项目。第一个是 RecyclerView
,第二个是 View
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:id="@+id/over_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
因为它是 FrameLayout
,所以 View
将位于 RecyclerView
之上,您可以将 onClickListener
设置为 View
,它会表现得好像是 RecyclerView
被点击了
over_view.setOnClickListener {
.....
}