I have a RecylcerView holding cards with messages, each has a like and a dislike button. When the like button is clicked the user-ID is stored in my database and the user should not be able to like it again. If he clicks it again his action should reset his like. Same should hold true for dislikes.
In my Adapter in onBindViewHolder I check in my database if the user has already clicked the button. If so, I replace the white up-arrow image with a red arrow. I also set a tag to the ImageView by setTag. Where 0 represents the unvoted case and 1 if the like-button was pressed before :
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final String mUID = getUserID();
final String postID = getPostID(position);
final PostViewHolder holder1 = (PostViewHolder) holder;
//REFERENCE WHERE USER IDs ARE STORED WHEN THEY LIKE THE POST
mRefForUID = rootRef.child("likesUIDs").child(postID);
ValueEventListener valueEventListenerForUID = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//IF USER HAS LIKED A REFERENCE IS STORED WITH "mUID+"
if (dataSnapshot.hasChild(mUID+"+")){
//SET RED ARROW IF UP IS CLICKED BEFORE AND SETTAG TO 1
holder1.mUp.setTag(1);
holder1.mUp.setImageResource(R.drawable.arrow_up_red);
} if(dataSnapshot.hasChild(mUID+"-")) {
//SAME AS ABOVE FOR THE DISLIKED CASE
//HERE I COULD SET THE TAG TO -1
} else {
//IF USER ID IS NOT KNOWN HE HAS NOT CLICKED
//SO I SET THE TAG TO 0 FOR THIS CASE
holder1.mUp.setTag(0);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mRefForUID.addListenerForSingleValueEvent(valueEventListenerForUID);
}
In my ViewHolder I define the onClick events of the buttons (in my case these are ImageViews). If the button was clicked before the onClick event should be different, so I distinguish between these states with the help of my previously initialized tag and a switch case algorithm:
public class PostViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public ImageView mUp;
public ImageView mDown;
public PostViewHolder(View itemView, postClickListener listener){
super(itemView);
mUp = mView.findViewById(R.id.arrowUp);
mDown = mView.findViewById(R.id.arrowDown);
mUp.setOnClickListener(this);
mDown.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v.getId() == mUp.getId()){
int tag = (int) mUp.getTag();
int position = getAdapterPosition();
switch(tag){
case 0:
//CASE 0 = NOT CLICKED BEFORE
//MAKE A NORMAL LIKE AND SET THE TAG TO 1 FROM HERE
case 1:
//CASE 1 = UPVOTE IS CLICKED BEFORE
//UNLIKE AND SET THE TAG TO 0 HERE
}
}
}
I leave out the case for the down-vote. So the assignment of the correct ImageView does work. When I open the app I get the red upvote button if it has been upvoted before. But the tag is always 0 when I start the app! So the onBindViewHolder does the following correctly holder1.mUp.setImageResource(R.drawable.arrow_up_red);, but does not set the tag holder1.mUp.setTag(1);.
It particularly does not work when opening the app for the first time. When clicking a couple of times I get the correct tags, since they will get set in the ViewHolder.
I hope to find the mistake or even a better solution for my intention.
Related
I have shown a list of apps installed on device in recyclerView(Image Attached), In this, I categorized apps with imageview(you can see red circle in image)
I set both icons in coding, based on condition, Now I want to show toast onclick of item based on imageView i.e. appCategory , means ...If item have imageView of phone, it shows toast "phone" otherwise "playstore"
that previous condition, I used to filter is not applicable here as it uses FOR Loop for whole list.
image for reference - Link
I tried tag and following code but doesn't worked for me
if (Category_Apps.drawable.constantState ==
ContextCompat.getDrawable(applicationContext, R.drawable.ic_phone)!!.constantState)
{
Toast.makeText(applicationContext, "Phone", Toast.LENGTH_SHORT).show()
}
else
{
Toast.makeText(applicationContext, "Play-store", Toast.LENGTH_SHORT).show()
}
I guess you should save the desired Toast's text in recycle view item itself and you can simply show it on icon click.
public class AppListAdapter extends RecyclerView.Adapter<AppListAdapter.CustomViewHolder> {
private Context context;
private List<AppItem> appItemList;
........
public class CustomViewHolder extends RecyclerView.ViewHolder {
ImageView icon;
.....
public CustomViewHolder(#NonNull View itemView) {
super(itemView);
//other code for item setup
icon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, appItemList.get(getAdapterPosition()).getIconText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
Happy coding !
I have created a horizontal recyclerview list. below is the image I have attached.
problem is that I have to change bulb state when everytime user clicked that row from off state to on state and on state to off state.
How do I implement this logic? please help me to find out the solution.
i want to change light color according to user clicks, if he clicks on then it should change its color for that row and vise versa
public void onClick(View view, int position) {
//toast("clicked" + position );
if (!lightClicked){
ImageView lightPopUp = view.findViewById(R.id.row_light_thumbnail);
DrawableCompat.setTint(lightPopUp.getDrawable().mutate(), ContextCompat.getColor(getContext(), R.color.white));
//toast("light on");
lightClicked = true;
}else {
ImageView lightPopUp = view.findViewById(R.id.row_light_thumbnail);
//toast("light off");
DrawableCompat.setTint(lightPopUp.getDrawable().mutate(), ContextCompat.getColor(getContext(), R.color.colorAccent));
lightClicked = false;
}
//View view1 = mLayoutManager.findViewByPosition(position);
}
Dont directly do this change as the recycler view's cells are reused and it wont work as expected, so instead apply the change in the list you are using. You can add a boolean variable in the model class of the list you use to populate the recycler view, and than on its click you can change the boolean's value and call notifydatasetchange on the adapter, and in bind view you should keep an If else condition based on that boolean for the Bulb's image
ie. if true set one image if false set another
declare this :
int selectedPosition=-1;
inside onBindViewHolder:
public void onBindViewHolder(FiltersAdapter.ViewHolder holder, int position) {
if(selectedPosition==position)
holder.itemView.setImageResource(R.drawable.higlihgt_image);
else
holder.itemView.setImageResource(R.drawable.normal_image);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPosition=position;
notifyDataSetChanged();
}
});
}
I have a Recyclerview, im animating a view inside individual list item, but when I scroll the recyclerview the animation is stopping. Its because recyclerview removes the items form its view so when we scroll back it fetches it back! But now i want that animation to keep going as I would stop it only when i get data from server!
All I want is the animation that I start in the individual items inside the recylerview shouldn't stop even if the recyclerview is scrolled and the view is out of focus and comes back to focus! I need to stop the animation in the code when I get the server data! I have the code where to stop the animation and it works if the item is not scrolled off the view!
btn.onClick -- this button is the onClick for the recyclerview list
item 1 btn.startAnimation(anim.xml) -- starting the animation
onSuccess -- server returns success btn.clearAnimation();
but before the onSuccess if we scroll the list the animation is stopped!
Please help!
By inspiring from crymson's answer i have made little easy and useful solution using tag method of View instead setting a boolean in complicated logic of your custom adapter.
#Override
public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
if (holder.getItemViewType() == TYPE_AD)
((ViewHolderForAd) holder).ivStory.setTag(false);
}
public class ViewHolderForAd extends RecyclerView.ViewHolder {
private ImageView ivStory;
TextView tvName;
public ViewHolderForAd(View view) {
super(view);
ivStory = (ImageView) view.findViewById(R.id.ivStoryImage);
tvName = (TextView) view.findViewById(R.id.tvAppName);
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int pos = getAdapterPosition();
if (pos < 0) {
pos = (int) v.getTag();
}
customItemClickListener.onItemClicked(v, pos);
}
});
//ivStory.startAnimation(AnimationUtils.loadAnimation(context, R.anim.pulse_story));
ivStory.setTag(false); //Set default tag false to decrease risk of null
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
//...Your code...
if (!(boolean) holder1.ivStory.getTag()) {
holder1.ivStory.setTag(true);
holder1.ivStory.startAnimation(AnimationUtils.loadAnimation(context, R.anim.pulse_story));
}
//...Your code...//
}
You can use setTag(key, object) instead of setTag(object) if you already tagged something(like position) in your imageView.
Hope this helps someone.
Hard to give you a full solution but have you tried saving the animation state inside the ViewHolder that you are using? I'd recommend saving a boolean flag in the ViewHolder class you defined like isAnimating which is initially set to false and in your onBindViewHolder(...) method you can do something like
if (viewHolder.isAnimating) {
// start animation
} else {
// clear animation
}
viewHolder.btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.isAnimating = true;
// start animation
}
});
I have a custom adapter that renders two (2) checkboxes, a picture and the name of the client. All the information needed for the adapter is fetched from an ArratList that contains the Client class.
Every row needs to have both checkboxes checked (selected) for the client in order to process the purchase order, in case that a particular client has one checkbox checked-off and the other checkbox not, that raises a flag as MISMATCH. To make a valid order both checkboxes need to be checked-off.
We are implementing a button for verification, which will find any mismatch in the adapter and then hightlight the mismatches.
EDITION: After pressing the verificationBtn I am able to identify if any row has mismatch on checkboxes, for example, if checkbox1 was checked and checkbox2 not. that will mark the row as mismatch. I am using the position of my checkboxes based on clientList that is an arraylist of List clientList.
QUESTION: How can I get the position that the viewHolder has in order compare against the clientList position? Is there any way I can force the viewHolder to store the position and get it back and make the comparison with cli.getClient_number() ?
So far I have tested two different ways with no luck:
Method 1:
viewHolder.clientName.setBackgroundColor((Interger.parseInt(cli.getClient_number()) ) == position ? Color.GREEN : Color.TRANSPARENT);
Method 2
viewHolder.clientName.setBackgroundColor(Color.GREEN);
here the code that I am implementing.
// This goes in my main Client Activity
Button verificationBtn = (Button) findViewById(R.id.verificationBtn);
verificationBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
buffer.setLength(0);
mismatchTv.setText("");
for (Client cli : clientList) {
if (cli.isCheckboxOneSelected() != cli.isCheckboxTwoSelected()) {
//We had defined above the following buffer = new StringBuffer();
buffer.append((ah.parseInt(cli.getClient_number(), 0) - 1) + ", ");
cli.setMismatch(true);
//We are passing here the ID that correspond to the client mismatch
list_adapter.setBackgroundColor(Integer.parseInt(cli.getClient_number()) - 1);
setListAdapter(list_adapter);
Log.w("cli.getClient_number() ", String.valueOf(Integer.parseInt(cli.getClient_number()) - 1));
}
}
// We display any mismatch on a TextView on top of the screen
if (buffer.length() != 0) {
//This is a TextView on top of the screen
mismatchTv.setText("Error en Client(s) "
+ buffer.toString());
}
// This goes inside of the ClientArrayAdapter
public void setBackgroundColor(int position) {
Log.w("inside of setBackgroundColor method", "True");
switchIndex = 1;
positionFetched = position;
}
// This goes inside of the ClientArrayAdapter
// and inside the body of public View getView(int position, View convertView, ViewGroup parent) {
switch (switchIndex) {
case 1:
viewHolder.cbone
.setButtonDrawable(R.drawable.btn_checkbox_selector);
viewHolder.cbtwo
.setButtonDrawable(R.drawable.btn_checkbox_selector);
Log.w("switch 1 was called ", "True");
for (Client cli : clientList) {
if (cli.isCheckboxOneSelected() != cli.isCheckboxTwoSelected()) {
Client cli = getItem(positionFetched);
if (cli.isMismatch()) {
cli.setColor(Color.BLACK);
Log.e("if (cli.isMismatch()) ", "");
//HERE WE ARE TRYING TO HIGHLIGHT THE ROW WITH MISMATCH
//WHY THIS LINE DOES NOT WORK?
//THE ISSUE THAT I AM GETTING IS THAT I CANNOT CONTROL WHAT ROW TO AFFECT
//IN THE VIEW HOLDER
viewHolder.clientName.setBackgroundColor(Color.GREEN);
}
}
}
break;
default:
viewHolder.cbone.setButtonDrawable(R.drawable.disabled_cb);
viewHolder.cbtwo.setButtonDrawable(R.drawable.disabled_cb);
break;
}
// This goes inside of the ClientArrayAdapter
static class ViewHolder {
public TextView clientName;
public TextView clientNumber;
public ImageView imageView;
public CheckBox cbtwo;
public CheckBox cbone;
public int position;
}
After three days trying to figure out what's wrong with this code, I finally found the solution moving the Method #1 just at the very end of the getView method. :-)
I want to set the background color of clicked TextView included in LinearLayout. But I am unable to set the respective background. I faced not
clear previous clicked background . if I clicked all then set all background color.
Would you suggest me ,
how to set clickable TextView included in LinearLayout background.
Here is my sample code:
holder.txtName = (TextView) convertView.findViewById(R.id.row_cell_text_dummy_multilevel);
holder.l_select = (LinearLayout) convertView.findViewById(R.id.linear_select);
holder.txtName.setTag(position);
holder.txtName.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
select_pos=(Integer) holder.txtName.getTag();
if (position==select_pos) {
holder.l_select.setTextColor(Color.RED);
}else {
holder.l_select.setTextColor(Color.WHITE);
}
}
});
I managed to get this much done, though you will have to do some homework and find out how to reflect the changes yourself. At the moment, the change gets reflected only when the view is scrolled.
But i hope it helps you.
1.declare a static variable that sets the position clicked on
private static int selectedPostion;
2.set the selectedPosition's value to -1 in the constructor
3.in the getView method in the onclickListener do this:
int value = (Integer)((TextView)v).getTag();
Log.e("tag","(TextView)v).getTag() : " + value);
Log.e("tag", "position : " + position);
if(value == position) {
selectedPostion = position;
}else {
selectedPostion = -1;
}
4.Under the onClick code entirely before return view write this:
if(selectedPostion == position) {
view.setBackgroundColor(mContext.getResources().getColor(R.color.even_color));
// or holder.l_select.setTextColor(Color.RED);
}else {
view.setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
// or holder.l_select.setTextColor(Color.WHITE);
}
Hope it helps!