I have a list view with a custom adapter. Each item has a delete icon which prompts a delete dialog fragment. on deleting the item, I am performing a slide animation and on animation end. the item is deleted from the list and adapter is notified about the deletion like below:
// dialog fragment on clicking "delete"
positive.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final FoldingCell fView = (FoldingCell) thisItem.getParent().getParent();
// wait for fold to finish then delete item
fView.postDelayed(new Runnable() {
#Override
public void run() {
deleteCell(fView, pos);
}
}, 850);
my adapter is of type FoldingCell so I am folding back the cell before deletion, hence the postDelayed. The deleteCell is the simple animation below:
private void deleteCell(final View v, final int index) {
TranslateAnimation transanim = new TranslateAnimation(0, 800, 0, 0);
transanim.setDuration(700);
transanim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
items.remove(index);
// update array adapter
adapter.notifyDataSetChanged();
v.setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
v.startAnimation(transanim);
}
An important not is that I also am using stableIds which might be causing the problem. After the animation deletion occurs, the adapter is deleting two items instead of just the one clicked on. when I disable the animation in the above code, and just write
items.remove(index);
adapter.notifyDataSetChanged();
exactly on delete, the deletion works perfectly even with stable ids (there is just no animation) why is that? The problem seems like its a combination of the animation and stable ids, since if stableids is false, the deletion works with the animation.
I cannot find a good solution for this other than making stable ids false and solve other issues that arise from doing that.
Solved! The reason why with stableids the adapter was deleting an extra fields was that when I override getItemId, I was returning the adapter position itself which, I guess, during the animation when an item is deleted, that same position id is taken by another list item which gets deleted as well. By returning a different itemId that is unique to the list item, this error does not occur. so I changed this:
#Override
public long getItemId(int position) {
return position;
}
to this:
#Override
public long getItemId(int position) {
Item item = items(position);
return item.getId();
}
where items is the arraylist I am passing to the adapter. Item is my class that holds the elements/views of each item and getId() is the getter I have to return the id integer of each Item
Related
I have a list of elements in recycler view, my need is to hide some elements by clicking a specific element of the same list. For instance, my list contains 10 elements and I want to hide all the elements from position 6 by clicking the element in the 5th position. how can I do that?
You have to remove that element from dataset and call notifyOnDataSetChanged Method, otherwise you can create a model class with boolean/hide show flag and then on item click listener you can set flag accordingly to remove/hide element.
Recyclerview react to notifyDatasetChanged method to redraw each visible row.
try to change behind model of recycler view then notifyDatasetChanged
bindViewHolder(VH holder, int position){
view.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
// Do you business to change data model in other position which
//identified whether the view must be visible or not
notifyDataSetChanged();
}
});
}
Just try this...
public void onBindViewHolder(final ViewHolder viewHolder,
final int position) {
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// set your logic like this..
if(position==5){
if(list.size()>6){
list.remove(6); // here list will be your data list.
notifyDataSetChanged();
}
}
}
});
I got a RecyclerView and want to change the appearance of any clicked row. For that I have a callbackFunction in my Activity which I pass to the Adapter, which then is called inside the Adapter, as soon as I click on any row in the RecyclerView.
The clicked row is then changed, but it happens, that not only the clicked rows are changed but also other rows, that weren't clicked and were never clicked before. I checked the ArrayList that contains the data, but everything is fine there. Only the clicked elements contain the trigger to change the appearance of the row.
What is causing the other rows to change, although they have not been clicked?
Interface inside activity for callback
public interface onHeaderClickListener{
void onHeaderClicked(int index);
}
Inside RecyclerView Adapter
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof ViewHolderHeader){
((ViewHolderHeader)holder).dateHeaderTextView.setText( Integer.toString(((objClass_offerDateHeader) arrayList.get(position)).getDate()));
if(((objClass_offerDateHeader) arrayList.get(position)).isSelected()){
((ViewHolderHeader)holder).dateHeaderTextView.setBackgroundColor(Color.parseColor("#b642f4"));
}
((ViewHolderHeader)holder).dateHeaderTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onHeaderClickListener.onHeaderClicked(position);
}
});
}
}
Adapter initialisation inside activity
customAdapterRecyclerViewAddOffersTo = new customAdapterRecyclerViewAddOffers(offerArrayList,"dragTo", new onHeaderClickListener() {
#Override
public void onHeaderClicked(int index) {
if (offerArrayList.get(index) instanceof objClass_offerDateHeader){
if(((objClass_offerDateHeader) offerArrayList.get(index)).isSelected()){
((objClass_offerDateHeader) offerArrayList.get(index)).setSelected(false);
}
else {
((objClass_offerDateHeader) offerArrayList.get(index)).setSelected(true);
}
customAdapterRecyclerViewAddOffersTo.notifyDataSetChanged();
}
}
});
In your onBindViewHolder method you have to set the background of the unselected cell, keep in mind the the cells are reused and you only set the background of selected cells so when it is reused the background is not returned to the normal color
So in code you will have to add an else condition
if(((objClass_offerDateHeader) arrayList.get(position)).isSelected()){
((ViewHolderHeader)holder).dateHeaderTextView.setBackgroundColor(Color.parseColor("#b642f4"));
} else {
((ViewHolderHeader)holder).dateHeaderTextView.setBackgroundColor(Color.parseColor("#FFFFFF")); // I assume you need it to be white you can change it to any other color
}
You need to add an else condition here:
if(((objClass_offerDateHeader) arrayList.get(position)).isSelected()){
((ViewHolderHeader)holder).dateHeaderTextView.setBackgroundColor(Color.parseColor("#b642f4"));
}
Viewholders get recycled, so you cannot be sure of the current state when onBindViewHolder is called.
I have a recyclerView and have implemented onLongClick listener on the items. The Adapter is in the same class as the Activity.
I created a set: public Set<Integer> multiplePositions as instance variable of the Activity class and am initialising it in onCreate() method as multiplePositions = new TreeSet<>().Coming to my Adapter class in the onBindViewHolder method I created a click listener as follows:
holder.textCardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Adapter.this.onLongClick(holder, position);
return true;
}
});
holder.textCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
As you can see I am calling the method onLongClick. My onLongClick method looks like this:
public void onLongClick(ViewHolder holder, int position) {
multiplePositions.add(position);
for(Integer i : multiplePositions) {
Log.e("set", ""+i);
}
holder.textCardView.setBackgroundResource(R.color.long_press);
}
Here, whenever an item clicked I am adding the position in the Set but I don't know how do I iterate through this set and set the background color of all the item at the positions in the set.
I am aware that this can be achieved by creating a boolean variable isSelected in the model class but if I go with this method then other functionalities of my code won't work.
My main concern is if I scroll the recyclerView up or down then color of the selected positions should not disappear. The unselection part will be done in setOnClickListener().
Use SparseBoolean array to store the positions of the items clicked in the reyclerview.Check the answer in this link
https://stackoverflow.com/questions/46336645/hilighted-selected-items-colour-changes-when-not-in-view-in-recyclerview/46336844#46336844
Check here the purpose of using SparseBoolean array
https://stackoverflow.com/questions/18822708/what-is-the-clear-purpose-of-sparsebooleanarray-i-referred-official-android
EDIT:
Use SparseBooleanArray to keep track of selected items in recycler view adapter
Initialize the SparseBooleanArray as private memeber variable
private SparseBooleanArray mClickedItems=new SparseBooleanArray();
Inside your click function while clicking textCardView make the item position as true.
holder.textCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mClickedItems.put(position,true);
notifyDataSetChanged();
}
});
Then in the onBindViewHolder check if the position is already selected or not like this
if(mClickedItems.get(position)==true){
//Show selected color
holder.textCardView.setBackgroundResource(ContextCompat.getColor(holder.textCardView.getContext(),R.drawable.selected));
}else {
//show unselected color
holder.textCardView.setBackgroundResource(ContextCompat.getColor(holder.textCardView.getContext(),R.drawable.unselected));
}
I am a beginner level android developer and was working on creating a basic To-Do list app. The app contains custom listview with a coloured circle to show priority, the title and a checkbox to check and delete the item when the user has done it. The app stores these to-dos using SQLite database and uses Loader to retrieve data.
What I'm trying is to delete the item from the list as soon as the checkbox is checked.
I tried to implement deletion task in the Adapter class like this (Also tried to animate deletion.):
CheckBox itemCheck = (CheckBox)view.findViewById(R.id.todo_checked);
itemCheck.setChecked(false);
itemCheck.setTag(priority);
itemCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
int idIndex = cursor.getColumnIndex(TodoTable.ID);
final int id = cursor.getInt(idIndex);
final Uri uriTodDelete = ContentUris.withAppendedId(TodoTable.TABLE_URI,id);
Animation slideOut = AnimationUtils.loadAnimation(context,android.R.anim.slide_out_right);
slideOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
context.getContentResolver().delete(uriTodDelete,null,null);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(slideOut);
The problem is, whenever I delete the top item of the listview, the item just below it gets deleted. But it works fine if I delete from the bottom.
How do I fix this? Any help will be appreciated.
P.S the delete method of the Content Resolver class is handling notifyDataSetChanged() method.
A wild guess, you are not moving the cursor to the relevant row
so cursor is on pos x but you may click -on listview- on item at pos y.
so you need to move cursor to the relevant position before you try to get the ID value.
i assume you are doing this code in getView() ? as you are usign an adapter.
You need to make use of position parameter passed in getView() as below:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
:
:
if(isChecked){
cursor.moveToPosition(position);
int idIndex = cursor.getColumnIndex(TodoTable.ID);
final int id = cursor.getInt(idIndex);
:
:
}
:
:
}
you also need to make sure the cursor is being re query otherwise you may get some problems with positions once you remove items from list
-- i think this is auto handled if you are using a cursor adapter?
just double check on that.
I want to do the next animation:
When I click on an item of my RecyclerView the items before disappear with a cascade animation.
For example: I Click on my item placed at position 10, then the items from 0 to 9 have to disappear applying a cascade swipe-left animation.
How can do it?
At this moment I tried to apply like this:
ViewHolder methods:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
swipeAnimation(position);
}
});
lstViewsHolder.add(holder.itemView);
setAnimation(holder.itemView, position);
}
private void swipeAnimation(int position){
int init = 0;
while(init < position){
lstViewsHolder.get(init).startAnimation(AnimationUtils.loadAnimation(context, R.anim.swipe_left));
init++;
}
}
The correct way of doing this is using a custom ItemAnimator. You should try to extend either SimpleItemAnimator or DefaultItemAnimator to apply your animation.
The ItemAnimators respond to data events from the adapter, so you should include logic in your adapter that calls notifyItemRemoved()or notifyItemRangeRemoved() so that the proper animation is triggered.
I think you should iterate over you're array and each time remove that item and then call notifyItemRemoved, you may want to delay each operation so that you're animation look smooth.
mResults.remove(position);
adapter.notifyItemRemoved(position);