I have a RecyclerView that holds a bunch of cards (fetches data from the web, then populates cards). I have the cards forming properly after data is fetched, but now I would now like the set a listener for when each card is clicked. Ideally, after each card is clicked, a new intent will be called with further details about that card's data.
Here is where I initialize the adapter. arrList is an ArrayList that has already been filled from another class
private void initializeAdapter() {
RecycleViewAdapter adapter = new RecycleViewAdapter(arrList);
rv.setAdapter(adapter);
}
This is another class that holds the cards:
public class PersonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
CardView cv;
TextView crimeName,crimeID, crimeAddress, crimeDate, crimeWeapon;
ImageView crimePhoto;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView)itemView.findViewById(R.id.cv);
crimeName = (TextView) itemView.findViewById(R.id.crime_name);
crimeID = (TextView) itemView.findViewById(R.id.crime_id);
crimeAddress = (TextView) itemView.findViewById(R.id.crime_address);
crimeDate = (TextView) itemView.findViewById(R.id.crime_date);
crimeWeapon = (TextView) itemView.findViewById(R.id.crime_weapon);
crimePhoto = (ImageView)itemView.findViewById(R.id.crime_photo);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
// WHAT DO I CALL HERE TO GET THE INDEX OF THE ITEM CLICKED ON ?? //
Log.d("app", "clicked");
}
}
the onClick from above should go to this class (by an intent) and pass the index along with it:
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
public class CrimeDetails extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime_details);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.crime_details, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_crime_details, container, false);
return rootView;
}
}
}
How can I get the Clicked Position of the Card ?
Thanks
Do net set the onClickListener in your onCreateVH method but in onBindViewHolder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((PersonViewHolder)holder).yourRootLayout.setOnClickListener(getPersonClickListener(position));
}
View.OnClickListener getPersonClickListener(final int position) {
return new View.OnClickListener() {
#Override
public void onClick(View view) {
//TODO whatever you want with position
}
};
}
EDIT:
tyczj is right. Binding the Clicklistener in the onBindViewHolder is a very easy but maybe not the best Solution - however it will work!
Another way is to use the getAdapterPosition() method from Viewholder itself and bind it in the onCreateViewHolder:
#Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.yourLayoutXml, parent, false);
final ViewHolder holder = new PersonViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final int position = holder.getAdapterPosition();
//check if position exists
if (position != RecyclerView.NO_POSITION) {
//TODO whatever you want
}
}
});
return holder;
}
you use getAdapterPosition() in your ViewHolder
I am doing this slightly different, using a different onClick call, then I can use the
getAdapterPosition()
method to determine which item was clicked.
public static class MyViewHolder extends RecyclerView.ViewHolder {
...
RelativeLayout myLayout = (RelativeLayout) v.findViewById(R.id.device_adap_layout);
myLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context cxt = myLayout.getContext();
final Intent i = new Intent(cxt, NextActivity.class);
i.putExtra("ITEMNAME", mAdapterList.get(getAdapterPosition()));
cxt.startActivity(i, options.toBundle());
}
});
...
}
Create adapter class which extend Recycle view and override needed method:
public class Adapter extends RecyclerView.Adapter
"<"Adapter.AdapterViewHolder">" {
public List<Object> Items;
private Context context;
public Adapter(List<Object> items, Context context) {
Items = items;
this.context = context;
}
#Override
public int getItemCount() {
return comentsItems.size();
}
#Override
public void onBindViewHolder(final AdapterViewHolder hd, int position) {
int indexOfCard = position;
hd.itemText.setText("pos: "+ position);
}
#Override
public AdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.item_layout, viewGroup, false);
return new AdapterViewHolder(itemView);
}
public static class AdapterViewHolder extends RecyclerView.ViewHolder {
protected TextView itemText;
public AdapterViewHolder(View v) {
super(v);
itemText = (TextView)v.findViewById(R.id. itemText);
}
}
}
You should set the onClickListener in onBindViewHolder where you already have the position:
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
listener.onItemSelected(position);
}
}
});
}
EDIT:
Like #tyczj says, for the RecyclerView it's better to use getAdapterPosition in ViewHolder.
When you populate the cardView with your data, you can also use the View's setTag() method to store the position. Since you will get the View when onClick is called, you can simply call getTag() on the View to retrieve the position.
Related
I have menu option in Recyclerview item row when user clicks on that row element then I want to show pop menu. I have implemented this functionality and on click message is showing on log but popmenu is not showing up.
Below is my code:
public class PdfAdapter extends RecyclerView.Adapter<PdfAdapter.ViewHolder> {
Context context;
List<Pdfs> pdfList;
public PdfAdapter(Context context, List<Pdfs> pdfList) {
this.context = context;
this.pdfList = pdfList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.pdf_item_row,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Pdfs model = pdfList.get(position);
holder.pdfName.setText(model.getFilenamename());
holder.datTime.setText(model.getDatetime());
holder.menu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("clik","Hello");
PopupMenu popupMenu = new PopupMenu(context,holder.menu);
popupMenu.inflate(R.menu.row_menu);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.itemShare:
break;
case R.id.itemDel:
break;
case R.id.itemDownload:
Toast.makeText(context,model.getFileurl(),Toast.LENGTH_SHORT).show();
break;
}
return false;
}
});
}
});
}
#Override
public int getItemCount() {
return pdfList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView pdfName,menu,datTime;
public ViewHolder(#NonNull View itemView) {
super(itemView);
pdfName = itemView.findViewById(R.id.pdfName);
menu = itemView.findViewById(R.id.menu);
datTime = itemView.findViewById(R.id.datTime);
}
}
}
What am I doing wrong?
You forgot to call .show() method on popupMenu.
Add below line at last inside click listener.
popupMenu.show();
I have RecyclerView in my Main Activity. I want to change different fragments after clicking on different recyclerview Items. I try this by using position from onbindviewholder() but i am not able to change the fragment when i click on recyclerview items. I am not getting any error but at the same time nothing is happening onclick of recyclerview. Please check my code and tell me where i am wrong or which line of code i am missing.
RecyclerAdapter Code:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
ArrayList<NewModel> newModels;
Context context;
public RecyclerAdapter(ArrayList<NewModel>newModels, Context context){
this.newModels = newModels;
this.context = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recy_layout, parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
holder.imageView.setImageResource(newModels.get(position).getImg());
holder.textView.setText(newModels.get(position).getText());
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(position ==1){
((FragmentActivity)context).getSupportFragmentManager().beginTransaction()
.replace(R.id.framelayout, new FirstFragment());
}
else if(position ==2){
((FragmentActivity)context).getSupportFragmentManager().beginTransaction()
.replace(R.id.framelayout, new SecondFragment());
}
}
});
}
#Override
public int getItemCount() {
return newModels.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.cirlceimg);
textView = itemView.findViewById(R.id.text1);
}
}
}
you forgot to call commit()
do this:
(((FragmentActivity)context).getSupportFragmentManager().beginTransaction()
.replace(R.id.framelayout, new SecondFragment()).commit();
Fragment is not rendered until you call commit() on the transaction , because transaction use builder pattern and in builder pattern there is a method to say that i don't want to do anything else just create the object, normally these method are name create() , build(), and commit() in this case
You can handle the onClickListener from your activity using callback implementation with an interface.
Interface:
public interface OnItemClickListener {
void onItemClick(int position);
}
Add the listener to your adapter's constructor
ArrayList<NewModel> newModels;
Context context;
private final OnItemClickListener listener;
public RecyclerAdapter(ArrayList<NewModel>newModels, Context context, OnItemClickListener listener){
this.newModels = newModels;
this.context = context;
this.listener = listener;
}
In your onBindViewHolder set the clicklistener
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
holder.imageView.setImageResource(newModels.get(position).getImg());
holder.textView.setText(newModels.get(position).getText());
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onItemClick(position);
}
});
}
In your activity:
RecyclerAdapter rvAdapter = new RecyclerAdapter(newModels, this, new RecyclerAdapter.OnItemClickListener() {
#Override public void onItemClick(int item) {
if(position ==1){
getSupportFragmentManager().beginTransaction().replace(R.id.framelayout, new FirstFragment()).commit();;}
else if(position ==2){
getSupportFragmentManager().beginTransaction().replace(R.id.framelayout, new SecondFragment()).commit();;
}
}});
recycler.setAdapter(rvAdapter);
Inside onBindViewHolder
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(position ==1){
((FragmentActivity)context).getSupportFragmentManager().beginTransaction()
.replace(R.id.framelayout, new FirstFragment()).addToBackStack(null).commit();
}
else if(position ==2){
((FragmentActivity)context).getSupportFragmentManager().beginTransaction()
.replace(R.id.framelayout, new SecondFragment()).addToBackStack(null).commit();
}
}
});
}
Hope this helps you.
I need to call an Interstitial in this Activity by clicking on an item in the list that comes from my Adapter.
public class SoundRecyclerAdapter extends RecyclerView.Adapter<SoundRecyclerAdapter.SoundViewHolder> {
private final ArrayList<SoundObject> soundObjects;
SoundRecyclerAdapter(ArrayList<SoundObject> soundObjects) {
this.soundObjects = soundObjects;
}
#NonNull
#Override
public SoundViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.sound_item, parent, false);
return new SoundViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull SoundViewHolder holder, int position) {
final SoundObject object = soundObjects.get(position);
final Integer soundID = object.getItemID();
holder.itemTextView.setText(object.getItemName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// I tried calling the interstitial here, but to no avail
EventHandlerClass.startMediaPlayer(v, soundID);
}
});
// Handle actions when the user presses a sound button
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
EventHandlerClass.popupManager(v, object);
return true;
}
});
}
#Override
public int getItemCount() {
return soundObjects.size();
}
class SoundViewHolder extends RecyclerView.ViewHolder {
final TextView itemTextView;
SoundViewHolder(View itemView) {
super(itemView);
itemTextView = itemView.findViewById(R.id.textViewItem);
}
}
}
Pass context as parameter of adapter's constructor:
private Context context;
SoundRecyclerAdapter(Context context, ArrayList<SoundObject> soundObjects) {
this.soundObjects = soundObjects;
this.context = context;
}
and use it in click handler
Use this code in the setOnClickListener() method:
mInterstitialAd = new InterstitialAd(SoundRecyclerAdapter.this);
Then add the adrequests and load the ad.
I have a setOnClick listener inside my onBindViewHolder
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
((ViewHolder) holder).txtType.setText(object.text);
((ViewHolder) holder).checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, ""+position, Toast.LENGTH_SHORT).show();
//???.scrolltoPosition(position+1);
}
});
on click, I would be scrolling the viewport to the next recycler view item. I've done this inside my MainActivity java by recyclerView.scrollToPosition(position);. But inside Adapter Class on onBindViewHolder, I have no idea how to call my recyclerView on the Main Java Class.
How should I do this?
edit:
For simplification purposes, I revised this previous code, because I have used a multiview adapter for my recycler. Here is the multiview adapter:
public class MultiViewTypeAdapter extends RecyclerView.Adapter {
private ArrayList<InstructionModel>dataSet;
Context mContext;
int total_types;
private LinearLayoutManager manager;
public MultiViewTypeAdapter(LinearLayoutManager manager)
{
this.manager=manager;
}
public static class SimpleTextViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
CardView cardView;
CheckBox checkBox;
public SimpleTextViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.cardView = (CardView) itemView.findViewById(R.id.card_view);
this.checkBox = (CheckBox) itemView.findViewById(R.id.checkBox);
}
}
public static class TimeTextViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
CardView cardView;
CheckBox checkBox;
public TimeTextViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.cardView = (CardView) itemView.findViewById(R.id.card_view);
this.checkBox = (CheckBox) itemView.findViewById(R.id.checkBox);
}
}
public MultiViewTypeAdapter(ArrayList<InstructionModel> data, Context context) {
this.dataSet = data;
this.mContext = context;
total_types = dataSet.size();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case InstructionModel.SIMPLE_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.simple, parent, false);
return new SimpleTextViewHolder(view);
case InstructionModel.TIME_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.time, parent, false);
return new TimeTextViewHolder(view);
}
return null;
}
#Override
public int getItemViewType(int position) {
switch (dataSet.get(position).type) {
case 0:
return InstructionModel.SIMPLE_TYPE;
case 1:
return InstructionModel.TIME_TYPE;
default: return -1;
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) {
InstructionModel object = dataSet.get(listPosition);
if (object != null) {
switch (object.type) {
case InstructionModel.SIMPLE_TYPE:
((SimpleTextViewHolder) holder).txtType.setText(object.text);
break;
case InstructionModel.TIME_TYPE:
((TimeTextViewHolder) holder).txtType.setText(object.text);
break;
}
}
}
#Override
public int getItemCount() {
return dataSet.size();
}
P.S.
If you see some random useless stuff, I was trying something.
Pass the LayoutManger with constuctor
class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
private LinearLayoutManager manager;
public Adapter(LinearLayoutManager manager)
{
this.manager=manager;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
((ViewHolder) holder).txtType.setText(object.text);
((ViewHolder) holder).checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, ""+position, Toast.LENGTH_SHORT).show();
manager.scrollToPosition(position+1);
}
});
}
I think you should define a interface to notify your parent RecyclerView to do scroll operation, the RecyclerView into ViewHodler inside operation, resulting in the coupling
You can pass a listener object in the constructor which implements by Fragment OR Activity.
/**
* item click interface of adapter
*/
public interface MultiViewTypeAdapterListener {
void onScroll(int position)
}
This interface implements by Fragment OR Activity
/**
* On Scroll Implement Method from adapter listener.
*
* #param position
*/
#Override
public void onScroll(int position) {
// Here you can call that method
recyclerview.scrolltoPosition(position)
}
Then you pass this listener in the constructor of the adapter.
private void buildRecyclerView() {
multiViewTypeAdapter = new MultiViewTypeAdapter(this);
recyclerView.setAdapter(multiViewTypeAdapter);
}
In the constructor, you can assign like this
private MultiViewTypeAdapterListener mMultiViewTypeAdapterListener;
public OfferAdapter(MultiViewTypeAdapterListener mMultiViewTypeAdapterListener) {
this.mMultiViewTypeAdapterListener = mMultiViewTypeAdapterListener
}
}
Now you can use this listener by setting click listener on anyViwe like this
((ViewHolder) holder).checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, ""+position, Toast.LENGTH_SHORT).show();
//???.scrolltoPosition(position+1);
mMultiViewTypeAdapterListener.onScroll(position+1);
}
});
It returns to call the method of onItemClick which implements this method.
private int getScrollRange() {
int scrollRange = 0;
if (parent.getChildCount() > 0) {
View child = parent.getChildAt(0);
scrollRange = Math.max(0,
child.getHeight() - (parent.getHeight()));
}
return scrollRange;
}
ScrollView parent=findViewById(R.id.scroll_Recipe_Detail);
parent.scrollTo(0,getScrollRange());
if you want to scroll on new items added, you must override "onItemsAdded".
Here is how I did it:
recyclerView.apply {
setHasFixedSize(true)
layoutManager = MyLinearLayoutManager(this#MainActivity)
...
}
private class MyLinearLayoutManager(private val context: Context) :
LinearLayoutManager(context) {
// Force new items appear at the top
override fun onItemsAdded(recyclerView: RecyclerView, positionStart: Int, itemCount: Int) {
super.onItemsAdded(recyclerView, positionStart, itemCount)
scrollToPosition(findLastVisibleItemPosition())
}
}
I have created a RecyclerView, and in the Adapter I am hoping to create a ClickListener. I want each item in the RecyclerView to send a unique response after click. In other words, a click is loading a different fragment for each specific RecyclerView item. Right now, the RecyclerView is not responding, as I have logged it and even added a Toast without any luck:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<LatestPoll> mDataSet;
int lastPosition = -1;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// each data item is just a string in this case
protected TextView pollQuestion;
protected ImageView pollImage;
protected RelativeLayout individualItem;
public ViewHolder(View v) {
super(v);
pollQuestion = (TextView) v.findViewById(R.id.latest_item_question);
pollImage = (ImageView) v.findViewById(R.id.pollThumbNailImage);
individualItem = (RelativeLayout) v.findViewById(R.id.individual_item);
individualItem.setOnClickListener(this);
}
#Override
public void onClick(View v) {
v.getId();
int itemPosition = getLayoutPosition();
String item = String.valueOf(mDataSet.get(itemPosition));
Toast.makeText(getActivity().getApplicationContext(), item, Toast.LENGTH_LONG).show();
Log.v("TESTING", "ITEM CLICKED!");
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<LatestPoll> myDataset) {
mDataSet = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.latest_item, parent, false);
// set the view's size, margins, paddings and layout parameters
return new ViewHolder(v);
}
// Replace the contents of a view (invoked by the layout manager)
//The OutOfBoundsException is pointing here
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.v("ON_BIND", "ON_BINDVIEWHOLDER CALLED");
LatestPoll latestPoll = mDataSet.get(position);
holder.pollQuestion.setText(latestPoll.getQuestion());
Picasso.with(getActivity())
.load(latestPoll.getPollImage())
.fit()
.placeholder(R.drawable.loading_spinnter_white)
.into(holder.pollImage);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataSet.size();
}
}
private void onLoad() {
if (mFragmentListener != null) {
mFragmentListener.onFragmentLoaded();
}
}
public interface FragmentListener {
void onFragmentLoaded();
}
}
You need to set onclick listener to the parent layout inside the onBindViewHolder .
holder.parentID.setOnClickListener(mRecyclerViewListener);
In your View holder
parentID = iteView.findViewById(R.id.parent);