I've been working on an online shop type of application, and I've hit a bump: I've been tasked to add a favorites system, but I can't figure out how to enable pressing a button that's part of the RecyclerView item to add it to favorites.
(In this case, the heart, which is supposed to turn to a full heart when clicked)
Add a boolean value for favourite in your list . Initially , keep it false .
You need to have two drawables , one for selected state and another for unselected state .
In your onBindViewHolder , set the drawable on runtime on the basis of above condition .
if(list.isfav)
{
holder.ivHeart.setImageDrawable(ContextCompat.getDrawable(context,(R.drawable.selected));
}else{
holder.ivHeart.setImageDrawable(ContextCompat.getDrawable(context,(R.drawable.unselected));
}
Put onClick on this ivHeart eg:
holder.ivHeart.setOnClickListener(v -> {
if(list.isfav) {
list[adapterPosition].isfav = false;
}else{
list[adapterPosition].isfav = true;
}
notifyItemChanged(adapterPosition);
});
Dont forget to notify the item while changing item .
In your RecyclerView adapter's onBindViewHolder() method, add click listener to your view and change the drawable programmatically.
The code will be something like this
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
holder.your_like_imageview.setOnClickListener{
holder.your_like_imageview.setImageDrawable(ContextCompat.getDrawable(context,R.drawable.something_else));
};
}
I'm assuming you're using an ImageView for the heart. What you can do is set a click listener on that ImageView and process that click.
An ideal way to do this would be to use an interface to handle click events that you pass to the Adapter.
However you could do something like this in the onBindViewHolder method:
imageView.setOnClickListener {
// depending your logic change the tint for the icon or the drawable
onClick(data[position], addToWishlist)
notifyDataSetChanged()
}
The onClick method will receive the particular item and a flag to add or remove it from
the wishlist:
fun onClick(data: Data, addToWishlist: Boolean) {
// you can perform the addition/ deletion from the wishlist here
}
Related
class file easily access to every page in Android
Initially set it zero
lastFirstVisiblePosition = 0;
You can store a scroll position on the scroll and set a scroll position like this.
((LinearLayoutManager) rv.getLayoutManager()).scrollToPosition(lastFirstVisiblePosition);
and if that not work try below
((LinearLayoutManager) rv.getLayoutManager()).scrollToPositionWithOffset(lastFirstVisiblePosition,0)
you don't need to save instance manually, just make sure that all views has an unique id in xml layout and android will do it for you.
You could use this method
mAdapter.notifyItemRangeChanged(position, list.size());
At the time you add new elements in adapter's list use this method to notify you adapter. it will retain the current position and add all new elements below the last item.
mAdapter.notifyDataSetChanged();
will notify your entire list ,which will make your list to scroll to top position so use
notifyItemChanged(int position) which will update only that position
and if you want to notify some range then use notifyItemRangeChanged(position,size)
for details refer this link for more detail. RecyclerView.Adapter to notify
Override onAttachedToRecyclerView to get the RecyclerView reference in your adaptor class
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
}
Override onViewRecycled to get the lastVisiblePosition which you can use when you will again reload your recycler view
#Override
public void onViewRecycled(SiteHolder viewHolder) {
lastVisiblePosition = ((GridLayoutManager) mRecyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
}
Where ever you are doing your reloading of data(as a setter of Arraylist or whatever way)
first call
notifyDataSetChanged();
and then
((GridLayoutManager) mRecyclerView.getLayoutManager()).scrollToPosition(lastVisiblePosition);
This will make sure data reload to happen first then scrolling
I'm making a button that when i click on him the visibility of a checkbox withing a listView will change. however it appears that the code run as expected but the visibailty is not udpateing. is there a way to update the item's visabilty?
mButtonEdit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for(int i = 0 ; i<calanders.size();i++){
View view = mListView.getAdapter().getView(i,null,mListView);
if(mButtonEdit.isSelected()){
print("button is selected");
CheckBox checkBox = view.findViewById(R.id.clockproperties_checkBox);
checkBox.setVisibility(View.GONE);
}else{
print("button is not selected");
CheckBox checkBox = view.findViewById(R.id.clockproperties_checkBox);
checkBox.setVisibility(View.VISIBLE);
}
}
mListView.getAdapter().
if(mButtonEdit.isSelected()){
mButtonEdit.setSelected(false);
}else{
mButtonEdit.setSelected(true);
}
}
});
You should never call the ListAdapter's getView() method. It is only supposed to be called by the system when scrolling though the ListView. Instead you need to update the list by calling mListView.getAdapter().notifyDataSetChanged().
Add a boolean field in the adapter and update its value when the button is clicked.
You can create a model/data class based on your data and keep a Boolean variable for checkbox visibility. So default make it false and on button click get the position of list-view item and update Boolean variable to true, and do adapter.notifyDataSetChanged().
You can also try with :
((YourAdapter) yourListView.getAdapter()).notifyDataSetChanged();
I made a list that's loaded with Contact friends and the user can select them by tapping on them. If a person is selected, the listitem's backgorund changes colour, if deselected, the bg colouring goes away.
Problem is, when I call my method on an OnClickListener, it's fine.
When I however call it in a loop to colour already selected friends (e.g. when revisiting the list), it doesn't do the colouring.
The loop that goes through the elements to call colorize if needed:
for (int i = 0; i < adapter.getCount();i++){
ContactFriend cf = (ContactFriend) adapter.getItem(i);
View v = getViewByPosition(i,listView);
colorizeFriendBg(v, cf);
adapter.notifyDataSetChanged();
}
note I do the exact same in the listener and it works fine there.
And the colorizer:
private void colorizeFriendBg(View v, ContactFriend friend){
if(friend.isSelected()){
v.setBackgroundColor(0x993399ff);
}else{
v.setBackgroundColor(0x00000000);
}
v.invalidate();
}
This issue is quite strange and I have no idea what to do in order to make it right. The whole bunch is called from onActivityCreated, if that matters.
Edit:
I debugged it of course and the code runs and should change the colour, not running isn't the issue.
Edit again:
here's the listener implementation:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ContactFriend fr = (ContactFriend) adapter.getItem(position);
addToSelected(fr);
//TODO: make it switch some BG colour when clicked. use getViewByPosition.
View v = getViewByPosition(position,listView);
colorizeFriendBg(v,fr);
adapter.notifyDataSetChanged();
}
});
what type of item View are you getting from the Adapter?
that View could / should implement colorize() and color itself;
for example: v.colorize(contact.isSelected()) to switch colors.
or with Android Data-Binding XML (where the viewModel is an instance of Contact):
<data class="com.acme.databinding.ContactViewHolderBinding">
<variable name="viewModel" type="com.acme.model.Contact"/>
</data>
...
android:backgroundColor="#{viewModel.isSelected ? R.color.MAGENTA : R.color.BLACK}"
class Contact just would require a getter and a setter for property isSelected.
one actually can also bind event handlers, which would be an alternate approach.
You need to call invalidate() on your view to make the color changes visible.
invalidate() forces a redraw with the new colors.
This is my problem:
https://youtu.be/k-N5uthYhYw
and this is my onBindViewHolder() method.
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.specName.setText(specList.get(position).getSpecName());
// Assign a tag number to later identify what radio-button
holder.specRadioBtn.setTag(new Integer(position));
/* Event listenr for longClick - we prob. won't use it, but it's here just in case */
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Toast.makeText(context, "Long press", Toast.LENGTH_SHORT).show();
return false;
}
});
/* Little hack to select its Radio Button when a specific row is tapped */
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Turn rowSelectedFlag to true since the user selected this row
rowSelectedFlag = true;
// When the user taps on a row select that row's radio button
holder.specRadioBtn.setChecked(true);
// I'm not sure why, but locally the interface needs to be started by pointing it
// to where it should drive the data (to send the params)
tempInterface = new AdminUserSpecialty();
// Call the interface to send the data (row spec-name and id) back to AdminUserSpecialty
tempInterface.activateSpecSelect(specList.get(position).getSpecName().toString(),
specList.get(position).getSpecId().toString(), rowSelectedFlag);
int clickedPos = ((Integer) holder.specRadioBtn.getTag());
// Check if the radio button is already selected
if (holder.specRadioBtn.isChecked()) {
if (lastCheckedBtn != null) {
// Don't deselect if user taps on the same row several times
if (lastCheckedBtn == holder.specRadioBtn) {
// do nothing
}
// Otherwise do deselect the previously selected radio button
else {
lastCheckedBtn.setChecked(false);
}
}
lastCheckedBtn = holder.specRadioBtn;
lastCheckedPos = clickedPos;
}
// If radio is not checked set the lastCheckedBtn to null (reset counter)
else {
lastCheckedBtn = null;
}
}
});
/* ----------------------------------------------------------------------*/
}
I can't seem to preserve my radio-button selection on RecyclerView scroll. On scroll the selection becomes erratic and random. I understand that one of RecyclerView's features is to recycle rows as they leave the screen, but what do I need to do to keep my selection? Thanks much.
I know that this was answered already but if some of you are still looking for an easier answer and your application does not rely on the RecyclerView view recycling feature much (for example if you have a fixed size list of items...) you can always set your recycler view cache view size. That way it would not recycler your views hence it would not recycler the views and you will avoid copy selected values to another views...
yourRecyclerView..setItemViewCacheSize(yourItemList.size());
Save the checked / unchecked status of the radio button (you should use checkbox instead if you want to allow the user to select multiple items) to your model (i.e. your items in the list should have a field for this) when the onClick event happens. When you bind the ViewHolder, make sure you set checkbox's value to whatever you saved in your model.
It's happen because of the Recycling mechanism
(PS: its the same for the ListView or RecyclerView).
To fix that:
1) Add a booelan variable to your model to save the state of the RadioButton
2) Update your RadioButton state in onBindViewHolder() method from this boolean in the model.
3) Add setOnCheckedChangeListener() to your RadioButton to listen to his state (checked/unchecked) and to update the boolean in your model when the state changes.
I have a ListView and in every single ListviewItem there is an ImageView with a little star (for marking it as favourite). Therefore I put an OnClickListener to the ImageView on every item in the custom ArrayAdapter.
imgStar.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Bitmap bitmap = ((BitmapDrawable)imgStar.getDrawable()).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable)(context.getResources().getDrawable(R.drawable.ic_action_not_important))).getBitmap();
if(bitmap != bitmap2) {
imgStar.setImageResource(R.drawable.ic_action_not_important);
} else {
imgStar.setImageResource(R.drawable.ic_action_important);
}
}
});
The problem: When I get some items and click for example on the star of the first item, the image changes as it should but a few items lower the image changes too o.O
I tested it with some code: The thing that won't get into my head is it is only changing the image (on the other item below), code that would be executed in the onclick is only executed for the item I really click not for the one where the image changes too.
Why does the image of a random other item in the list change also? I hope someone can help me.
Custom Adapter Constructore Code
public LinkArrayAdapter(Context con, int textViewResourceId) {
super(con, textViewResourceId);
context = con;
}
The main problem is that you can't change the image of items in the onClick then leave it and hope it will be updated on every item on the list. Because onClick get called in different time than getView. So you must set item images outside of onClick but in the getView so every time that getView called for a specific item it will set the appropriate image for that item.
Define a boolean array in your CustomAdapter class as:
private boolean[] stars;
Then in constructor method of your class, initialize it as:
this.stars = new boolean[items.size()];
In the onClick method:
// **Edited to apply image update at click**
stars[position] = !stars[position];
notifyDataSetInvalidated();
At last in the getView() method of custom adapter
(ensure this code is not in any other inner blocks):
if (stars[position])
imgStar.setImageResource(R.drawable.ic_action_important);
else
imgStar.setImageResource(R.drawable.ic_action_not_important);
private int selectedPositions ;
// /... your code.
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
OneComment mComment = mlist.get(position);
mComment.isStart = !mComment.isStart;
if(mComment.isStar){
//set star image
} else{
do not set star image}
}
});
#semsamot's answer works, however
notifyDataSetInvalidated() causes the List to reload and goes to the first item.
Use notifyDataSetChanged() instead.