Deleting item in recyclerView does not reflect change in next Activity - java

I'am trying to delete a card having in recyclerview. The problem is when i click the delete button in my card the changes does not reflect the next activity. When i click Delete Button the textview and the imageview must change accordingly. But that is not happening.
this is my onclick in viewHolder. I have used Interface. :
public void onClick(View view)
{
//get data from array list
if(view.getId()==R.id.cont_root){
if(itemClickCallback!=null) {
itemClickCallback.onRootClick(getAdapterPosition());
}
}
else{
delete(getAdapterPosition());
}
}
}
This is my delete Method in the adapter :
public void delete( int position){
listData.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount()-1);
}
This is my code when a Cardview is clicked .Card's name is Root.
public void onRootClick(int p) {
//adapter.setItemClickCallback(this);
adapter.notifyItemChanged(p);
ListItem item =(ListItem)listData.get(p);
Intent i = new Intent(this,SecondActivity.class);
Bundle extras=new Bundle();
extras.putString(EXTRA_QUOTE, item.getTitle());
extras.putInt(EXTRA_ATTR,item.getImageResID());
i.putExtra(BUNDLE_EXTRAS,extras);
startActivity(i);
}
Note : i have not used interface method for deleting the element.
Please help. Iam new to Recyclerview. :(

You have to use the Methodes which relate to "what user is seeing" I can call them (Layout side Methods) and they are :
getLayoutPosition()
findViewHolderForLayoutPosition(int)
The first one returns the view position in accordance to the Layout it self and not the adapter
and the second one returns the ViewHolder for the item in the given position of the data set
In your code you are using what i can call (Adapter side Method)
getAdapterPosition()
any changes using The (adapter side Methods) will not be reflected to Layout untill you use (the Layout side Methods)
Hope This helps

Related

How to make the holder in if else statements of onClick method work without final in argument Viewholder in arguments of onBindViewHolder method

I am trying to make a text change when a button located along with the text (layoutPasswd) in recycler view and to change it back if the button is again pressed.Like a password hiding button. The values to the adapter is from a static class object as arraylist. The problem occurring now is that the value for all the items (only for layoutPasswd) in recycler view is same.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
holder.layoutUName.setText(users.get(position).getUserName());
pos = position;
holder.layoutPasswd.setText("********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (holder.view1) {
holder.layoutPasswd.setText(users.get(pos).getPasswd());
holder.btnViewChanger.setText("hide");
holder.view1 = false;
} else {
holder.layoutPasswd.setText("********");;
holder.btnViewChanger.setText("Show");
holder.view1 = true;
}
}
});
You cannot rely on the ViewHolders or Views in a RecyclerView to hold any state, because they are recycled. Every time a view scrolls onto the screen, first it calls your onBindViewHolder function to update the contents of that ViewHolder to match the data.
Any configuration you set on the views or the ViewHolder instance in onBindViewHolder cannot be relied on to stay the same if the view scrolls off the screen, because the original ViewHolder might be recycled to be used for some other data, and when it scrolls back on screen, you might be looking at some other view that has been recycled from other data that just scrolled off the screen.
So if your views have configuration that you want to "stick", you have to back it up when you change it, and restore it in onBindViewHolder. The way you accomplish this will depend on how you are managing the data that you pass to the adapter.
If you can modify your User class, you can add a Boolean to it that stores whether it should show the password. Then in your onBindViewHolder, you restore the state based on this Boolean. And you also update this Boolean when the state changes.
I also updated the way the click listener works to simplify it for toggling. I removed the pos = position line, because almost certainly that is not something you should be doing.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
final User user = users.get(position)
holder.layoutUName.setText(user.getUserName());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
user.setShowPassword(!user.isShowPassword());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setText(user.isShowPassword() ? "hide" : "show");
}
});
// ...
}
If you cannot modify the User class, this is more complicated. Then the adapter should have its own ArrayList<Boolean> to store the state by position index, but you need to keep this list at least as long as the data that is bound, and reset everything to false if the whole list of data is refreshed.

Updating comment count for a post in recyclerview

Activity A has a recycler view, which has each row as a post, now each post has a comment count textview which launches a comment activity where user can see comments and also add comments.
Now question is, after adding a comment how do I update the comment count in that particular row of recycler view in Activity A when comment Activity finishes or pressing back button to get back on posts page. But I don't want to reload the recycler view again as it will distract the user from the current location.
Please guide me in the right direction. Below is my Recyclerview adapter code snippet.
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
int viewType = getItemViewType(position);
final Posts posts = postsList.get(position);
final viewHolderPost viewHolderPost = (viewHolderPost) viewHolder;
viewHolderPost.commentcount_wrap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), CommentViewerActivity.class);
intent.putExtra("post_id", posts.getPost_id());
v.getContext().startActivity(intent);
}
});
}
Okay here is what I would do:
Create common object (preferably Repository), where I would store list of Posts with Comments in memory.
When clicking on Post, I would call startActivityForResult method from PostActivity to open CommentActivity
After adding/deleting/editing the comment in CommentActivity I would update the list in Repository and finish activity with proper result
In PostActivity I would override onActivityResult method to check if the CommentActivity finished with result that will tell me to refresh list in RecyclerView
If so, I would get the updated list from repository in PostActivity and update it in Adapter
You can use startActivityForResult instead of startActivity
Ref: https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int,%20android.os.Bundle)
So when the CommentActivity is finish, the previous activity will receive a callback that is the result of CommentActivity, you will handle that result and update your recyclerview's adapter.
Hope that help. :)
You could notify the Adapter that there is a change at the particular position.
check: adapter.notifyItemChanged(1)
-edit-
On your Activity A add this.
override fun onResume() {
super.onResume()
if(lastPosition != -1)
recycler_test.adapter?.notifyItemChanged(lastPosition)
}
And your adapter should provide a callback like this.
recycler_test.adapter = CustomAdapter(Controller.shared.myList){ positionClicked ->
//This block of code will be called every time a user clicked a comment.
lastPosition = positionClicked
val intent = Intent(applicationContext, ActivityB::class.java)
intent.putExtra("position", positionClicked)
startActivity(intent)
}

Removing item from list in the OnBindViewHolder method for RecyclerView Android

Ok so it is a bit complicated, I have a custom RecyclerView Adapter and in the OnBindViewHolder method I would like to remove the current item from the recyclerview depending on some different variables but when I remove the item from the ArrayList and call notifyDataSetChanged(); I get :
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling.
How can I remove the item from the RecyclerView in the onBindViewHolder ? , I do not want to do it before setting the adapter because each item in the recyclerview has a sublist and I want to remove the item if the sublist is empty. Thanks for any ideas and sorry for bad english.
Please follow the method used below as shown in this answer.
private List<DetectedIssue> issues = new ArrayList<DetectedIssue>();
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
issues.remove(position);
notifyItemRemoved(position);
//this line below gives you the animation and also updates the
//list items after the deleted item
notifyItemRangeChanged(position, getItemCount());
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
#Override
public int getItemCount() {
return issues.size();
}
PS: As can be seen from the documentation it is bad practice to use notifyDataSetChanged() if only an item is added, moved or removed as unnecessary processes are run.
This event does not specify what about the data set has changed, forcing any observers to assume that all existing items and structure may no longer be valid. LayoutManagers will be forced to fully rebind and relayout all visible views.
If you want the underlying arraylist in some activity be changed then you will have to use an interface to communicate with the activity to change the arraylist.
I know its a bit late (4 years, heh) but I'm in the same solution. As the error states it is actually impossible to call methods such as adapter.notifyItemRemoved(position); or notifyItemRangeChanged(position, getItemCount()); and even the more intensive notifyDataSetChanged(); from within the onBindViewHolder method unless it is within an onClickListener whereby it will be called after the layout is built and not during the build.
A simple workaround for me was to call datalist.remove(position); so if the layout is rebuilt the view is omitted, then simply call holder.itemView.setVisibility(View.GONE);. It's not as clean, but it gets the job done for views you wish to hide.

How to get child views of listview item from outside the adapter

I want to thank you in advance for the solution to my question.
I have a custom adapter for my listview. the list item has an imageView(1), a progressBar(3), and a download button(2).
When the download button for each list item is clicked, I grab vital details of the listitem such as the position of the view, the resourceid of the button, the resource id of imageview and the resource id of progress bar, then i make an parcealable object of class "Download".
Download download = new Download();
download.setUrl(url);
download.setButtonResource(this.downloadBt.getId());
download.setCreativeWork(creativeWork);
download.setDownloadBt(this.downloadBt);
download.setProgressBarResource(this.progressBar.getId());
download.setProgressBar(this.progressBar);
download.setContext(activity);
download.setViewResource(view.getId());
Intent intent = new Intent(activity, BroadcastService.class);
intent.putExtra("download", download);
Log.e("download button", url);
activity.startService(intent);
I start a service which does the download and reports back using a broadcast. When this broadcast message is received I want to update the progress bar.
My problem is how to get the progress bar concerned from my mainactivity which receives the message. How do I reference this?
I am currently using a parcelable object to pass the resource id for the progressbar, all the way from the adapter to the service, then to the receiving activity(mainActivity) using the intent's putExtra(), then I do this at the mainActivity
Download download = (Download)intent.getParcelableExtra("download");
ProgressBar progressBar = (ProgressBar)findViewById(download.getProgressBarResource());`
The issue here is that always only returns the first item of the list, regardless of which listitem is clicked. I would want to get each unique listitem and update the progress bar and the download button. Thanks
I am assuming that you show this layout in a ListView or RecyclerView. First thing that need to do is, add a field for index in your Download class to identify the position in the list from where that button was pressed. Next, pass the index from your getView() or onBindViewHolder() to the function that creates the Download object and add that index to the object.
When you receive the when you broadcast any update from your Service, do include the index value. Now, in your code in your activity where you receive the broadcast value, extract the value of index and the progress. Now, you could write something like this to update the progress of that view in the list:
int index = ... //some value you received in broadcast
int progress = ...//some value you received in broadcast
View view = listView.getChildAt(index);
if (view != null) {
ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
if (progressBar != null) {
progressBar.setProgress(progress);
}
}
This can be one of the solution to your problem
First You need to pass progress value to the adapter that may be zero in start.
Implement setOnItemClickListener for your listview
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
//now point to your item position and do your stuff here
}
});
When ever you receive broadcast for download progress. Just update your ListView by Nullifying previous items with new items containing download progress values.
Not sure if this is what you looking for...
In you adapter. do below.. (I take a sample from my project which do a delete card from the list, when the delete button is clicked)
In getView function inside adapter.
Card card = list.get(position);
viewHolder.imgDelete.setTag(card);// --> i set the whole card info to tag
outside the adapter.
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
int id = v.getId();
if (id == R.id.iv_delete_card) {
//System.out.println("---> Card: " + tag);
//delete card
Object tag = v.getTag();
if (tag != null && tag instanceof Card) {
promptDeleteCard((Card) tag);
}
}
}

Remove a Card Using an Event

I am using cardslib by gabrielemariotti. I am using an expandable ListView Card. I have a custom layout inside it, and on of the views inside the ListView Card is an image view, name remove. I am planning to use the Remove imageview as a trigger to dynamically remove a card from the list.
The problem is, there are two layout involved on a Card, the header and the main content. The Cards by the way are contained inside the ListView. One of the layout, is the remove imageview is located. The code below can remove a Card by clicking it. CardListView
CardHeader header = new CardHeader(getActivity());
//Set the header title
header.setTitle(titleHeader);
//Set visible the expand/collapse button
header.setButtonExpandVisible(true);
header.setOtherButtonClickListener(new CardHeader.OnClickCardHeaderOtherButtonListener() {
#Override
public void onButtonItemClick(Card card, View view) {
Toast.makeText(getActivity(), "Drawable", Toast.LENGTH_SHORT).show();
if(mCardArrayAdapter!=null){
mCardArrayAdapter.remove(card); //It is an example.
}
}
});
The code below will trigger a Toast when the imageview Remove is clicked,
#Override
public void setupInnerViewElements(ViewGroup parent,View view){
//Add simple title to header
if (view!=null){
TextView mTitleView=(TextView) view.findViewById(R.id.card_header_inner_simple_title);
if (mTitleView!=null)
mTitleView.setText(mTitle);
ImageView img = (ImageView)view.findViewById(R.id.cancel);
img.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(getContext(), "Clicked", Toast.LENGTH_SHORT).show();
}});
}
}
I wanted to combine the two. The library
Cardslib works like first you add all your cards to one cards array and then you initalize your CardArrayAdapter with your cards array.
It's same as using ListView. So deleting one item is also same as delete from your ListView.
1) On any event remove the card from cards array.
2) Call notifyDataSetChanged(); on your CardArrayAdapter object. It will update the Cards list and remove the deleted card from the view as well.

Categories