It show an Null Object error.
When i am fetching an Button Value on card RecylerView
Process: com.softedge.visioneering.tfd, PID: 9884
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference
at com.softedge.visioneering.tfd.adapters.MyOrdersAdapter$MyOrdersViewHolder$1.onClick(MyOrdersAdapter.java:138)
at android.view.View.performClick(View.java:5609)
This Is My Adapter and Button Code:
public class MyOrdersViewHolder extends RecyclerView.ViewHolder {
public TextView txtOrderDescription, txtOrderQuantity, txtOrderStatus, txtOrderDate, txtExpectedDate, txtCreatedBy,txtOrderStatus_Per;
String check;
public Button update_btwn;
public MyOrdersViewHolder(View convertView) {
super(convertView);
update_btwn=(Button) convertView.findViewById(R.id.status_update_button);
final FragmentManager fragmentManager = ((FragmentActivity) context).getSupportFragmentManager();
update_btwn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
id = (Integer) update_btwn.getTag();
fragmentManager.beginTransaction().replace(R.id.container, new Status_update_Fragment().newInstance()).addToBackStack(" ").commit();
//((FragmentActivity) context).getSupportFragmentManager().beginTransaction().
// remove(((FragmentActivity) context).getSupportFragmentManager().findFragmentById(R.id.container)).commit();
}
});
I want to perform an Action on Button.
Which is place in Cards RecylerView
Use setTag() in the onBindViewHolder method of your adapter:
#Override
public void onBindViewHolder(myViewHolder viewHolder, int position) {
viewHolder.mCardView.setTag(position);
}
where mCardView is defined in the myViewHolder class.
private class myViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public View mCardView;
public myViewHolder(View view) {
super(view);
mCardView = (CardView) view.findViewById(R.id.card_view);
mCardView.setOnClickListener(this);
}
}
Use getTag() in your OnClickListener implementation:
#Override
public void onClick(View view) {
int position = (int) view.getTag();
//display toast with position of cardview in recyclerview list upon click
Toast.makeText(view.getContext(),Integer.toString(position),Toast.LENGTH_SHORT).show();
}
Did you set the tag on the button?
You can either call .setTag() or in xml android:tag="".
Also, you shouldn't set the click listener in the constructor of the view holder, do it in onBindViewHolder.
Related
I'm looking the answer how to make onClickListener button located in Recycleview Item using Butterknife. I know how to do it without Butterknife, but I can't find anything with Bt.
Does Bt supports this?
' class ViewHolder {
#BindView(R.id.title) TextView name;
#BindView(R.id.job_title) TextView jobTitle;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
#OnClick(R.id.submit)
public void submit(View view) {
// TODO submit data to server...
}
}'
Try to learn from the sample code at official site
If you want to implement the click logic in the activity then here are the steps.
1 Create an interface
public interface ClickHandler{
void onClick(int position);
}
2 Implement CLickHandler in activity
MainActivity extends AppCompatActivity implements ClickHandler{
...
public void onclick(int position){
Log.d("Check","Clicked at" + position);
}
...
adapter = new MyAdapter(this);//Pass the reference to activity as it implements the clickhandler.
...
}
3 Now your adapter has the reference for clickhandler. Similarly pass it to the viewholder and call the onCLick method from there.
class ViewHolder {
#BindView(R.id.title) TextView name;
#BindView(R.id.job_title) TextView jobTitle;
ClickHandler clickHandler;
public ViewHolder(View view, ClickHandler) {
ButterKnife.bind(this, view);
this.clickHandler = clickHandler;
}
#OnClick(R.id.submit)
public void submit(View view) {
if(clickHandler(!=null){
clickHandler.onClick(getAdapterPosition());
}
}
}
I want to be able to add items to my ReclycerView dynamically.
When an item loads -> setText() -> I add another item on list.
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final Message message = mDataset.get(position);
if(message.isAnswers()) {
holder.mAnswer1Button.setText(message.getAnswer1());
holder.mAnswer2Button.setText(message.getAnswer2());
holder.mAnswer1Button.setOnClickListener(v -> {
if(message.getChild1() > 0) {
add(position + 1, dataListShared.get(message.getChild1()));
holder.mAnswer1Button.setClickable(false);
holder.mAnswer2Button.setEnabled(false);
}
});
} else {
holder.mMessageTextView.setText(message.getMessage());
if(message.getChild1() > 0) {
add(position + 1, dataListShared.get(message.getChild1()));
holder.mMessageTextView.setEnabled(false);
}
}
}
This is what I have inside onBindViewHolder. When I am on the first case if(), and I click the button, the item is added to the list. On the second Case else(), I would like for the text to be set on this current item and than already add another one.
How can I achieve this?
Moreover, why add() works inside onClickListener but not outside of it?
The error I get is:
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
Thanks! :)
The error itself is self explanatory ... It is dangerous to setOnClickListener in onBindViewHolder. This method is step of refresh each recycler item.
You should move method setOnClickListener to ViewHolder which is inner class on your adapter.
class MyViewHolder extends RecyclerView.ViewHolder
{
private final OnClickListener mOnClickListener = new MyOnClickListener();
Button mAnswer1Button, mAnswer2Button;
public MyViewHolder (View itemView) {
super(itemView);
mAnswer1Button = (Button) itemView.findViewById(R.id.item);
mAnswer2Button = (Button) itemView.findViewById(R.id.item);
mAnswer1Button.setOnClickListener(mOnClickListener);
}
#Override
public void onClick(final View view) {
//Now your Logic ....
}
}
One more thing you could do is set Create an interface OnItemClickListener
and declare onItemClick and then make the activity from where you are setting up the adapter for the particular recycler view implment OnItemClickListener this and over there you can dynamically add another item and setAdapter again or notifyDataSetChanged()
Your InterFace
public interface OnItemClickListener{
public void onItemClick(int position);
}
Your MainActivity
class MainActivity implements OnItemClickListener{
RecyclerView mRecyclerView ;
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.layout);
mRecyclerView = (RecyclerView ) findViewById(R.id.recyclerview);
mAdapter= new MyAdapter(ArrayList, getContext(), MainActivity.this);
}
#Overrride
public void onItemClick(int position)
{
//Now your Logic ....
}
}
Now you can call this method onItemClick from the Adpater clas by setting onClickListener mAnswer1Button in the ViewHolder class and calling this method with in the onClick
As commented, I think it should work like this:
private int position;
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
this.position = position;
}
#Override
public void onViewAttachedToWindow(ViewHolder holder) {
final Message message = mDataset.get(position);
if(message.isAnswers()) {
holder.mAnswer1Button.setText(message.getAnswer1());
holder.mAnswer2Button.setText(message.getAnswer2());
holder.mAnswer1Button.setOnClickListener(v -> {
if(message.getChild1() > 0) {
add(position + 1, dataListShared.get(message.getChild1()));
holder.mAnswer1Button.setClickable(false);
holder.mAnswer2Button.setEnabled(false);
}
});
} else {
holder.mMessageTextView.setText(message.getMessage());
if(message.getChild1() > 0) {
add(position + 1, dataListShared.get(message.getChild1()));
holder.mMessageTextView.setEnabled(false);
}
}
}
In onBindViewHolder(), you apparently cannot change the dataset - I guess as the framework is still busy displaying the previous dataset. But when saving the position in an instance variable, and updating the dataset in onViewAttachedToWindow(), the RecylerView should be ready for more data.
To be honest though, I wouldn't add all this logic to the ViewHolder, but pass an interface to it so the logic can be kept in a more central place, like a presenter.
i am working on a notapad like android app project. i which i have implemented recycler.
My project contains NotedAdaper class that extends RecyclerView.Adapter<NotesAdapter.ViewHolder>
in that class using the below code i used click listener,
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.ViewHolder> {
private List<Notes> mNotes;
private Context mContext;
public NotesAdapter(Context context, List<Notes> notes) {
mNotes = notes;
mContext = context;
}
#Override
public NotesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
// Inflate the custom layout
View notesView = inflater.inflate(R.layout.items_notes, parent, false);
// Return a new holder instance
ViewHolder viewHolder = new ViewHolder(notesView);
return viewHolder;
}
// Easy access to the context object in the recyclerview
private Context getContext() {
return mContext;
}
#Override
public void onBindViewHolder(NotesAdapter.ViewHolder viewHolder, final int position) {
// Get the data model based on position
Notes notes = mNotes.get(position);
// Set item views based on your views and data model
TextView textView = viewHolder.preTitle;
textView.setText(notes.getTitle());
TextView textView1 = viewHolder.preText;
textView1.setText(notes.getText());
String color=notes.getColor();
CardView preCard=viewHolder.preCard;
preCard.setBackgroundColor(Color.parseColor(color));
ImageView img = viewHolder.preImage;
img.setVisibility(View.GONE);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Notes notes = mNotes.get(position);
Intent intent = new Intent(view.getContext(),EditNote.class);
Bundle bundle = new Bundle();
bundle.putSerializable("DATA",notes);
intent.putExtras(bundle);
getContext().startActivity(intent);
Toast.makeText(getContext(), "Recycle Click" + position+" ", Toast.LENGTH_SHORT).show();
}
});
}
// Returns the total count of items in the list
#Override
public int getItemCount() {
return mNotes.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
// Your holder should contain a member variable
// for any view that will be set as you render a row
public RobotoTextView preTitle, preText;
public ImageView preImage;
public CardView preCard;
public ViewHolder(View itemView) {
super(itemView);
preTitle = (RobotoTextView) itemView.findViewById(R.id.preTitle);
preText = (RobotoTextView) itemView.findViewById(R.id.preText);
preImage=(ImageView) itemView.findViewById(R.id.preImage);
preCard=(CardView) itemView.findViewById(R.id.preCard);
}
}}
And its absolutely working find. on clicking of a item in recycler, it retrieves the data using position of that item. and showing in another activity.
just like, suppose a activity shows the list of notes created by user. and clicking on any note, it shows the full content of that note.
but now i want to implement Long click listener on the item. and get the position.
so that, i used the following code ...
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
Notes notes = mNotes.get(position);
Toast.makeText(getContext(), "long Click" + position+" ", Toast.LENGTH_SHORT).show();
return false;
}
});
so, its also working.
but what i want is, on long click, it should only show that Toast.
but its not only showing the long click toast. but also recognising click listener and after showing the toast>> "Long click: ..." it executing the the code written for single click event.
n i dont want it.
both listeners should work separately.
but why its executing single click after long click??? any idea???
Am i making mistake anywhere?
So, the following changes in my code, help me to achieve my output.
1) The method onBindViewHolder is called every time when you bind your view with data. So there is not the best place to set click listener. You don't have to set OnClickListener many times for the one View. Thats why, i wrote click listeners in ViewHolder, (actually that was not my question, but i read somewhere that it would be the best practice, thats why i am following it)
like this,
public static class ViewHolder extends RecyclerView.ViewHolder {
// Your holder should contain a member variable
// for any view that will be set as you render a row
public ImageView preImage;
public CardView preCard;
// We also create a constructor that accepts the entire item row
// and does the view lookups to find each subview
public ViewHolder(final View itemView) {
// Stores the itemView in a public final member variable that can be used
// to access the context from any ViewHolder instance.
super(itemView);
itemView.findViewById(R.id.preTitle);
preImage=(ImageView) itemView.findViewById(R.id.preImage);
preCard=(CardView) itemView.findViewById(R.id.preCard);
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
int p=getLayoutPosition();
System.out.println("LongClick: "+p);
return true;// returning true instead of false, works for me
}
});
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int p=getLayoutPosition();
Notes notes = mNotes.get(p);
Toast.makeText(getContext(), "Recycle Click" + p +" ", Toast.LENGTH_SHORT).show();
}
});
}
}
You may notice that, in onLongClick, i have returned "true", bydefault it was "false".
and this change works for me.
just make onLongClick(View v) returns return true instead of return false
this solved my problem it should solve yours too
i think you should set both the listeners from ViewHolder class.
itemView.setOnClickListener(...);
itemView.setOnLongClickListener(...);
And call getAdapterPosition() from ViewHolder to get the adapter position of the item.
You can checkout the following resource.
https://www.bignerdranch.com/blog/recyclerview-part-1-fundamentals-for-listview-experts/
I think this is an easier way to implement onClick and longClickListeners to RecyclerViews. Here's my code snippet. I've cut out unnecessary codes from here.
public class PrescriptionAdapter extends RecyclerView.Adapter<PrescriptionAdapter.ViewHolder> {
static final String TAG = "RecyclerViewAdapterMedicineFrequency";
ArrayList<Prescription> pdata;
Context context;
OnItemClickListener onItemClickListener;
OnItemLongClickListener onItemLongClickListener;
public PrescriptionAdapter(Context context, ArrayList<Prescription> presData, OnItemClickListener onItemClickListener, OnItemLongClickListener onItemLongClickListener) {
this.pdata = presData;
this.context = context;
this.onItemClickListener = onItemClickListener;
this.onItemLongClickListener = onItemLongClickListener;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
TextView pName, totalMeds;
ImageView pImage;
OnItemClickListener onItemClickListener;
OnItemLongClickListener onItemLongClickListener;
public ViewHolder(View itemView, OnItemClickListener onItemClickListener, OnItemLongClickListener onItemLongClickListener) {
super(itemView);
pName = (TextView) itemView.findViewById(R.id.prescriptionName);
totalMeds = (TextView) itemView.findViewById(R.id.totalMeds);
pImage = (ImageView) itemView.findViewById(R.id.prescriptionImage);
this.onItemClickListener = onItemClickListener;
this.onItemLongClickListener = onItemLongClickListener;
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
#Override
public void onClick(View v) {
onItemClickListener.onItemClick(getAdapterPosition());
}
#Override
public boolean onLongClick(View v) {
onItemLongClickListener.onItemLongClick(getAdapterPosition());
return true;
}
}
public interface OnItemClickListener {
void onItemClick(int i);
}
public interface OnItemLongClickListener {
void onItemLongClick(int i);
}
}
1) Problem
I would like to set 2 listeners on 2 buttons inside a CardView. I need to know which item has been selected. I use CardView with RecyclerView and FirebaseRecyclerAdapter (RecyclerView.Adapter)
2) Old Situation
I have created the listener in populateViewHolder() of my FirebaseRecyclerAdapter like this:
#Override
protected void populateViewHolder(final ItemViewHolder viewHolder, final Item model, final int position) {
viewHolder.firstButton.setOnClickListener(new View.OnClickListener() {
Item selectedItem = getItem(position);
//do something for the item selected
}
}
this works fine!
3) What I would like to do
Set listeners in the ViewHolder class (defined in his own file) and do something on the selected ITEM
I would like to set the listeners in the ViewHolder class because the same ViewHolder is used for different adapters and I think it's a better approach to have the behaviour of the listeners defined in only one place.
I did like this:
public class ItemViewHolder extends RecyclerView.ViewHolder {
public CardView cardView;
public TextView itemText;
public ImageView itemFirstButton;
public ImageView itemSecondButton;
public ItemViewHolder(View itemView) {
super(itemView);
cardView = (CardView)itemView.findViewById(R.id.item_card_view);
itemText = (TextView)itemView.findViewById(R.id.item_text);
itemFirstButton = (ImageView)itemView.findViewById(R.id.ic_first_action);
itemSecondButton = (ImageView)itemView.findViewById(R.id.ic_second_action);
itemFirstButton.setOnClickListener(firstListener);
itemSecondButton.setOnClickListener(secondListener);
}
private View.OnClickListener firstLinstener = new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
}
};
private View.OnClickListener secondListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
}
};
}
Inside the listener I can't get any reference on the item, just the position...
I have the impression I have to declare an interface for the listener and pass to the adapter, but I don't know how.
Does it make still sense to declare the listeners in the viewholder or I keep them in the adapter as the old solution?
If you want to tell your position to the adapter, just pass the some interface through ViewHolder constructor.
OR
the previous way.
The point is whom you want to delegate the position to.
Yes it is good practice to use interface. Create an interface like
public interface OnClick {
void getItem(int item);
}
Then setOnClicklistner like
itemViewHolder. itemText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OnClick onClick = (OnClick)context;
onClick.getItem(position);
}
});
Then in your activity implement that method. From this you get position. Then by using position you get elements from array.
Got stuck on this problem for few days hope to find the solution.
I am unable to pass the data written on the card (Card View) to the next activity.
For clicking card, I am implementing its onClickListner in its recyclerView Adapter.
I want to pass the position of the card clicked but since card View does not support onItemClickListner I am facing the problem.
RecyclerViewAdapter class
public class RVAdapter extends recyclerView.Adapter<RVAdapter.PersonViewHolder> implements View.OnClickListener, View.OnLongClickListener {
int position;
#Override
public void onBindViewHolder(PersonViewHolder holder, int i) {
setEventsForViews(holder, i);
}
private void setEventsForViews(PersonViewHolder holder, int i) {
position = i;
holder.cv.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Navigator.toAddFriends(context, placeList.get(getPosition()).getPlaceId());
}
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
}
}
I tried to set up a variable position and tried to pass its value but since onBindViewHolder is called intially for all items in the view and I end up in setting the value of position as last one. I know this is the wrong approach but as I am new to this card View thing please suggest me any good and clean way to do this.
Thanks in advance.
Create a new interface, like:
public interface RecyclerViewClickListener {
public void recyclerViewListClicked(View v, int position);
}
On your Fragment/Activity, implement the new interface:
public class MyFragment extends BaseFragment implements RecyclerViewClickListener { }
Now, you will be forced to implement the interface method recyclerViewListClicked(View v, int position) on your Fragment/Activity.
Do that so you can do whatever you want when one item is clicked. Do it like so:
#Override
public void recyclerViewListClicked(View v, int position) {
myList.get(position);
}
Now lets go to your Adapter.
On your adapter constructor, add a new listener parameter. It's type is the interface we have just created (don't forget to create a private static listener and set it on your constructor):
private static RecyclerViewClickListener mListener;
public myListAdapter(List<Thing> things, final Context context, RecyclerViewClickListener itemClickListener) {
mThings = things;
mContext = context;
mListener = itemClickListener;
}
Inside your adapter, on your ViewHolder, implement View.OnClickListener like:
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
You will now be forced do #Override the onClickMethod inside your ViewHolder. Do it:
#Override
public void onClick(View v) { }
On your ViewHolder constructor, set the onClickListener of your view. Like:
public MyViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
myCardViewLayout.setOnClickListener(this);
}
Now implement the behaviour to your ViewHolder onClickMethod. In this case, I call the recyclerViewListClicked method of the static listener we declared in our adapter. This method is the one we have #Overriden in our fragment/activity, so it requires the position parameter.
Get the position by calling getLayoutPosition() inside your view holder. It will look like this:
#Override
public void onClick(View v) {
mListener.recyclerViewListClicked(v, getLayoutPosition());
}
Finally, get back to your fragment/activity, and pass the listener to the adapter constructor with this (since now we implement the interface RecyclerViewClickListener it can just pass itself as a listener to the adapter):
mRecyclerView.setAdapter(new MyListAdapter(this.mList, this.getActivity(), this))
Now, when you click on a myCardViewLayout on your list items it's going to call the recyclerViewListClicked(View v, int position) of your Fragment/Activity and you will be able to work with the position parameter as you'd like.
You can do this to any layout you want to get a click event from inside your list. Just replace myCardViewLayout.setOnClickListener(this) inside your ViewHolder constructor with myDesiredToHaveOnClickListenerLayout.setOnClickListener(this).
Hope this helps.
#EDIT:
Some tips:
Remove implements View.OnClickListener, View.OnLongClickListener from your adapter.
And looks like you don't need to call setEventsForViews(holder, i) on your onBindViewHolder anymore. Confirm?
#UPDATE
This is how your ViewHolder will look like:
public static class PersonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
CardView cv;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
cv.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.recyclerViewListClicked(v, getLayoutPosition());
}
}