Recyclerview with Section but with a Footer only per section - java

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);

Related

How to add dynamic views to android layout in recyclerview adapter

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.

Why is the content of CardView inside a RecyclerView (in my case) changing when I scroll?

I have the same question like this post "Cardview's data are changed while scrolling RecyclerView".
But taking out the statics isn't working.
Context of what I am doing:
I am adding a couple of buttons inside a FlexboxLayout and again inside a CardView (CardView inside a RecyclerView). Buttons are being added dynamically.
The same thing happens with a couple of TextView, which I add after Buttons to CardView.
Problem:
Buttons and TextViews are being Multiplied, while I Scroll.
Context of different CardViews are being exchanged, while scrolling.
Video: https://sendvid.com/adugp8vh
What I am using:
RecyclerView is inside one of my Fragments (ConstraintLayout), in which I defined the recycler Adapter.
This is my adapter
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {
ArrayList<DataModel> dataSet = new ArrayList<DataModel>();
Context currentContext;
public class MyViewHolder extends RecyclerView.ViewHolder {
public Button datumButton;
public FlexboxLayout matchedWordsLayout;
public LinearLayout textviewLayout;
public MyViewHolder(View itemView) {
super(itemView);
this.datumButton = (Button) itemView.findViewById(R.id.datumButton);
this.matchedWordsLayout = (FlexboxLayout) itemView.findViewById(R.id.matchedWordsLayout);
this.textviewLayout = (LinearLayout) itemView.findViewById(R.id.textviewLayout);
}
}
public CustomAdapter(ArrayList<DataModel> data, Context currentContext) {
this.dataSet = data;
// currentContext not getting it from here
}
#NonNull
#Override
public CustomAdapter onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.positive_result_card, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view);
this.currentContext = parent.getContext();
return myViewHolder;
}
#RequiresApi(api = Build.VERSION_CODES.O)
//#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int listPosition) {
Button DatumButton = holder.datumButton;
FlexboxLayout MatchedWordsLayout = holder.matchedWordsLayout;
LinearLayout TextviewLayout = holder.textviewLayout;
//Modify the button for date
ArrayList <String> TTMMYY = dataSet.get(listPosition).getDatum();
String Datum = String.join(".", TTMMYY);
DatumButton.setText(Datum);
DatumButton.setTag(Datum);
// add button for each word
ArrayList <String> ButtonNames = dataSet.get(listPosition).getButtonnames();
for (String Buttonname : ButtonNames) {
Button sampleButton = new Button(currentContext);
sampleButton.setText(Buttonname);
sampleButton.setTag(Datum);
MatchedWordsLayout.addView(sampleButton);
}
ArrayList <String> textLines = dataSet.get(listPosition).getTextLines();
for (String satzt : textLines){
TextView sampleTextView = new TextView(currentContext);
sampleTextView.setText(satzt);
TextviewLayout.addView(sampleTextView);
}
}
#Override
public int getItemCount() {
return dataSet.size();
}
}
My text has probably mistakes
You are adding Views programmatically on every bind, but you never remove them, so each bind just adds more (remember that ViewHolders are re-used, so the Buttons and TextViews you added last time are still there). To fix it, remove all the children from the ViewGroups before you add the new children:
// Added: remove all the children before we add more
MatchedWordsLayout.removeAllViews();
for (String Buttonname : ButtonNames) {
Button sampleButton = new Button(currentContext);
sampleButton.setText(Buttonname);
sampleButton.setTag(Datum);
MatchedWordsLayout.addView(sampleButton);
}
ArrayList <String> textLines = dataSet.get(listPosition).getTextLines();
// Added: remove all the children before we add more
TextviewLayout.removeAllViews();
for (String satzt : textLines){
TextView sampleTextView = new TextView(currentContext);
sampleTextView.setText(satzt);
TextviewLayout.addView(sampleTextView);
}

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().

Android Realm RecyclerView animations not working?

I have a RecyclerView which gets populated by a RealmRecyclerViewAdapter but somehow there are no animations playing when the data changes.
The adapter class uses multiple ViewHolders for different layouts but that should not affect animations right?
public class DiaryPageEntryAdapter extends RealmRecyclerViewAdapter<Entry, RecyclerView.ViewHolder> {
static class MealEntryViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.item_diary_entry_drink_title) TextView tvMealEntryTitle;
#BindView(R.id.item_diary_entry_meal_time) TextView tvMealEntryTime;
BindView(R.id.item_diary_entry_meal_bullet_list) RecyclerView rvBulletList;
MealEntryBulletAdapter bulletAdapter;
public MealEntryViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
this.bulletAdapter = new MealEntryBulletAdapter();
rvBulletList.setLayoutManager(new LinearLayoutManager(itemView.getContext()));
rvBulletList.setAdapter(this.bulletAdapter);
}
void bindData(MealEntry mealEntry) {
tvMealEntryTitle.setText(mealEntry.getTitle());
tvMealEntryTime.setText(DateTimeUtils.timeValueToText(itemView.getContext(), mealEntry.getTime()));
this.bulletAdapter.updateData(mealEntry.getConsumedMeals() ,mealEntry.getConsumedDrinks());
}
}
// Other ViewHolders
public DiaryPageEntryAdapter(#Nullable OrderedRealmCollection<Entry> data, boolean autoUpdate) {
super(data, autoUpdate, true);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case MEAL_ENTRY:
// Inflate meal entry layout and then create a new meal view holder with it
View rowMeal = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_diary_entry_meal, parent, false);
return new MealEntryViewHolder(rowMeal);
// Other case options.
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Entry entry = getItem(position);
if(Entry.isMealEntry(entry)) {
MealEntryViewHolder mealHolder = (MealEntryViewHolder) holder;
mealHolder.bindData(Entry.getMealEntryFromEntry(entry));
}
// Other if branches.
}
#Override
public int getItemViewType(int position) {
Entry entry = getItem(position);
if(Entry.isMealEntry(entry)) return MEAL_ENTRY;
// Other if branches.
}
The code for setting up the RecyclerView and adapter looks as following:
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.frag_diary_page_rv_entries);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
entryAdapter = new DiaryPageEntryAdapter(null, true);
recyclerView.setAdapter(entryAdapter);
The code that binds the data to the adapter is stated below:
RealmResults<Entry> sortedEntries = diaryEntry.getEntries()
.where()
.findAllSortedAsync("time");
entryAdapter.updateData(sortedEntries);
The auto-updates work fine but somehow no there is no animation when the data changes. A new entry simply appears but without animations. Furthermore I want a short animation to play once the RecyclerView is populated for the first time - an entrance animation similar to [http://anthony-skr.com/article/recyclerview-items-animation-with-rebound-effect][1].
Note: In my appĀ“s build.gradle file
dependencies {
// Other dependencies
compile 'io.realm:android-adapters:2.1.0'
}
In the Project build.gradle file:
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
classpath "io.realm:realm-gradle-plugin:3.1.4"
}
From your code sample, it isn't easy to see where you call entryAdapter.updateDate() from, but calling this method continuously on every update will disable all animations as it just calls notifyDataSetChanged().
A more standard pattern would look like this:
RealmResults<Entry> sortedEntries = diaryEntry.getEntries()
.where()
.findAllSortedAsync("time");
entryAdapter = new DiaryPageEntryAdapter(sortedEntries, true);
recyclerView.setAdapter(entryAdapter);
Animations should work if you do the above.

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

Categories