Implementing Click In CardView in RecycleView - android

So I am trying to make my cards clickable and as they are clicked it opens a new activity (not implemented in the code below though), here is my code:
For Interface
import android.view.View;
public interface ClickListener {
public void itemClicked(View view , int position);
}
For Adapter:
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.SensorViewHolder> {
private ClickListener clicklistener = null;
List<SensorData> sensors;
RVAdapter(List<SensorData> sensors) {
this.sensors = sensors;
}
public class SensorViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView sensorName;
TextView sensorDesc;
ImageView sensorPhoto;
private LinearLayout main;
SensorViewHolder(final View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
sensorName = (TextView) itemView.findViewById(R.id.sensor_name);
sensorDesc = (TextView) itemView.findViewById(R.id.sensor_desc);
sensorPhoto = (ImageView) itemView.findViewById(R.id.sensor_photo);
main = (LinearLayout) itemView.findViewById(R.id.main);
main.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(itemView.getContext(), "Position:" + Integer.toString(getPosition()), Toast.LENGTH_SHORT).show();
if(clicklistener != null){
clicklistener.itemClicked(v, getAdapterPosition());
}
}
});
}
}
public void setClickListener(ClickListener clickListener){
this.clicklistener = clickListener;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public SensorViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
SensorViewHolder pvh = new SensorViewHolder(v);
return pvh;
}
#Override
public void onBindViewHolder(SensorViewHolder SensorViewHolder, int i) {
SensorViewHolder.sensorName.setText(sensors.get(i).name);
SensorViewHolder.sensorDesc.setText(sensors.get(i).descriptor);
SensorViewHolder.sensorPhoto.setImageResource(sensors.get(i).iconID);
}
#Override
public int getItemCount() {
return sensors.size();
}
}
and here is the code for MainActivity:
public class MainActivity extends AppCompatActivity {
private List<SensorData> sensorData;
private RecyclerView rv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Find the recycler view for the <code>
rv = (RecyclerView) findViewById(R.id.rv);
//Initialising a linear layout manager
final LinearLayoutManager llm = new LinearLayoutManager(this);
rv.setLayoutManager(llm);
rv.setHasFixedSize(true);
initializeData();
initializeAdapter();
rv.setClickListener(this);
}
private void initializeData() {
sensorData = new ArrayList<>();
sensorData.add(new SensorData("Accelerometer", "Measures the acceleration of a moving or vibrating body.", R.mipmap.ic_accl));
sensorData.add(new SensorData("GPS Sensor", "Provides real-time user location and time information.", R.mipmap.ic_gps));
sensorData.add(new SensorData("Proximity Sensor", "Provides ambient light and proximity sensing.", R.mipmap.ic_als));
}
private void initializeAdapter() {
RVAdapter adapter = new RVAdapter(sensorData);
rv.setAdapter(adapter);
}
}
And yet it seems I cannot access setClickListener. I must mention I am quite a beginner in this.
Where have I mistaken? Is this the correct way to implement clicks on a card or I am missing something?
Also here is the MainActivity XML if needed.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:id="#+id/main">
<android.support.v7.widget.RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/rv">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
and items.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cv"
app:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sensor_photo"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginRight="16dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sensor_name"
android:layout_toRightOf="#+id/sensor_photo"
android:layout_alignParentTop="true"
android:textSize="30sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sensor_desc"
android:layout_toRightOf="#+id/sensor_photo"
android:layout_below="#+id/sensor_name" />
</RelativeLayout>
</android.support.v7.widget.CardView>
EDIT: Just to avoid confusion, **I want the use to be able to click the cards and go to a new activity. ** How can I implement that in the simplest possible way? I am really confused on how to do it in the RecyclerView.
Thanks a lot and regards.

Since there is a difference between the old ListView and the new RecyclerView component, I have a reusable piece of code that I use to handle click events (both regular clicks and LongClicks):
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
#SuppressWarnings("CanBeFinal")
private GestureDetector mGestureDetector;
#SuppressWarnings("CanBeFinal")
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener)
{
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener()
{
#Override
public boolean onSingleTapUp(MotionEvent e)
{
return true;
}
#Override
public void onLongPress(MotionEvent e)
{
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if(childView != null && mListener != null)
{
mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e)
{
View childView = view.findChildViewUnder(e.getX(), e.getY());
if(childView != null && mListener != null && mGestureDetector.onTouchEvent(e))
{
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
Then simply do this in your activity or fragment where you want to handle click events:
mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, mRecyclerView, new RecyclerItemClickListener
.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
//start new activity here
}
#Override
public void onItemLongClick(View view, int position) {
}
}));
That should be the easiest way to do it!
I hope this helps you! Good luck and happy coding!

Basically you need to set the listener in the Viewholder
public class SensorViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView sensorName;
TextView sensorDesc;
ImageView sensorPhoto;
private LinearLayout main;
SensorViewHolder(final View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
//Add the following line below
cv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//The intent firing goes here
}
});
sensorName = (TextView) itemView.findViewById(R.id.sensor_name);
sensorDesc = (TextView) itemView.findViewById(R.id.sensor_desc);
sensorPhoto = (ImageView) itemView.findViewById(R.id.sensor_photo);
main = (LinearLayout) itemView.findViewById(R.id.main);
main.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(itemView.getContext(), "Position:" + Integer.toString(getPosition()), Toast.LENGTH_SHORT).show();
if(clicklistener != null){
clicklistener.itemClicked(v, getAdapterPosition());
}
}
});
}
}
Hope it helps.

To fix the compiler error, the correct way to set a OnClickListener is with
rv.setOnClickListener()
Note that the error is because you are missing the word On. Generally, you should use autocomplete in Android Studio to help avoid these types of issues. If autocomplete doesn't pop up automatically, you can push Ctrl-Space to get it.
As a side note, you can build your project by clicking Build -> Make Project in the main menu. This will give a window with the error messages where you can copy and paste more easily than the usual error balloons.

first of all set tag for your card in onBindViewHolder:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.cv.setTag(position);
}
then add on click listener for your card in your view holder in adapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public CardLayout cv;
public ViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(Rid.cv);
cv.setOnClickListener(this);
}
#Override
public void onClick(View v) {
//for find item that hold in list
int postition = (Integer) v.getTag();
...
}

In your findViewById you are casting the view to a RecyclerView and therefore you are referring the standard RecyclerView implementation of the Android framework. So this would never work.
The next thing is you are trying to set a listener to the RecyclerView but define the setListener method inside your adapter.
So what you should do is to either get a reference to your adapter via:
((RvAdapter) rv.getAdapter()).setClickListener();
after setting it or even better set the listener before you call
rv.setAdapter()
via
RvAdapter adapter = new RvAdapter();
adapter.setClickListener(listener)

Related

Why is my recyclerView not displaying any data?

I have implemented a RecyclerView and customer Adapter many times, but for some reason I cannot get this one to display any data. I am feeding in data from JSON using retrofit and calling notifyDataSetChanged() once this has been loaded, yet it still remains blank. I have stripped this back to just one text view to try and simplify but still not getting anything. Can anyone see where I am going wrong here?
When I debug, I am getting the List to contain data so I am definitely parsing the data correctly, I just cant get it display in the recycler view. I have even checked the list.size() in the loadTrailerList method and it has data.
My Activity onCreate method:
trailerAdapter = new TrailerAdapter(this);
trailerRecyclerView = findViewById(R.id.trailer_recycler_view);
trailerRecyclerView.setLayoutManager(new LinearLayoutManager(this));
trailerRecyclerView.setAdapter(trailerAdapter);
Retrofit onResponse method:
if (response.body() != null) {
trailers = response.body().getTrailers();
}
trailerAdapter.loadTrailerList(response.body().getTrailers());
My custom adapter:
public class TrailerAdapter extends RecyclerView.Adapter<TrailerAdapter.TrailerViewHolder> {
private final List<Trailer> trailerList = new ArrayList<>();
private final TrailerClickListener listener;
public TrailerAdapter(TrailerClickListener listener) {
this.listener = listener;
}
#NonNull
#Override
public TrailerViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.trailer_list_item, viewGroup, false);
return new TrailerViewHolder(itemView, this);
}
#Override
public void onBindViewHolder(#NonNull TrailerViewHolder trailerViewHolder, int i) {
trailerViewHolder.trailerTitle.setText(trailerList.get(i).getName());
}
#Override
public int getItemCount() {
return trailerList.size();
}
public void loadTrailerList(List<Trailer> trailers) {
this.trailerList.clear();
if (trailers != null) {
trailers.addAll(trailers);
}
notifyDataSetChanged();
}
class TrailerViewHolder extends RecyclerView.ViewHolder {
final TrailerAdapter trailerAdapter;
private final TextView trailerTitle;
private TrailerViewHolder(#NonNull View itemView, TrailerAdapter trailerAdapter) {
super(itemView);
this.trailerAdapter = trailerAdapter;
trailerTitle = itemView.findViewById(R.id.text_view_trailer_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onTrailerClicked(trailerList.get(getAdapterPosition()));
}
});
}
}
}
My List Item XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_view_trailer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play Trailer" />
</LinearLayout>
the recycler view in my activity XML:
<android.support.v7.widget.RecyclerView
android:id="#+id/trailer_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
app:layout_constraintTop_toBottomOf="#+id/trailer_divider">
</android.support.v7.widget.RecyclerView>
I am grateful for anyone that can point me in the right direction
It is because you are sending a list to adapter but, you are not initializing your list which is used in the adapter.
try this.
public void loadTrailerList(List<Trailer> trailers) {
this.trailerList.clear();
if (trailers != null) {
trailerList = trailers;
}
notifyDataSetChanged();
}
Doh! I just realised what I was doing wrong:
In my loadTrailerList() method in my adapter, I was calling:
trailers.addAll(trailers);
instead of:
trailerList.addAll(trailers);
to load the list of items into the actual ArrayList! whoops!

Pass data from RecyclerView to a Fragment

I have referred few links regarding this topic but I still couldn't understand how to pass data from a RecyclerView to a Fragment[opening a Fragment on Cardview click]. Following is my RecyclerView class:
public class PhotoAdapter extends RecyclerView.Adapter<PhotoAdapter.PhotosHolder>
{
static class PhotosHolder extends RecyclerView.ViewHolder
{
CardView cv;
ImageView photo_img;
PhotosHolder(final View itemView)
{
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv_photo);
photo_img = (ImageView) itemView.findViewById(R.id.thum_photo);
}
}
private List<PhotoInitialise> photo;
private Activity mContext;
public PhotoAdapter(List<PhotoInitialise> photos, Context mContext)
{
this.photo=photos;
this.mContext= (Activity) mContext;
}
#Override
public PhotosHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.phototab_layout,parent,false);
return new PhotosHolder(layoutView);
}
#Override
public void onBindViewHolder(PhotosHolder holder, final int position)
{
holder.photo_count.setText(photo.get(position).gettotalImages());
holder.cv.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
// want to pass the value to a Fragment here and invoke the Fragment
}
});
}
#Override
public int getItemCount()
{
return photo.size();
}
}
This is the XML Layout of the Fragment that should be displayed:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/photo_detail_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/photodetail_description"
android:textSize="20sp"
android:paddingLeft="5dp"
android:layout_marginTop="5dp"
android:paddingRight="5dp">
</RelativeLayout>
I already have an idea of how to pass the value to fragment but I never could able to invoke the layout. Now what should I write in my onClick() to initiate the Fragment Layout ?
Implement interface to handle the click in the specified Fragment.
Sample code structure will be like below. You can customize and use it according to your need:
public InterfaceListItemClickListener{
void listItemClickAction(<parameters to pass>);
}
In your fragment class implement this interface:
public class SampleFragment extends BaseFrgment implements InterfaceListItemClickListener{
#Override
public void listItemClickAction(<parameters to pass>) {
//override the interface function
//handle your click action here
}
//pass the instance of your interface to your adapter like below line
YourAdapter yourAdapter = new YourAdapter(otherParameters, this);
}
In constructor of your Adapter access the interface:
public YourAdapter(otherParameters, InterfaceListItemClickListener clickListenerInferface ){
//other assignations
this.clickListenerInferface = clickListenerInferface;
}
and then onClick callback you can call your onclick function:
clickListenerInferface.listItemClickAction(<parameter to pass>);

Disable item click and enable onclick of recyclerview

here is my layout xml.If relativelayout click works that is also fine.Not getting any click events other than item clicks.
<RelativeLayout
android:id="#+id/layoutItems"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/viewDivider">
<android.support.v7.widget.RecyclerView
android:id="#+id/item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:padding="8dp"></android.support.v7.widget.RecyclerView>
</RelativeLayout>
There are some different ways to do it. The first way is to do like this:
RecyclerView recyclerView = findViewById(R.id.recycler);
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(context, recyclerView ,new RecyclerItemClickListener.OnItemClickListener() {
#Override public void onItemClick(View view, int position) {
// do whatever
}
#Override public void onLongItemClick(View view, int position) {
// do whatever
}
})
);
Or you can do it in a ViewHolder.
We will need a listener interface.
public interface OnItemClickListener {
public void onClick(View view, int position);
}
In your ViewHolder class in your RecyclerView adapter, implement View.OnClickListener, bind the listener to the view. In the onClick method, call the onClick method of the interface OnItemClickListener. This should be passed in from your RecycyclerView’s constructor. The actual implementation of the onclick event will be from an activity or fragment that contains this RecyclerView. The important line here is clickListener.onClick(view, getPosition()); where clickListener is a global variable in your RecyclerView class, again it should’ve passed in from your RecyclerView’s constructor.
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView cityName;
public ViewHolder(View view) {
super(view);
cityName = (TextView) view.findViewById(R.id.city_name);
itemView.setOnClickListener(this); // bind the listener
}
#Override
public void onClick(View view) {
clickListener.onClick(view, getPosition()); // call the onClick in the OnItemClickListener
}
}
The onClick implementation in the Activity class, the important line here is mAdapter.setClickListener(this); and the onClick method. The onClick method gets triggered from the ViewHolder’s onClick method in your RecyclerView class, which passes the view and position of the clicked item.
public class CityActivity extends Activity implements ItemClickListener {
private RecyclerView mRecyclerView;
private CityAdapter mAdapter;
private List<City> cities;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_city);
cities = CityManager.getInstance(this.getApplicationContext()).getCites();
mRecyclerView = (RecyclerView)findViewById(R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new CityAdapter(cities, R.layout.row_city, this);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setClickListener(this); // Bind the listener
}
#Override
public void onClick(View view, int position) {
// The onClick implementation of the RecyclerView item click
final City city = cities.get(position);
Intent i = new Intent(this, CityviewActivity.class);
i.putExtra("city", city.name);
i.putExtra("desc", city.description);
i.putExtra("image", city.imageName);
Log.i("hello", city.name);
startActivity(i);
}
}

Nested RecyclerView onClickListener not working

I've implemented an nested ReyclerView (horizontal into vertical) and I wan't to add an click listener for the entire row which is wrapped in a CardView element.
The issue I'm having is that the inside RecyclerView captures all the touch events and the root CardView does not respond to the onClick event.
I've also tried to make the CardView intercept the touch events, but with this approach the ripple effect (in fact any feedback) wasn't working.
Can someone recommend an solution for how to implement a click listener on a row while having nested RecyclerView?
Thank you.
--LE--
This is the current implementation:
fragment layout
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/list"
android:name=".NestedRecyclerViewsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"
tools:context=".NestedRecyclerViewsFragment"
tools:listitem="#layout/fragment_nested_recyclerview_item"
/>
Fragment onCreateView() implementation
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_nested_recyclerview_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
recyclerView.setHasFixedSize(true); // Improves performance - as we know the size doesn't change
//Initialize and set the adapter
mAdapter = new RootAdapter(context, mListener);
recyclerView.setAdapter(mAdapter);
final GestureDetector mGestureDetector =
new GestureDetector(view.getContext(), new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
//TODO: intercept simple gestures like onClick and/or onLongClick
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && mGestureDetector.onTouchEvent(e)) {
//TODO: handle the intercept??
child.callOnClick();
return true;
}
return super.onInterceptTouchEvent(rv, e);
}
});
recyclerView.setNestedScrollingEnabled(true);
}
return view;
}
**Root Adapter layout **
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="#dimen/row_height"
android:layout_gravity="center_horizontal"
android:layout_margin="#dimen/none"
android:padding="#dimen/none"
app:cardCornerRadius="#dimen/none"
tools:context=".NestedRecyclerViewActivity"
<!-- Simple selector for API < 21 and ripple effect for APi >= 21 -->
android:foreground="#drawable/selector_default"
android:clickable="true"
android:focusable="true"
>
<!-- Horizontal image gallery inside row item -->
<android.support.v7.widget.RecyclerView
android:id="#+id/child_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
...
</android.support.v7.widget.CardView>
Root Adapter onBindView() implementation:
...
holder.childRecyclerView.setLayoutManager(new LinearLayoutManager(holder.mView.getContext(),
LinearLayoutManager.HORIZONTAL, false));
holder.childRecyclerView.setHasFixedSize(true); // We know the image don't change size
holder.childRecyclerView.setAdapter(new ChildAdapter(items));
...
Root Adapter ViewHolder implementation:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ItemClickListener mClickListener;
/**
* The whole view - useful if you need to place some touch listener on the entire row.
*/
public final View mView;
/**
* The model associated with this view. - will be updated on bind method
*/
public Object mItem;
#Bind(R.id.child_recycler_view)
RecyclerView childRecyclerView;
...
public ViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
mView = view;
mView.setOnClickListener(this);
// Set the click listener bound to the fragment or activity
mClickListener = RootAdapter.this;
}
#Override
public void onClick(View v) {
if (null != mClickListener) {
mClickListener.onClick(v, getAdapterPosition());
}
}
}
The inner RecyclerView (the child) is a very simple and standar implementation without any listener set to it.
Although this solution works and I get the click event and the inner RecyclerView scroll works, I encountered the issue where the feedback of the click is not shown.
LE: Solution
My requirements changed a little since I posted this Issue, having to replace the inner RecyclerView with an PagerAdapter, but the solution I implemented should work with the nested RecyclerView also.
Basically I use an custom ItemClickSupport class which will pass the click events back to the parent as soon as they are intercepted:
/**
* Utility class which adds the ability to add Click Support for RecyclerViews without the need to implement click
* listeners into the adapter or in the ViewHolder's implementation.
* <p>
* Use it by simply binding an click listener to the desired RecyclerView.
* <pre><code>
* ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
* {#literal#}Override
* public void onItemClicked(RecyclerView recyclerView, int position, View v) {
* // Handle the clicked item
* }
* });
* </code></pre>
* </p>
* Based on <a href="http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/">Handle-Android-RecyclerView
* -Clicks</a>, <br/><b>Hugo Visser</b>. Which is very similar with the implementation from <a
* href="https://github.com/lucasr/twoway-view">TwoWay-View</a>.
* <p/>
* Created by ionut on 22.03.2016.
*/
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener =
new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(View view) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
#Override
public void onChildViewDetachedFromWindow(View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
void onItemClicked(int position);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
public static class SimpleOnItemClickListener implements OnItemClickListener {
#Override
public void onItemClicked(int position) {
}
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
}
}
}
When creating the root adapter, I set the item click listener for it, like this:
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(this);
And I also send this listener for the inner adapter through an setter method, like this:
rootAdapter.setOnItemClickListener(this);
rootAdapter is the root adapter, and this is the current fragment I'm in, which implements the ItemClickSupportListener.
In the root adapter, when binding the items and creating the inner adapter, I pass on the item click support listener to the inner adapter, like this:
innerAdapter.setOnItemClickListener(new ItemClickSupport.SimpleOnItemClickListener() {
#Override
public void onItemClicked(int position) {
// Here we pass the click to the parent provided click listener.
// We modify the position with the one of the ViewHolder so that we don't get the
// position of the horizontal RecyclerView adapter - we are interested on the
// vertical item actually.
if (null != mItemClickListener) {
mItemClickListener.onItemClicked(holder.getAdapterPosition());
}
}
});
mItemClickListener is actually the listener set by the fragment through the setter method, described above.
In the inner adapter, when creating the views, I set an click listener on the root of the layout which I inflated and pass that event back to my custom click listener:
// Detect the click events and pass them to any listeners
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mOnItemClickListener) {
mOnItemClickListener.onItemClicked(position);
}
}
});
mOnItemClickListener is actually the item click support which was passed by adapter described above.
Another important thing is to use an FrameLayout for the content of the adapter item views, like this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:addStatesFromChildren="true"
android:foreground="#drawable/selector_default"
>
The important thing here is the foreground which will use an selector and the flag android:addStatesFromChildren. This is set for the root adapter items.
The inner adapter items should also use an FrameLayout as content view so that we can also set it's selector using foreground attribute:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="#drawable/selector_default"
>
This is needed, because when the inner adapter item will capture the click event, the selector to be activated, otherwise the parent should react to the click event and activate it's selector.
try setting click listener to the view in adapter
public class NormalItem extends RecyclerView.ViewHolder {
CardView cardView;
TextView textView;
public NormalItem(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.textView);
cardView = (CardView) itemView.findViewById(R.id.cardview);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// to perform click on entire row do like this.
}
});
textView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
// perform clicks to views inside the items.
}
}

RecyclerView onLongClickListener refuse to trigger

I want to implement a long press of a CardView inside a RecyclerView
The layout:
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:scrollbars="vertical"
android:longClickable="true"
android:hapticFeedbackEnabled="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
I tried this:
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(Utils.context));
mRecyclerView.setOnLongClickListener(new AdapterView.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
Toast.makeText(Utils.context,"dsfd",Toast.LENGTH_LONG).show();
return true;
}
});
And I also tried to implement this event in the RecyclerView.ViewHolder class but nothing works. I have the long press effect but the event itself is not triggered. There is no onItemLongClickListener. I also tried this: RecyclerView onClick .
What am I missing?
You can create Interface where you should implement your onLongClicked(int position) method. And use it.
Follow this steps to create your onLongClickListener:
Create your Interface:
public interface IRecyclerViewClickListener {
void onLongClicked(int position);
}
Create listener object in your activity/fragment and pass this listener object to your adapter in the create adapter method. Example:
// ... some code ...
adapter = new MoviesListAdapter(getActivity(), list, listener);
rvDialogs.setAdapter(adapter);
// ... code ...
In your adapter in your owned ViewHolder constructor set long listener on itemView:
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView Title;
// ...
public MessageViewHolder(View itemView) {
super(itemView);
Title = (TextView) itemView.findViewById(R.id.tvDialogTitle);
// ...
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
listener.onLongClicked(getAdapterPosition());
return false;
}
});
}
}
After in your activity/fragment setup your IListener:
listener = new IRecyclerViewClickListener() {
#Override
public void onLongClicked(int position) {
Toast.makeText(getActivity(), String.valueOf(position), Toast.LENGTH_SHORT).show();
}
};
Hope it help you! Good luck and sorry for my bad english ;)

Categories

Resources