I have a RecyclerView with post cards. So there is a like button in every post card. When I tap on a like button. Sometimes it works fine and updates on current item. But after some scroll when I tap on like button it updates on another item simultaneously.
It doesn't send that data to server. But, only the view is updated. Then when I scroll up and down again. The data goes back to normal.
Problem is in the following onClick method:-
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child("like").exists()){
holder.like.setImageResource(R.drawable.ic_favorite_red_500_36dp);
holder.like.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).child("like").removeValue();
}
});
}else if(dataSnapshot.child("dislike").exists()) {
holder.like.setImageResource(R.drawable.brokenheart);
holder.like.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).child("dislike").removeValue();
}
});
}else{
holder.like.setImageResource(R.drawable.ic_favorite_border_red_500_36dp);
holder.like.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).child("dislike").removeValue();
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).child("like").setValue(true);
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
holder.like.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).child("like").removeValue();
FirebaseDatabase.getInstance().getReference().child("topics").child("likes").child(data.getId()).child(username).child("dislike").setValue(true);
return true;
}
});
Full Adapter class https://pastebin.com/UnFGahWT
Try the following:-
Set Your on click listeners in the public ViewHolder(View itemView) method and make Your ViewHolder class implement the View.OnClickListener.
In Adapter add:
public Topic getItem(int position) {
return topics.get(position);
}
In ViewHolder's onClick method add:
int position = getAdapterPosition();
if (position == RecyclerView.NO_POSITION) return;
item = getItem(position);
Thus You will get the exact object You need to change or do something with it.
Related
i've this issue made me crazy. When retrieved JSON data i will use this my adapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof OriginalViewHolder) {
final DomandaQuizTipo1 domandaQuiz1 = items.get(position);
OriginalViewHolder vItem = (OriginalViewHolder) holder;
/* Getting each answer from array of answer */
Risposta1 risposta1 = domandaQuiz1.getRisposte().get(0);
Risposta1 risposta2 = domandaQuiz1.getRisposte().get(1);
Risposta1 risposta3 = domandaQuiz1.getRisposte().get(2);
/* Get and set text of question */
vItem.domanda.setTypeface(typefaceRegular);
vItem.domanda.setText(domandaQuiz1.getDomanda());
/* Get and set text of answer 1 */
vItem.risposta1.setText(risposta1.getRisposta());
vItem.risposta1.setTextOn(risposta1.getRisposta());
vItem.risposta1.setTextOff(risposta1.getRisposta());
/* Get and set text of answer 2 */
vItem.risposta2.setText(risposta2.getRisposta());
vItem.risposta2.setTextOn(risposta2.getRisposta());
vItem.risposta2.setTextOff(risposta2.getRisposta());
/* Get and set text of answer 3 */
vItem.risposta3.setText(risposta3.getRisposta());
vItem.risposta3.setTextOn(risposta3.getRisposta());
vItem.risposta3.setTextOff(risposta3.getRisposta());
/* When i click the first answer uncheck 2nd and 3rd */
vItem.risposta1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.punteggiDomandeQuiz1[position] = items.get(position).getRisposte().get(0).getPunteggio();
MainActivity.UpdatePunteggioTotale();
vItem.risposta2.setChecked(false);
vItem.risposta3.setChecked(false);
}
});
/* When i click the second answer uncheck 1st and 3rd */
vItem.risposta2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.punteggiDomandeQuiz1[position] = items.get(position).getRisposte().get(1).getPunteggio();
MainActivity.UpdatePunteggioTotale();
Log.d("adapterposition", "POS" + position);
vItem.risposta1.setChecked(false);
vItem.risposta3.setChecked(false);
}
});
/* When i click the third answer uncheck 1st and 2nd */
vItem.risposta3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.punteggiDomandeQuiz1[position] = items.get(position).getRisposte().get(2).getPunteggio();
MainActivity.UpdatePunteggioTotale();
vItem.risposta1.setChecked(false);
vItem.risposta2.setChecked(false);
}
});
} else {
((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
}
}
My issue is this: in a recyclerview of each question that contains 3 answers to choose, when i click on one of answer (ex. first toggle button) is checked correctly but when i scroll recycler view i see other question with answer (ex. first toggle button) checked.
How can i avoid this issue? Take a look from this gif i putted below.
Check gif this please to understand
You should add a field like a selected (local not server) and check that's is selected and change item ui
if(vItem.selected==1)
vItem.risposta1.setChecked(true);
else
vItem.risposta1.setChecked(false);
if(vItem.selected==2)
vItem.risposta2.setChecked(true);
else
vItem.risposta2.setChecked(false);
if(vItem.selected==3)
vItem.risposta3.setChecked(true);
else
vItem.risposta3.setChecked(false);
vItem.risposta1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.punteggiDomandeQuiz1[position].selected=1;
notifyItemChange(adapterPosition);
}
});
vItem.risposta2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.punteggiDomandeQuiz1[position].selected=2;
notifyItemChange(adapterPosition);
}
});
vItem.risposta3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.punteggiDomandeQuiz1[position].selected=3;
notifyItemChange(adapterPosition);
}
});
You have to set the status of each toggle button in onBindViewHolder.
Something like:
vItem.risposta1.setText(risposta1.getRisposta());
//...
vItem.risposta1.setChecked(risposta1.isChecked());
Also when you update the data in the OnClickListener you have to notify the adapter:
adapter.notifyDataSetChanged();
I use RecyclerView for searching for users.
the search works fine, but the follow button in the ItemView is not.
Whenever I press the follow button, the text on the button is supposed to change from "follow" to "following". and and when I press it again it supposed to change from "following" to "follow". it was working just fine and suddenly it just stopped working!
Whenever I press the button it changes from "follow" to "following" for a sec and then it return to "follow". I couldn't figure out the reason!
Here is my code.
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
final User user = mUsers.get(position);
isFollowing(user.getId(), holder.btn_follow);
holder.btn_follow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (holder.btn_follow.getText().toString().equals("follow"))
{
FirebaseDatabase.getInstance().getReference().child("Follow").child(firebaseUser.getUid())
.child("following").child(user.getId()).setValue(true);
FirebaseDatabase.getInstance().getReference().child("Follow").child(user.getId())
.child("followers").child(firebaseUser.getUid()).setValue(true);
addNotifications(user.getId());
}
else
{
FirebaseDatabase.getInstance().getReference().child("Follow").child(firebaseUser.getUid())
.child("following").child(user.getId()).removeValue();
FirebaseDatabase.getInstance().getReference().child("Follow").child(user.getId())
.child("followers").child(firebaseUser.getUid()).removeValue();
// FirebaseDatabase.getInstance().getReference("Notifications").child(user.getId()).removeValue();
}
}
});
}
Here is the isFollowing function:
private void isFollowing(final String userid, final Button button) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference()
.child("Follow").child(firebaseUser.getUid()).child("following");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child(userid).exists()) {
button.setText("following");
button.setBackgroundResource(R.drawable.buttonafter);
} else {
button.setText("follow");
button.setBackgroundResource(R.drawable.buttonbefore);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
It keeps returning to the default "follow" text on the button, so it is not allowing me to follow anyone.
Please help if you know what's causing this problem.
Move your implementation of onClickListener() of your button from onBindViewHolder() method to your ViewHolder class of your RecyclerView. It's not a good idea to implement listeners in onBindViewHolder().
Which is the most recommended way of handling click on RecyclerView items?
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
private RecyclerView.OnItemTouchListener itemTouchListener;
#Override
public boolean onInterceptTouchEvent(#NonNull RecyclerView recyclerView, #NonNull MotionEvent motionEvent) {
return false;
}
#Override
public void onTouchEvent(#NonNull RecyclerView recyclerView, #NonNull MotionEvent motionEvent) {}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean b) {}
});
Or setting a click listener on an item view inside the adapter?
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Do stuff
}
});
A Best and efficient way to get a click of recycler view item is by using interface as below:
1) First, define one interface as below
public interface OnListItemClick {
void onClick(View view, int position);
}
2) Now, set it from your fragment or activity
OnListItemClick onListItemClick = new OnListItemClick() {
#Override
public void onClick(View view, int position) {
// you will get click here
// do your code here
}
};
youradaptor.setClickListener(onListItemClick);
3) Now create one method in your adapter
public void setClickListener(OnListItemClick context) {
this.onListItemClick = context;
}
4) Now, from your itemview click use as below
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onListItemClick.onClick(view, getAdapterPosition()); // passing click to interface
}
});
Most common way it to use interfaces to handle onClick events inside a Recycler View.
Great read about this topic here
Go for your second opinion. But an interface should be implemented to handle the OnClick Event
I am working on a project in android and i just learn about card view class.
I made a card who generates a toast when user clicks on it.
But i also want my card to call another activity when user clicks on it.
I am posting part of my code below.
btnProceed.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
showToast("Proceed to the next step");
Intent intent = new Intent(MyLocationUsingLocationAPI.this, click_picture.class);
startActivity(intent);
}
});
I have made changes in my code as you said but when i click on proceed button my app crashes.What is wrong with code?
the main idea here is to define your actionClickListener
1. create a custom recycleView Adapter
public class AdapterCustomList extends RecyclerView.Adapter<RecyclerView.ViewHolder>
2. define onItemClickListener interface
public interface OnItemClickListener {
void onItemClick( whateverArgsYouWant );
}
3. make an attribute of the interface and define a setter for it
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mOnItemClickListener = mItemClickListener;
}
4. append a listener to each item while its created in the adapter class
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
...
OriginalViewHolder vItem = (OriginalViewHolder) holder;
vItem.baseCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick( whateverArgsYouWant );
}
}
});
}
this method will be called when items of recycle view is created (in case you use card view inside a recycle view)
5. use your onClickListener in the Activity you want
AdapterCustomList mAdapter = new AdapterCustomList (getActivity(), recyclerView,yourListItemsHere));
recyclerView.setAdapter(mAdapter);
// on item list clicked
mAdapter.setOnItemClickListener(new AdapterPostList.OnItemClickListener() {
#Override
public void onItemClick( whateverArgsYouWant ) {
...
statements
...
}
});
Checkbox is selected randomly in Recyclerview when using notifyDatasetChange. Here is my code.
public void onBindViewHolder(final ViewHolder holder, int position) {
SplitObject object=splitObjects.get(position);
holder.textView_memberName.setText(object.getMember_name());
holder.textView_splitAmount.setText(amount/splitObjects.size()+"");
if(holder.checkBox.isChecked())
{
Debug.e("checked"+position);
}
else
{
Debug.e("Unchecked"+position);
}
holder.checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
notifyDataSetChanged();
}
});
}
I am not able to figure out why notifyDataSetChanged function is not working properly.
Use checkChangeListener instead of onClickListener
holder. checkBox.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.checkBox.setChecked(pass the boolean from your list);
holder.checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//set your object's last status
yourarrayobject.setSelected(isChecked);
}
});
I think check box selected when you scroll the recyleview,YOu can please Viewholder concept,So add viewHolder.CheckBox.setChecked(isItemChecked); to onBindViewHolder and it will work. You will have to replace isItemChecked with your logic of retrieving the actual state for the item at the position...
Your notifyDataSetChanged() needs to run on the UI Thread. I had faced similar difficulties where the adapter wasn't getting properly updated. Try doing this in your OnClickListener:
#Override
public void onClick(View v) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
}