How to add dynamic views to android layout in recyclerview adapter - java

enter image description here
I have a list of parents that each has zero or many children
I use recyclerview to show the parents.
I want to show parents as header of each recyclerview item and below show all the children
what is the best way to show the children?
is it suitable to show children in a listview or add dynamic views for each child if so how to do that?
I have attach the image of layout, please check what I mean
thanks
enter image description here

I think, may this helpful bcz, in every position there are dynamicly generate item view respectably.
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
IAFTbind.rcvFilters.setLayoutManager(linearLayoutManager);
IAFTbind.rcvFilters.setAdapter(new RecyclerView.Adapter() {
public int checkedPosition = 0;
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ImageView imageView = new ImageView(getContext());
imageView.setScaleX(.9f);
imageView.setScaleY(.9f);
imageView.setLayoutParams(new ViewGroup.LayoutParams(size, size));
return new RecyclerView.ViewHolder(imageView) {
#Override
public String toString() {
return super.toString();
}
};
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int p = holder.getAdapterPosition();
((ImageView) holder.itemView).setBackground(new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(fBits.get(p), size, size, false)));
// ((ImageView) holder.itemView).setBackground(new BitmapDrawable(getResources(), bitmaps.get(position)));
int cp = this.checkedPosition;
if (cp == -1) {
((ImageView) holder.itemView).setImageResource(0);
} else if (cp == p) {
((ImageView) holder.itemView).setImageResource(R.drawable.draw_tool_selecter_strock);
} else {
((ImageView) holder.itemView).setImageResource(0);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((ImageView) holder.itemView).setImageResource(R.drawable.draw_tool_selecter_strock);
checkedPosition = p;
notifyDataSetChanged();
changeBitmapEffect(p);
}
});
}
#Override
public int getItemCount() {
return fBits.size();
}
});

Problem statement
You want to add childs dynamically as per the screenshot you shared.
Solution
So here I can see 2 recycler views, one inside another. The first recycler view is for parents which have 2 items
Parents name
Another recycler view for children
What you need to do is, you need to pass the updated list of item to the parent's recyclerview adapter, which will send the updated list of item to childs recyclerview adapter and the childs recyclerview adapter will check using the diffUtil about any changes and it will reflect the changes in the recyclerview.

Related

RecyclerView items in ArrayList

I am working on an android project and I am putting some TextViews inside a RecyclerView and at the same time I am trying to put those things in an array list as ViewHolder type. After some tests on the program i understood that the items that are inserted in the ArrayList are only the items that are shown in the screen. For example if my screen fits 15 textviews and i put 30 textviews inside the recycler view and arraylist, the size of the arraylist will be only 15 so i can't make any changes to the rest of the items.
Also when i scroll down the recycler view the arraylist get a size of the items that has been shown while scrolling but when i scroll back to the top and try to change the number of the TextViews and make them less the program crashes.
What i want is to have all of the items that have been added to the recycler view also in the arraylist in order to can use them.
Recycler View class code:
public class Tab1Child1Numbers extends RecyclerView.Adapter<Tab1Child1Numbers.ViewHolder> {
ArrayList<Integer> textFront;
ArrayList<Integer> textBack;
ArrayList<Integer> colors;
Context context;
ArrayList<ViewHolder> texts = new ArrayList<>();
public Tab1Child1Numbers(Context context, ArrayList<Integer> textFront, ArrayList<Integer> textBack, ArrayList<Integer> colors) {
super();
this.context = context;
this.textFront = textFront;
this.textBack = textBack;
this.colors = colors;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.tab1_child1_numbers, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
viewHolder.textFront.setText(textFront.get(i)+"");
viewHolder.textBack.setText(textBack.get(i)+"");
viewHolder.textFront.setBackgroundColor(colors.get(i));
viewHolder.textBack.setBackgroundColor(colors.get(i));
texts.add(viewHolder);
// cardsFront.add(viewHolder.imageFront);
// cardsBack.add(viewHolder.imgThumbnail);
viewHolder.setClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
if (isLongClick) {
} else {
Deck deck = new Deck();
deck.flipCard(texts.get(position).frame, texts.get(position).textFront, texts.get(position).textBack);
}
}
});
}
#Override
public int getItemCount() {
return textFront.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public TextView textFront;
public TextView textBack;
public FrameLayout frame;
public LinearLayout layout;
private ItemClickListener clickListener;
public ViewHolder(View itemView) {
super(itemView);
textFront = (TextView) itemView.findViewById(R.id.txt1);
textBack = (TextView) itemView.findViewById(R.id.txt2);
frame = (FrameLayout) itemView.findViewById(R.id.list_frame);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
public void setClickListener(ItemClickListener itemClickListener) {
this.clickListener = itemClickListener;
}
#Override
public void onClick(View view) {
clickListener.onClick(view, getPosition(), false);
}
#Override
public boolean onLongClick(View view) {
clickListener.onClick(view, getPosition(), true);
return true;
}
}
class Deck {
private void flipCard(View rootLayout, View cardFace, View cardBack) {
FlipAnimation flipAnimation = new FlipAnimation(cardFace, cardBack);
if (cardFace.getVisibility() == View.GONE) {
flipAnimation.reverse();
}
rootLayout.startAnimation(flipAnimation);
}
public void flipAll(){
System.out.println(texts.size()+"--------");
randomize();
for (int i = 0; i < texts.size(); i++) {
flipCard(texts.get(i).frame, texts.get(i).textFront, texts.get(i).textBack);
}
}
private void randomize(){
for (int i=0; i<texts.size(); i++){
if (texts.get(i).textFront.getVisibility() == View.GONE) {
texts.get(i).textFront.setText(textFront.get(i) + "");
texts.get(i).textFront.setBackgroundColor(colors.get(i));
}
else {
texts.get(i).textBack.setText(textBack.get(i) + "");
texts.get(i).textBack.setBackgroundColor(colors.get(i));
}
}
}
}
It looks like you're trying to do something that you shouldn't.
onBindViewHolder() is called by the system whenever it is about to display a ViewHolder to the user. So it will initially be called once for every view on the screen, plus once for a small number of views just off-screen (so that they can scroll on screen nicely).
However, this means it will also be called repeatedly as the user scrolls through the data in your adapter. In these cases, the ViewHolder being passed to onBindViewHolder() might have been recycled, meaning it might previously have been used to display different data and is now being re-used to display new data.
Let's say you have one million items in your data set, but your layout is set up in such a way that only 15 items are visible on screen at any one time. Chances are good that the system will wind up creating about 20 ViewHolder instances (15 on the screen, 5 available for re-use just off-screen). These same 20 ViewHolders will be used to display all one million items if the user scrolls enough. They'll just keep being re-used over and over.
That's why this line is problematic:
texts.add(viewHolder);
You're going to wind up building a list with potentially millions of objects in it, with the same 20 ViewHolders appearing multiple times in your list.
It would be much better to think about storing your data in a different way, rather than trying to store ViewHolders passed to onBindViewHolder().

Search listview but instead of filtering, just inflate and display the valid items

I have a arraylist of buttons (reserveButtons) that I can display in a listview. I have made a search function which searches in my database and outputs a list of integers (resultID). They correspond to the indexes of reserveButtons I want to display.
Simply put, I want to do something like this when the search button is clicked:
ArrayAdapter<ReserveButton> adapter = new MyListAdapter();
ListView list = (ListView) myView.findViewById(R.id.resultslist);
list.setAdapter(adapter);
for (int result : resultID) {
adapter.add(reserveButtons.get(result));
}
So, for each result, I want to add the corresponding button to the listview.
Here is the private class MylistAadapter :
private class MyListAdapter extends ArrayAdapter<ReserveButton> {
public MyListAdapter() {
super(getActivity(), R.layout.list_item, reserveButtons);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if(itemView == null) {
itemView = gettActivity().getLayoutInflater().inflate(R.layout.list_item, parent, false);
}
ReserveButton currentButton = reserveButtons.get(position);
//the resultItem is the id of the buttons
Button butt = (Button) itemView.findViewById(R.id.resultItem);
butt.setBackground(currentButton.getImage());
return itemView;
}
}
I know that getView will just display every reserveButton, but I want the code in getView to be executed when I add each button, but the position doesn't change since position = result in the for loop of the first code block.
//This code is inside MyListAdapter
#Override
public void add(ReserveButton object) {
/* What do I write here to inflate a list_item and give it
the background image reserveButton.get(result).getImage() */
super.add(object);
}
How do I override the add method of MyListAdapter so that I can add a reserveButton and change its background image for each result in the resultID list.
If the same thing can be accomplished without the add method, please do tell.
P.S: I do not want to just list every reserveButton and then filter them with the search; I want to display ONLY the buttons that the user is looking for.
I figured it out myself!
Basically, what I did was create a separate ArrayList of ReserveButtons and do the foreach loop like so:
int index = 0;
for (int result : resultID) {
//result is the single ID of an answer
answerButtons.add(index,reserveButtons.get(result));
index ++;
}
populateListView();
So I end up storing ONLY the buttons I want to display in the answerButtons list. And here is what happens in populateListView()
private void populateListView() {
ArrayAdapter<ReserveButton> adapter = new MyListAdapter();
ListView list = (ListView) myView.findViewById(R.id.resultslist);
list.setAdapter(adapter);
}
and the getView() method:
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if(itemView == null) {
itemView = getActivity().getLayoutInflater().inflate(R.layout.list_item, parent, false);
}
//Just set the image of the corresponding answerButton
ReserveButton currentButton = answerButtons.get(position);
Button butt = (Button) itemView.findViewById(R.id.resultItem);
butt.setBackground(currentButton.getImage());
return itemView;
}
Problem solved. I haven't seen any answers to a problem like this, so this post should make it easily google-able for any newcomer who stumbles upon this problem.

Android Recyclerview- increase font size of all inner textviews

this code is in my MainActivity(working good) :
mAdapter = new Adapter_RecyclerViewReader(readerLineObjs);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setLayoutManager(new LinearLayoutManagerWithSmoothScroller(getBaseContext()));
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
recyclerView.setAdapter(mAdapter);
and the recyclerview Adapter:
public class Adapter_RecyclerViewReader extends RecyclerView.Adapter<Adapter_RecyclerViewReader.MyViewHolder> {
private List<ReaderLineObj> readerLineObjList;
float minTextSize = 50;
float maxTextSize = 80;
Adapter_RecyclerViewReader.MyViewHolder holderGlobal;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tvRowHidId, tvRowTitle, tvRowContent;
public MyViewHolder(View view) {
super(view);
tvRowTitle = (TextView) view.findViewById(R.id.tvRowTitle);
tvRowContent = (TextView) view.findViewById(R.id.tvRowContent);
}
}
public void ResizeTextSize(Boolean makeBigger){
}
public Adapter_RecyclerViewReader(List<ReaderLineObj> readerLineObjList) {
this.readerLineObjList = readerLineObjList;
}
#Override
public Adapter_RecyclerViewReader.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_recycler_list_reader, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(Adapter_RecyclerViewReader.MyViewHolder holder, int position) {
ReaderLineObj readerLineObj = readerLineObjList.get(position);
if (readerLineObj.getTitle().length() >0 ){
holder.tvRowTitle.setText(Html.fromHtml(readerLineObj.getTitle()));
}else{
holder.tvRowTitle.setVisibility(View.GONE);
}
holder.tvRowContent.setText(Html.fromHtml(readerLineObj.getContent()));
}
#Override
public int getItemCount() {
return readerLineObjList.size();
}
}
the font size of tvRowTitle and tvRowContentare the same btw.
I'v added 2 Buttons in the MainActivity xml, and i want them to have functinalty
of getting current fontsize of tvRowTitle and then increase/decrease it (Relatively for current fontsize) for all textviews in the recyclerview.
preview
clicing the left button will add 1sp to current font size all over the grid view title & content.
how can i do this?
Points to note:
i) You will be changing value in the Activity class but adapter has to implement the changes you make on the sizes
ii) You will need to reload the views using
mAdapter.notifyDataSetChanged();
method
So what is the work around. You can have a static float value to hold the size, for example, in the MainAcitivity initialized.
static float size_of_items;
then on increment button change the values of float as
size_of_items=size_of_items+1;
do similar thing on the decrement button.
NOTE: On the adapter class make this changes to the onBindViewHolder method adding
holder.tvRowTitle.setTextSize(TypeValue.COMPLEX_UNIT_SP, MainActivity.size_of_items);
holder.tvRowContent.setTextSize(TypeValue.COMPLEX_UNIT_SP, MainActivity.size_of_items);
so that you have your code inside onBindViewHolder as below
if (readerLineObj.getTitle().length() >0 ){
holder.tvRowTitle.setText(Html.fromHtml(readerLineObj.getTitle()));
}else{
holder.tvRowTitle.setVisibility(View.GONE);
}
holder.tvRowContent.setText(Html.fromHtml(readerLineObj.getContent()));
holder.tvRowTitle.setTextSize(TypeValue.COMPLEX_UNIT_SP, MainActivity.size_of_items);
holder.tvRowContent.setTextSize(TypeValue.COMPLEX_UNIT_SP, MainActivity.size_of_items);
FINALLY:
Remember to use the mAdapter.notifyDataSetChanged();after every change in size_of_items so you have such a simple method:
private void increaseTextSize(){
size_of_items=size_of_items+1;
mAdapter.notifyDataSetChanged();
}
All the best, hoping not too late with the answer. You do not need the method i created last, instead you can use the code inside on your increase_size button on the setOnClickListener method. Goodluck

Recyclerview with Section but with a Footer only per section

Good day all,
I have a Recyclerview, where on first load achieves the result I want which is grouping all the Trades a specific seller is selling, by that seller.
Example:
Bob selling a watch.
Bob selling a car.
Bob selling a horse.
Button to remove all Bobs trades
Jim selling a house.
Jim selling a monkey.
Button to remove all Jims trades
ect
Example:
The issue is the minute I start scrolling the recyclerview, the rows of items get mixed up.
The way I did this was, I have a single layout that holds a Vertical Linear Layout with a green button below the LinearLayout.
Now I was programatically inflating the view for each Row, then setting the data for that row.
This is my onBindViewHolder:
#Override
public void onBindViewHolder(final ParentCartResultsViewHolder holder, int position) {
final LinkedHashMap<Long, List<Trade>> mapTradesBySeller = CartUtils.getUserToFixedPriceTradeMap();
mTradesBySeller = (new ArrayList<>(mapTradesBySeller.values())).get(holder.getAdapterPosition());
if (mTradesBySeller != null) {
for (Trade trade : mTradesBySeller) {
View singleTrade = LayoutInflater.from(MyApplication.getAppContext()).inflate(R.layout.item_trade_details_include_row, holder.mLinearLayout, false);
TextView tradeTitle = (TextView) singleTrade.findViewById(R.id.trade_details_include_trade_title);
tradeTitle.setText(trade.getTitle());
TextView endDate = (TextView) singleTrade.findViewById(R.id.trade_details_include_trade_ending_time);
endDate.setText(trade.getUserAlias() + " : " + trade.getUserId());
holder.mLinearLayout.addView(singleTrade);
}
}
}
My onCreateViewHolder:
#Override
public ParentCartResultsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.cart_grouped_by_seller, parent, false);
return new ParentCartResultsViewHolder(v);
}
Now I think I know what the issue is, its just I cant figure out how I can fix it.
The problem is the creating of the views and adding them to the LinearLayout is in the onBindViewHolder, as this runs numerous times
You can use the library SectionedRecyclerViewAdapter to easily group your data into sections and add a footer to each section.
First create a Section class:
class TradeSection extends StatelessSection {
List<String> list;
public TradeSection(List<String> list) {
// call constructor with layout resources for this Section header, footer and items
super(-1, R.layout.section_item, R.layout.section_footer);
// remove header
this.setHasHeader(false);
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size(); // number of items of this section
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
#Override
public RecyclerView.ViewHolder getFooterViewHolder(View view) {
return new MyFooterViewHolder(view);
}
#Override
public void onBindFooterViewHolder(RecyclerView.ViewHolder holder) {
MyFooterViewHolder footerHolder = (MyFooterViewHolder) holder;
// bind your footer view here
footerHolder.tvItem.setText(title);
}
}
Then you set up the RecyclerView with your Sections:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Create your sections with the list of data for each year
TradeSection section1 = new TradeSection(bobDataList);
TradeSection section2 = new TradeSection(jimDataList);
// Add your Sections to the adapter
sectionAdapter.addSection(section1);
sectionAdapter.addSection(section2);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);

How to call bindViewHolder when RecyclerView data changes [duplicate]

I have a RecyclerView with an TextView text box and a cross button ImageView. I have a button outside of the recyclerview that makes the cross button ImageView visible / gone.
I'm looking to remove an item from the recylerview, when that items cross button ImageView is pressed.
My adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {
private ArrayList<String> mDataset;
private static Context sContext;
public MyAdapter(Context context, ArrayList<String> myDataset) {
mDataset = myDataset;
sContext = context;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);
ViewHolder holder = new ViewHolder(v);
holder.mNameTextView.setOnClickListener(MyAdapter.this);
holder.mNameTextView.setOnLongClickListener(MyAdapter.this);
holder.mNameTextView.setTag(holder);
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mNameTextView.setText(mDataset.get(position));
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public void onClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
}
}
#Override
public boolean onLongClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
mDataset.remove(holder.getPosition());
notifyDataSetChanged();
Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
Toast.LENGTH_SHORT).show();
}
return false;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mNumberRowTextView;
public TextView mNameTextView;
public ViewHolder(View v) {
super(v);
mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
}
}
}
My layout is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:id="#+id/layout">
<TextView
android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
android:background="#drawable/greyline"/>
<ImageView
android:id="#+id/crossButton"
android:layout_width="16dp"
android:layout_height="16dp"
android:visibility="gone"
android:layout_marginLeft="50dp"
android:src="#drawable/cross" />
</LinearLayout>
How can I get something like an onClick working for my crossButton ImageView? Is there a better way? Maybe changing the whole item onclick into a remove the item? The recyclerview shows a list of locations that need to be edited. Any technical advice or comments / suggestions on best implementation would be hugely appreciated.
I have done something similar.
In your MyAdapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public CardView mCardView;
public TextView mTextViewTitle;
public TextView mTextViewContent;
public ImageView mImageViewContentPic;
public ImageView imgViewRemoveIcon;
public ViewHolder(View v) {
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
mTextViewContent = (TextView) v.findViewById(R.id.item_content);
mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
//......
imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);
mTextViewContent.setOnClickListener(this);
imgViewRemoveIcon.setOnClickListener(this);
v.setOnClickListener(this);
mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(view, getPosition());
}
return false;
}
});
}
#Override
public void onClick(View v) {
//Log.d("View: ", v.toString());
//Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
if(v.equals(imgViewRemoveIcon)){
removeAt(getPosition());
}else if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getPosition());
}
}
}
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
mDataset.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
}
Edit:
getPosition() is deprecated now, use getAdapterPosition() instead.
first of all, item should be removed from the list!
mDataSet.remove(getAdapterPosition());
then:
notifyItemRemoved(getAdapterPosition());
notifyItemRangeChanged(getAdapterPosition(), mDataSet.size()-getAdapterPosition());
if still item not removed use this magic method :)
private void deleteItem(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
holder.itemView.setVisibility(View.GONE);
}
Kotlin version
private fun deleteItem(position: Int) {
mDataSet.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, mDataSet.size)
holder.itemView.visibility = View.GONE
}
The Problem
RecyclerView was built to display data in an efficient and responsive manner.
Usually you have a dataset which is passed to your adapter and is looped through to display your data.
Here your dataset is:
private ArrayList<String> mDataset;
The point is that RecyclerView is not connected to your dataset, and therefore is unaware of your dataset changes.
It just reads data once and displays it through your ViewHolder, but a change to your dataset will not propagate to your UI.
This means that whenever you make a deletion/addition on your data list, those changes won't be reflected to your RecyclerView directly. (i.e. you remove the item at index 5, but the 6th element remains in your recycler view).
A (old school) solution
RecyclerView exposes some methods for you to communicate your dataset changes, reflecting those changes directly on your list items.
The standard Android APIs allow you to bind the process of data removal (for the purpose of the question) with the process of View removal.
The methods we are talking about are:
notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)
A Complete (old school) Solution
If you don't properly specify what happens on each addition, change or removal of items, RecyclerView list items are animated unresponsively because of a lack of information about how to move the different views around the list.
The following code will allow RecyclerView to precisely play the animation with regards to the view that is being removed (And as a side note, it fixes any IndexOutOfBoundExceptions, marked by the stacktrace as "data inconsistency").
void remove(position: Int) {
dataset.removeAt(position)
notifyItemChanged(position)
notifyItemRangeRemoved(position, 1)
}
Under the hood, if we look into RecyclerView we can find documentation explaining that the second parameter we pass to notifyItemRangeRemoved is the number of items that are removed from the dataset, not the total number of items (As wrongly reported in some others information sources).
/**
* Notify any registered observers that the <code>itemCount</code> items previously
* located at <code>positionStart</code> have been removed from the data set. The items
* previously located at and after <code>positionStart + itemCount</code> may now be found
* at <code>oldPosition - itemCount</code>.
*
* <p>This is a structural change event. Representations of other existing items in the data
* set are still considered up to date and will not be rebound, though their positions
* may be altered.</p>
*
* #param positionStart Previous position of the first item that was removed
* #param itemCount Number of items removed from the data set
*/
public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
Open source solutions
You can let a library like FastAdapter, Epoxy or Groupie take care of the business, and even use an observable recycler view with data binding.
New ListAdapter
Google recently introduced a new way of writing the recycler view adapter, which works really well and supports reactive data.
It is a new approach and requires a bit of refactoring, but it is 100% worth switching to it, as it makes everything smoother.
here is the documentation, and here a medium article explaining it
Here are some visual supplemental examples. See my fuller answer for examples of adding and removing a range.
Add single item
Add "Pig" at index 2.
String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);
Remove single item
Remove "Pig" from the list.
int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);
Possibly a duplicate answer but quite useful for me. You can implement the method given below in RecyclerView.Adapter<RecyclerView.ViewHolder>
and can use this method as per your requirements, I hope it will work for you
public void removeItem(#NonNull Object object) {
mDataSetList.remove(object);
notifyDataSetChanged();
}
I tried all the above answers, but inserting or removing items to recyclerview causes problem with the position in the dataSet. Ended up using delete(getAdapterPosition()); inside the viewHolder which worked great at finding the position of items.
The problem I had was I was removing an item from the list that was no longer associated with the adapter to make sure you are modifying the correct adapter you can implement a method like this in your adapter:
public void removeItemAtPosition(int position) {
items.remove(position);
}
And call it in your fragment or activity like this:
adapter.removeItemAtPosition(position);
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;
public MyAdapter(Context context, List<cardview_widgets> list) {
this.context = context;
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
viewGroup, false);
return new MyViewHolder(view);
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView txtValue;
TextView txtCategory;
ImageView imgInorEx;
ImageView imgCategory;
TextView txtDate;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
txtValue= itemView.findViewById(R.id.id_values);
txtCategory= itemView.findViewById(R.id.id_category);
imgInorEx= itemView.findViewById(R.id.id_inorex);
imgCategory= itemView.findViewById(R.id.id_imgcategory);
txtDate= itemView.findViewById(R.id.id_date);
}
}
#NonNull
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, int i) {
myViewHolder.txtValue.setText(String.valueOf(list.get(i).getValuee()));
myViewHolder.txtCategory.setText(list.get(i).getCategory());
myViewHolder.imgInorEx.setBackgroundColor(list.get(i).getImg_inorex());
myViewHolder.imgCategory.setImageResource(list.get(i).getImg_category());
myViewHolder.txtDate.setText(list.get(i).getDate());
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
list.remove(myViewHolder.getAdapterPosition());
notifyDataSetChanged();
return false;
}
});
}
#Override
public int getItemCount() {
return list.size();
}}
i hope this help you.
if you want to remove item you should do this:
first remove item:
phones.remove(position);
in next step you should notify your recycler adapter that you remove an item by this code:
notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());
but if you change an item do this:
first change a parameter of your object like this:
Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);
or new it like this :
Service s = new Service();
services.set(position,s);
then notify your recycler adapter that you modify an item by this code:
notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());
hope helps you.
String str = arrayList.get(position);
arrayList.remove(str);
MyAdapter.this.notifyDataSetChanged();
To Method onBindViewHolder Write This Code
holder.remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
values.remove(position);
notifyDataSetChanged();
}
});
Incase Anyone wants to implement something like this in Main class instead of Adapter class, you can use:
public void removeAt(int position) {
peopleListUser.remove(position);
friendsListRecycler.getAdapter().notifyItemRemoved(position);
friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}
where friendsListRecycler is the Adapter name
you must to remove this item from arrayList of data
myDataset.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(), getItemCount());
//////// set the position
holder.cancel.setTag(position);
///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
mDataset.remove(positionToRemove);
notifyDataSetChanged();
}
});
make interface into custom adapter class and handling click event on recycler view..
onItemClickListner onItemClickListner;
public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public interface onItemClickListner {
void onClick(Contact contact);//pass your object types.
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
// below code handle click event on recycler view item.
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListner.onClick(mContectList.get(position));
}
});
}
after define adapter and bind into recycler view called below code..
adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
#Override
public void onClick(Contact contact) {
contectList.remove(contectList.get(contectList.indexOf(contact)));
adapter.notifyDataSetChanged();
}
});
}
In case you are wondering like I did where can we get the adapter position in the method getadapterposition(); its in viewholder object.so you have to put your code like this
mdataset.remove(holder.getadapterposition());
In the activity:
mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);
In the your adapter:
void removeAt(int position) {
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, list.size());
}
void updateAt(int position, String text, Boolean completed) {
TodoEntity todoEntity = list.get(position);
todoEntity.setText(text);
todoEntity.setCompleted(completed);
notifyItemChanged(position);
}
in 2022, after trying everything the whole internet given below is the answer
In MyViewHolder class
private myAdapter adapter;
inside MyViewHolder function initalise adapter
adapter = myAdapter.this
inside onclick
int position = getAdapterPosition()
list.remove(position);
adapter.notifyItemRemoved(position);

Categories