RecyclerView nested ViewHolder child views - android

I have problems with RecyclerView when I try to loop adding more child views to the parent view. When I scroll, it appears blank for a second. Is from Data binding or the view rendering?
Here is my code:
public class TournamentFixtureAdapter extends LoadMoreRecyclerViewAdapter<FixtureGroup> {
private OnFixtureClickListener onFixtureClickListener = null;
public TournamentFixtureAdapter(List<FixtureGroup> data) {
super(data);
}
#Override
protected RecyclerView.ViewHolder onCreateContentItemViewHolder(ViewGroup parent, int contentViewType) {
return new TournamentFixtureHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_tournament_fixture, parent, false));
}
#Override
protected void onBindContentItemViewHolder(RecyclerView.ViewHolder holder, int position) {
super.onBindContentItemViewHolder(holder, position);
FixtureGroup fixtureGroup = data.get(position);
((TournamentFixtureHolder) holder).onFixtureClickListener = onFixtureClickListener;
((TournamentFixtureHolder) holder).parentPos = position;
((TournamentFixtureHolder) holder).binding.setFixtureGroup(fixtureGroup);
((TournamentFixtureHolder) holder).addFixtures(fixtureGroup.getFixtures());
}
public void setOnFixtureClickListener(OnFixtureClickListener onFixtureClickListener) {
this.onFixtureClickListener = onFixtureClickListener;
}
static class TournamentFixtureHolder extends FixtureHolder {
ListItemTournamentFixtureBinding binding = null;
public TournamentFixtureHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
}
}
public class FixtureHolder extends BaseAdapter.BaseHolder {
LinearLayout layoutMain = null;
OnFixtureClickListener onFixtureClickListener = null;
int parentPos;
public FixtureHolder(View itemView) {
super(itemView);
layoutMain = (LinearLayout) itemView.findViewById(R.id.layout_main);
setIsRecyclable(layoutMain.getChildCount() > 0);
}
public void addFixtures(final ArrayList<Fixture> fixtures) {
for (final Fixture fixture : fixtures) {
LinearLayout parent = (LinearLayout) LayoutInflater.from(itemView.getContext()).inflate(R.layout.view_fixture, null);
Utils.getDefaultClubLogo((NetworkImageViewPlus) parent.findViewById(R.id.netview_home_img)).setImageUrl(fixture.getHome().getImg(), AppController.getInstance().getImageLoader());
Utils.getDefaultClubLogo((NetworkImageViewPlus) parent.findViewById(R.id.netview_away_img)).setImageUrl(fixture.getAway().getImg(), AppController.getInstance().getImageLoader());
ViewFixtureBinding binding = DataBindingUtil.bind(parent);
layoutMain.addView(parent);
binding.setFixture(fixture);
parent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onFixtureClickListener.onFixtureClick(parentPos, findFixturePosById(fixtures, fixture.getId()));
}
});
}
}
private int findFixturePosById(ArrayList<Fixture> fixtures, int id) {
for (int i = 0; i < fixtures.size(); i++) {
if (fixtures.get(i).getId() == id) {
return i;
}
}
return 0;
}
}

If the problem was more on the data side (adapter), then it would probably be showing up on the normal layout, not just during scrolling. For instance, if you were loading images from a slow server, the initial display would be slow. Since it's only happening when you scroll, that points more to a problem with the layout manager.
For every new view, you have to get it from the adapter, and add it to the layout. If you allow maximum dx in horizontal/vertical scrolling, and have recycled views outside of the screen display cached, it's likely that things will appear blank prior the layout manager getting the new views from the adapter and laying them out.
So there are two factors - horizontal and/or vertical dx is too large, too soon, and the number of recycled (or scrapped) views is too small. So the solution is to either slow down scrolling, or to increase the number of views you are adding off-screen.

Related

RecyclerView not deleted inside RecyclerView.Adapter

I have a recyclerview adapter that is based on inflating two different card layouts. This adapater is dynamically updated with new cards and therefore each of the cards in the adapter needs to be updated as well. For each of the cards, there are a progressbar showing random generated data.
However, the problem Im facing is when I remove one of the cards in my list, the reference for the deleted card is not removed (the onCreateViewHolder is not called and therefore does not update the views), instead the cards left are just writing over the layout of the "deleted" card. The text on the card is correct and it removes the correct item from the list, but the progress bar is still having the old reference for the old card, which makes it adding values that corresponds to the deleted card.
To illustrate the problem, I'm adding random data to each of the cards in the progressbar depending on the movement name. So regardless of the card, the movement card named "Open Hand" should always add random data between 0-20, "Close hand" 20-60" and "Pronation" 60-100".
Here is everything working as supposed after adding three cards:
After inserted three cards
But when I delete the two first cards "Open hand" and "Close hand", the "Pronation" card left is showing the data in progress bar that actually corresponds to the "Open hand" data range. So why isn't the referencing correct?
I've tried calling both onDataSetChange(); and notifyItemRemoved(pos);
After deleted two cards
My adapter code:
public class ParameterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<Movement> movements;
public ParameterAdapter() {
this.movements=movementList;
}
class AddCard extends RecyclerView.ViewHolder {
public AddCard(View inflate) {
super(inflate);
}
}
class Card extends RecyclerView.ViewHolder {
TextView movement;
TextView channel;
TextView remove;
ProgressBar strength;
TextView graph;
LinearLayout expandedView;
LineChart lineChart;
Movement mov;
public Card(View itemView, Movement m) {
super(itemView);
movement = (TextView) itemView.findViewById(R.id.movementText);
channel = (TextView) itemView.findViewById(R.id.channelNameText);
remove = (TextView) itemView.findViewById(R.id.removeCard);
strength = (ProgressBar) itemView.findViewById(R.id.progressBarStrength);
graph = (TextView) itemView.findViewById(R.id.graphTextButton);
expandedView = (LinearLayout) itemView.findViewById(R.id.detailsNr2);
lineChart = (LineChart) itemView.findViewById(R.id.linechartCard);
mov=m;
channel.setText(m.getChannel());
//Setup real time graph
setupLineChart(mov, lineChart, mov.getId());
// Start adding random data to the graph
startAddingRandomData();
}
public void startAddingRandomData() {
new Thread(
new Runnable() {
public void run() {
while (true) {
int generatedData;
// Depending on what type of movement, add slightly different generated random data
// (to be sure that the views are correctly updated when removed for instance)
if(mov.getSettingsType().equals(SettingsDbHelper.MOVEMENTS_STRING[0])){
// Add random generated data between the span 0-20
generatedData=generateRandomData(0,20);
} else if (mov.getSettingsType().equals(SettingsDbHelper.MOVEMENTS_STRING[1])) {
// Add random generated data between the span 20-60
generatedData=generateRandomData(20, 60);
} else {
// Add random generated data between the span 60-100
generatedData=generateRandomData(60, 100);
}
//Set the progress bar
strength.setProgress(generatedData);
// Add the random generated data to the graph
addEntry(mov, lineChart, generatedData);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
public int generateRandomData(int n1, int n2){
Random r = new Random();
return r.nextInt(n2 - n1) + n1;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View vSetting=null;
View vCard=null;
// If its the first position, then inflate the "add movement" card.
if(viewType==0) {
vSetting = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_parameter_cardview, parent, false);
return new AddCard(vSetting);
}else if(viewType>0) {
// Otherwise, if it's not the first position, then inflate a "movement card" and create the corresponding movement item
vCard = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview_movement, parent, false);
Movement m = movementList.get(viewType - 1);
return new Card(vCard, m);
}else {
// For some reason, this occurs
return null;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,final int position) {
// "Add movement" card
if(position==0){
((AddCard)holder).itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), AddMovement.class);
startActivityForResult(intent, ADD_MOVEMENT);
}
});
// "Movement" item card
} else if(position>0) {
final boolean isExpanded = position == mExpandedPosition;
((Card) holder).expandedView.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
((Card) holder).graph.setActivated(isExpanded);
((Card) holder).graph.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mExpandedPosition = isExpanded ? -1 : position;
notifyDataSetChanged();
}
});
// Get the type of movement
Movement m = movementList.get(position - 1);
// This lets the real-time graph view be created before actual adding any data.
m.setChartCreated(1);
//Update the textviews inside the card
((Card)holder).movement.setText(m.getMovement());
((Card)holder).channel.setText(m.getChannel());
((Card)holder).remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Movement m = movementList.get(position - 1);
//Remove the item from the list
remove(m);
// Get ID and settings type in order to update the entry
String id=m.getId();
String settingsType = m.getSettingsType();
//Update the entry
settingsdb.updateSetting(settingsType,id,"false");
}
});
}
}
// Remove item from the list
public void remove(Movement data) {
int position = movementList.indexOf(data);
movementList.remove(data);
notifyDataSetChanged();
// (+ 1 one to compensate for the first card)
notifyItemRemoved(position + 1);
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public int getItemViewType(int position) {
//Return the position
return position;
}
#Override
public int getItemCount() {
// (+1 to compensate for the first card)
return movementList.size()+1;
}
}

how to change image in Recycler View in android (like a radio button)?

I am having Recycler View. It's like a grid view. A total of 9 images in grid layout. If I click a image in any one of the above, that image have to change to an another image. If I click another image. Last one want to reset. Then the clicked image alone will change to highlighted image.
Here is my code...
holder.mLayout.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
//for (int i = 0; i < data_collection.size(); i++) {
holder.mLayout.setVisibility(View.VISIBLE);
holder.mHighLighted.setVisibility(View.GONE);
if (position == i) {
}
//}
holder.mLayout.setVisibility(View.GONE);
holder.mHighLighted.setVisibility(View.VISIBLE);
mHighLight.onHighLight(position, view);
}
});
Remove what you dont need.
#Override
public void onBindViewHolder(final SimpleViewHolder holder, final int position) {
holder.textView.setText(elements.get(position).getName());
holder.textView.setTypeface(typeface1);
CircularImageView circularImageView = (CircularImageView) holder.linearLayout.findViewById(R.id.personazhe_layout_grid_item_image);
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// circularImageView.setBackground(elements.get(position).getPhoto());
// }
circularImageView.setImageDrawable(elements.get(position).getProfileImage());
//Picasso.with(context).load(elements.get(position).getProfileImage()).into(circularImageView);
holder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(pos != position){
c.setImageDrawable(elements.get(position).getProfileImage());
t.setText(elements.get(position).getName());
seekBar.setProgress(0);
pos = position;
}
//image = elements.get(position).getProfileImage();
// textviews
// trajneri = elements.get(position).getTrajneri();
// mosha = elements.get(position).getMosha();
// vendbanimi = elements.get(position).getVendbanimi();
// vendlindja = elements.get(position).getVendlindja();
// arsimi = elements.get(position).getArsimi();
// name = elements.get(position).getName();
// surname = elements.get(position).getSurname();
// pos = elements.get(position).number();
// posi = position;
// button.performClick();
}
});
}
The ViewHolder pattern is something that Android pushed developers to use for a long time, and then (rightfully) forced on them with RecyclerViews. The idea, opposed to a simple ListView, is that you reuse as much of the view as possible when scrolling to reduce inflation and resource identification. The ViewHolder should be managed as something that is changed/not created within the RecyclerView.
Because of that, storing information in a ViewHolder that must be persistent will not work. For that, there are a plethora of other options. Let's go with an inner class that will manage holding onto the currently selected view position and its relative images.
Let's say we have a custom ViewHolder like below:
public class ImageViewHolder extends RecyclerView.ViewHolder{
private ImageView iv;
public ImageViewHolder(View v){
iv = (ImageView) v.findViewById(R.id.iv);
}
public ImageVie getImageView(){
return iv;
}
}
And utilizing that view holder is an adapter DemoAdapter, we can modify it to look something like this:
public class DemoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
public interface SelectionListener{
void onImageSelected(Bitmap bmp);
}
private static class SelectionHolder{
protected int position;
protected Bitmap originalBmp, newBmp;
public SelectionHolder(int position, Bitmap originalBmp,
Bitmap newBmp){
this.position = position;
this.originalBmp = originalBmp;
this.newBmp = newBmp
}
}
private SelectionHolder selectionHolder;
private SelectionListener selectionListener;
/*
Pre-existing Adapter functionality
*/
public void setSelectionListener(SelectionListener listener){
selectionListener = listener;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
/*
Pre-existing onBindViewHolder code
*/
ImageView iv = holder.getImageView();
if(selectionHolder != null && selectionHolder.position == position)
iv.setImageBitmap(selectionHolder.newBmp);
else{
//set the image however you are doing it now
}
iv.setOnClickListener(
new new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageView iv = (ImageView) v;
// Get the IV's current bmp
Bitmap originalBmp = getBitmapFromImageView(iv);
// Get the currently selected image's "new" image
// if it is null, set it to the original bmp
// this will initialize our "highlighting"
Bitmap newBmp = selectionHolder == null || selectionHolder.newBmp == null?
originalBmp: selectoinHolder.newBmp;
// set the selection holder
selectionHolder = new SelectionHolder(position, originalBmp, newBmp);
// notify our listener
if(selectionListener != null)
selectionListener.onImageSelected(bmp);
// refresh the adapter
DemoAdapter.this.notifyDataSetChanged();
}
});
}
private Bitmap getBitmapFromImageView(ImageView iv){
return ((BitmapDrawable)(iv.getDrawable()).getBitmap()
}
}
Then if we have an activity that needs the selected image, perhaps to display it in an ImageView it hosts
recyclerAdapter = new DemoAdapter(...);
recyclerAdapter.setSelectionListener(new SelectionListener(){
#Override
public void onImageSelected(Bitmap bmp){
// set the bmp to your image view or whatever you want
}
}

Facebook native ads in GridView : MediaView displays a grey rectangle

I integrated a Facebook native Ad into a GridView. For now, I display test Ads. It works fine except for the one playing a video.
The MediaView plays a video just fine if the user doesn't interacts with the GridView.
When the GridView is being scrolled, the video is paused and resumed when the Ad reappears on the screen.
After scrolling the grid up and down a few times, the MediaView doesn't show the video anymore and simply displays a grey rectangle.
Out of curiosity, I tried to run Ui Automatic Viewer on my device when the MediaView is grey. I noticed something interesting but I can't really make sense of.
In the View hierarchy, I can see the GridView with a some children FrameLayout (container for the Views given by the adapter). This includes the native ad and the other views.
But when the MediaView is grey, its FrameLayout doesn't appear in the View hierarchy ! But it is rendered just fine on the screen !
I'm very puzzled by what I see.
Also, when I integrated these ads in a RecyclerView, I didn't have this problem (or at least didn't notice it).
Let's talk about the code. I have a reference that points on the Facebook native ad View.
Suggestions welcome :)
Here is the code of the adapter that supplies Views to the GridView :
public class AdapterGridGallery extends BaseAdapter implements AdListener {
private static int POSITION_AD = 4;
private List<QuizzModel> listQuizzes;
int heightViews;
FragmentGallery fragmentGallery;
View facebookAdView;
private NativeAd facebookNativeAd;
private boolean nativeAdSet = false;
public AdapterGridGallery(FragmentGallery fragment, int height) {
heightViews = height;
fragmentGallery = fragment;
facebookNativeAd = new NativeAd(fragment.getContext(), "my_tag");
facebookNativeAd.setAdListener(this);
facebookNativeAd.loadAd();
}
public void updateData(List<QuizzModel> list) {
listQuizzes = list;
notifyDataSetChanged();
}
#Override
public int getCount() {
return listQuizzes != null ? listQuizzes.size() + 1 : 0;
}
#Override
public Object getItem(int i) {
return listQuizzes.get(i);
}
#Override
public long getItemId(int i) {
return listQuizzes.get(i).getId();
}
#Override
public int getItemViewType(int position) {
if (position == POSITION_AD)
return 0;
else
return 1;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
View viewQuizz = null;
switch (getItemViewType(position)) {
case 0:
if (facebookAdView == null) {
facebookAdView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.view_facebook_native, viewGroup, false);
//Settings the height of the view
AbsListView.LayoutParams params = (AbsListView.LayoutParams) facebookAdView.getLayoutParams();
params.height = heightViews;
params.width = AbsListView.LayoutParams.MATCH_PARENT;
facebookAdView.setLayoutParams(params);
}
viewQuizz = facebookAdView;
viewQuizz.setTag(0);
if (facebookNativeAd.isAdLoaded()) {
if (!nativeAdSet) {
Log.d("NativeAdList", "update views resources");
nativeAdSet = true;
ImageView nativeAdIcon = (ImageView) facebookAdView.findViewById(R.id.native_ad_icon);
TextView nativeAdTitle = (TextView) facebookAdView.findViewById(R.id.native_ad_title);
TextView nativeAdBody = (TextView) facebookAdView.findViewById(R.id.native_ad_body);
MediaView nativeAdMedia = (MediaView) facebookAdView.findViewById(R.id.native_ad_media);
TextView nativeAdSocialContext = (TextView) facebookAdView.findViewById(R.id.native_ad_social_context);
Button nativeAdCallToAction = (Button) facebookAdView.findViewById(R.id.native_ad_call_to_action);
nativeAdSocialContext.setText(facebookNativeAd.getAdSocialContext());
nativeAdCallToAction.setText(facebookNativeAd.getAdCallToAction());
nativeAdTitle.setText(facebookNativeAd.getAdTitle());
nativeAdBody.setText(facebookNativeAd.getAdBody());
// Downloading and setting the ad icon.
NativeAd.Image adIcon = facebookNativeAd.getAdIcon();
NativeAd.downloadAndDisplayImage(adIcon, nativeAdIcon);
// Download and setting the cover image.
nativeAdMedia.setNativeAd(facebookNativeAd);
nativeAdMedia.setAutoplay(true);
facebookNativeAd.registerViewForInteraction(facebookAdView);
nativeAdCallToAction.setVisibility(View.VISIBLE);
} else {
Log.d("NativeAdList", "views resources already set");
}
} else {
Log.d("NativeAdList", "nativeAdCallToAction is set invisible");
nativeAdCallToAction.setVisibility(View.INVISIBLE);
}
break;
case 1:
view = new CustomView();
}
return view;
}
#Override
public void onError(Ad ad, AdError adError) {
}
#Override
public void onAdLoaded(Ad ad) {
notifyDataSetChanged();
}
#Override
public void onAdClicked(Ad ad) {
}
}
Here is a screenshot of Ui Automator Viewer.
As you said
when I integrated these ads in a RecyclerView, I didn't have this
problem (or at least didn't notice it).
I interpreted that recycler view work perfectly fine for you. Then instead of trying to redo same thing in gridview simply use LayoutManager to convert recycler view as grid or list.

Scrolling will cause collapsed items in listView to de-collapse, RecyclerView.Adapter set up issue

I have an adapter that extends RecycleView.Adapter and I am running into an issue where scrolling will cause views to change their visibility. I use visibility of views to simulate a collapsed or de-collapsed state, and scrolling is somehow resetting the visibility of the final and first items only from a list of 15 items.
The items begin with a Title displayed with TextView. Clicking on that item will de-collapse that item to reveal the Description text also displayed with TextView (I just set the visibility of the view that has the Description text to VISIBLE). Clicking the Description text will collapse that item (visibility set to GONE) to reveal only the initial Title text.
How come when I scroll to my last item, it de-collapses even when not pressed. Additionally, if I have my first item de-collapsed scrolling to the bottom will collapse the first item. Here is my setup.
public class TextAdapter extends RecyclerView.Adapter<TextAdapter.ViewHolder> {
private static final String TAG = TextAdapter.class.getSimpleName();
private Context mContext;
private ArrayList<TextDescription> alTextDesc;
public TextAdapter(Context context, ArrayList<TextDescription> textDesc) {
mContext = context;
alTextDesc = textDesc;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.ui_item_text_desc, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final TextDescription textDesc = alTextDesc.get(position);
holder.llParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
textDesc.isExpanded = !textDesc.isExpanded;
setExpanded(holder, textDesc);
}
});
setExpanded(holder, textDesc);
}
#Override
public int getItemCount() {
return alTextDesc.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
private static void setExpanded(ViewHolder holder, TextDescription textDesc) {
if (textDesc != null) {
if (textDesc.isExpanded) {
// display full description
llContent.setVisibility(View.VISIBLE);
} else {
// display title
llContent.setVisibility(View.GONE);
}
}
}
public static class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout llParent;
LinearLayout llContent;
public ViewHolder(View v) {
super(v);
llParent = (LinearLayout) v.findViewById(R.id.ll_parent);
llContent = (LinearLayout) v.findViewById(R.id.ll_content);
}
}
My TextDescription model class is nothing more than a boolean
public boolean isExpanded;
Any suggestions on what I am doing incorrectly? Thank you in advance!
EDIT
I have also tried updating my ArrayList after changing the attribute expanded value and the results are still the same
holder.llParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
textDesc.isExpanded = !textDesc.isExpanded;
alTextDesc.set(position, textDesc); //tried to update the list with updated value
setExpanded(holder, textDesc);
}
});
The issue is due to recycling of views.
You need to update your list alTextDesc after you change the expanded flag so that when the views are recycled, you have your flag state saved in the list alTextDesc and it will again collapse/expand based on that saved flag state.
holder.llParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
textDesc.isExpanded = !textDesc.isExpanded;//Update the list with the updated object now
setExpanded(holder, textDesc);
}
});

Data on recycleview item is not correct when scroll

My adapter code:
public class BrandAdapter extends RecyclerView.Adapter<BrandAdapter.BrandViewHolder> {
private static final String TAG = BrandAdapter.class.getSimpleName();
private List<BrandItem> brands;
private Context context;
public BrandAdapter(Context context, List<BrandItem> data) {
this.context = context;
this.brands = data;
}
public void setData(List<BrandItem> dataDownload) {
this.brands = dataDownload;
}
#Override
public BrandViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_item_brand, null);
BrandViewHolder holder = new BrandViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(BrandViewHolder holder, int position) {
BrandItem brandItem = brands.get(position);
String name = brandItem.getName();
int count = brandItem.getCountArticles();
holder.tvName.setText(name);
if (count > 0) {
holder.tvCount.setText("" + count);
} else {
holder.tvCount.setVisibility(View.GONE);
}
}
#Override
public int getItemCount() {
return brands.size();
}
public static class BrandViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
TextView tvCount;
public BrandViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.tv_brand_name);
tvCount = (TextView) itemView.findViewById(R.id.tv_count_article);
}
}
}
Fragment code :
recyclerView = (RecyclerView) view.findViewById(R.id.recycleView);
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new BrandAdapter(getActivity(), brands);
recyclerView.setAdapter(adapter);
Data for brands is downloaded from server. After downloaded finished, I just set new data for adapter by this code :
brands = downloadedBrands();
adapter.setData(brands);
adapter.notifyDataSetChanged();
Everything is Ok when data loaded for first time after the download finish. But when I scroll down the recycleview and scroll up again, data for each item is wrong now, all textview tvCount is gone. I do not know why.
Is there any problem from my code ?
Greenrobo's answer is correct but here is an explanation as to WHY you are having this issue.
You are assuming that your view is always set to the default values in your onBindViewHolder method.
The RecyclerView re-uses views that have scrolled off screen and therefore the view you are binding to may have already been previously used (and changed).
You onBindViewHolder method should always set EVERYTHING up. i.e all views reset to the exact values you want and do not assume that because you default an item to visible, it will always be so.
Please make tvCount visible when setting a non-zero count.
if (count > 0) {
holder.tvCount.setText("" + count);
holder.tvCount.setVisibility(View.VISIBLE);
} else {
holder.tvCount.setVisibility(View.GONE);
}
See if this helps.
You told that if count is less than 0, hide the view. What if count is greater than zero ? You are not making the view visible again. So simply make the below changes in your if condition:
if (count > 0) {
holder.tvCount.setText("" + count);
holder.tvCount.setVisibility(View.VISIBLE);
} else {
holder.tvCount.setVisibility(View.GONE);
}

Categories

Resources