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;
}
}
Related
I have a recycleview showing a list of audio files fetched from my audios.json file hosted on my server. i have a model class with a getter method getLanguage() to see the audio language. I would like to show only audio files of users preference in recycle view. Say for example, if user wants only english and russian i would like to show only list of russian and english. How can we achieve this? Right now the entire list is displayed.
public class AudioAdapter extends RecyclerView.Adapter<AudioAdapter.HomeDataHolder> {
int currentPlayingPosition = -1;
Context context;
ItemClickListener itemClickListener;
List<Output> wikiList;
public AudioAdapter(List<Output> wikiList, Context context) {
this.wikiList = wikiList;
this.context = context;
}
#NonNull
#Override
public HomeDataHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.audio_row_layout,viewGroup,false);
HomeDataHolder mh = new HomeDataHolder(view);
return mh;
}
#Override
public void onBindViewHolder(#NonNull final HomeDataHolder homeDataHolder, int i) {
String desc = wikiList.get(i).getLanguage() + " • " + wikiList.get(i).getType();
homeDataHolder.tvTitle.setText(wikiList.get(i).getTitle());
homeDataHolder.tvotherinfo.setText(desc);
homeDataHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
homeDataHolder.rippleLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return wikiList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public void setClickListener(ItemClickListener itemClickListener) { // Method for setting clicklistner interface
this.itemClickListener = itemClickListener;
}
public class HomeDataHolder extends RecyclerView.ViewHolder {
TextView tvTitle,tvotherinfo;
MaterialRippleLayout rippleLayout;
public HomeDataHolder(View v) {
super(v);
this.tvTitle = v.findViewById(R.id.title);
this.tvotherinfo = v.findViewById(R.id.audioDesc);
this.rippleLayout = v.findViewById(R.id.ripple);
}
}
}
The general idea for this should be:
you have one list with all items
you have filter rules selected by the user
You filter items from number 1, to see which ones match the constraints and store this in another list.
Then the recycler view only shows the items of the list from number 3.
This means that recycler view's getItemCount would return the size of the filtered list, not the whole list.
Instead of passing the wikiList as it is, filter it then send it:
Lets say that you filled up the wikiList, before passing it to the adapter, filter it like this:
In the activity that you initialize the adapter in:
public class YourActivity extends ............{
........
........
//your filled list
private List<Output> wikiList;
//filtered list
private List<Output> filteredList= new ArrayList<Output>();
//filters
private List<String> filters = new ArrayList<String>();
//lets say the user chooses the languages "english" and "russian" after a button click or anything (you can add as many as you want)
filters.add("english");
filters.add("russian");
//now filter the original list
for(int i = 0 ; i<wikiList.size() ; i++){
Output item = wikiList.get(i);
if(filters.contains(item.getLanguage())){
filteredList.add(item);
}
}
//now create your adapter and pass the filteredList instead of the wikiList
AudioAdapter adapter = new AudioAdapter(filteredList , this);
//set the adapter to your recyclerview........
......
.....
......
}
I use above "english" and "russian" for language. I don't know how they are set in your response, maybe you use "en" for "english" so be careful.
Actually I am going to ask more than one question here. Don't ban me, please, just read a full story. Let's begin. So I need to create an activity or fragment (it doesn't matter) with to parts (views) inside (top and bottom). Inside the bottom part dynamically loads buttons (sometimes 2, sometimes 30), there is a click listener on them. When a user clicks on a button, the button appears on the top part (view) and disappears on the bottom view. The buttons on the top view also have click listener and if a user clicks on a button it appears on the bottom view and disappears on a top. So this is a task. I thought how to implement it. The simplest solution that I created is: two views are recycler views with two adapters. Mm, probably it is not the best solution, I am pretty sure of it. I could implement two adapters, but I can't implement the click listener for my second adapter. It doesn't work!? I don't like this way for two reasons: 1. both adapters are the same; 2. I can't use click adapter for second adapter. Below you can find my code.
My adapter - standard adapter:
public class KeyboardAdapter extends RecyclerView.Adapter<KeyboardAdapter.KeyboardAdapterViewHolder> {
private List<String> values;
/*
* An on-click handler that we've defined to make it easy for an Activity to interface with
* our RecyclerView
*/
private final KeyboardAdapterOnClickHandler mClickHandler;
/**
* The interface that receives onClick messages.
*/
public interface KeyboardAdapterOnClickHandler {
void onClick(int position, String nameClicked);
}
/**
* Creates a SourceAdapter.
*
* #param clickHandler The on-click handler for this adapter. This single handler is called
* when an item is clicked.
*/
public KeyboardAdapter(List<String> myDataset, KeyboardAdapterOnClickHandler clickHandler) {
values = myDataset;
mClickHandler = clickHandler;
}
/**
* Cache of the children views for a forecast list item.
*/
public class KeyboardAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// each data item is just a string in this case
private Button btnValue;
private String mName;
public View layout;
private int parentId;
private KeyboardAdapterViewHolder(View view) {
super(view);
//layout = view;
btnValue = view.findViewById(R.id.btn);
//parentId = ((View) btnValue.getParent()).getId();
// Call setOnClickListener on the view passed into the constructor (use 'this' as the OnClickListener)
view.setOnClickListener(this);
}
public void setData(String name) {
mName = name;
btnValue.setText(mName);
}
#Override
public void onClick(View view) {
int adapterPosition = getAdapterPosition();
mClickHandler.onClick(adapterPosition, mName);
}
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override
#NonNull
public KeyboardAdapterViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout, parent, false);
return new KeyboardAdapterViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull KeyboardAdapterViewHolder viewHolder, final int position) {
viewHolder.setData(values.get(position));
}
#Override
public int getItemCount() {
return values.size();
}
#Override
public int getItemViewType(int position) {
return 0;
}
public void remove(int position) {
values.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, values.size());
}
}
MainActivity:
protected void onCreate(Bundle savedInstanceState) {
...
String s = "test it";
mAdapter = new KeyboardAdapter(virtualKeyboardInit(s), MainActivity.this);
recyclerView1.setAdapter(mAdapter);
// empty list just to init rv
answerList = new ArrayList<>();
mAdapter1 = new KeyboardAdapter1(answerList, MainActivity.this); // doesn't work, error message "KeyboardAdapter1.KeyboardAdapterOnClickHandler cannot be applied to MainActivity"
recyclerView2.setAdapter(mAdapter1);
}
private List<String> virtualKeyboardInit(String s) {
boolean checkBool = true;
// convert string to array and then to list
String [] strArray = s.split("(?!^)");
stringList = new ArrayList<>(Arrays.asList(strArray));
// shuffle letters in the list
long seed = System.nanoTime();
Collections.shuffle(stringList, new Random(seed));
// API 24
// /String[] strArray = Stream.of(cArray).toArray(String[]::new);
return stringList;
}
#Override
public void onClick(int position, String nameClicked) {
mAdapter.remove(position);
}
These are just a fragment of code. So, what can I do in this case? Thank you for attention and help.
Recycler view item color change repeating after scrolling.
I used to change color at a particular position of the Recyclerview list. When scrolling occurs another item at the bottom has the same change. And it is repeating in pattern. How to resolve this?
holder.recycle_Listing.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemListener.connectionClicked(v,position, itemtype);
holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}
});
The recycler view recycles the view in OnBindViewHolder.So when items are clicked it gets reflected in some other positions.To solve this. create a global SparseBooleanArray to store the clicked position.
private final SparseBooleanArray array=new SparseBooleanArray();
Then inside final viewholder add the clickListener and onClick store the position of the clicked item.
public class ViewHolder extends RecyclerView.ViewHolder {
public YOURVIEW view;
public ViewHolder(View v) {
super(v);
view = (YOURVIEW) v.findViewById(R.id.YOURVIEWID);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
array.put(getAdapterPosition(),true);
notifyDataSetChanged();
}
});
}
}
And in inside OnBindViewHolder,
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
if(array.get(position)){
holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}else{
holder.mainlayout.setBackgroundColor(UNSELECTEDCOLOR);
}
}
I think you can set your background color in void onBindViewHolder(VH holder, int position); such as
List<Integer> selectedPosition = new ArrayList(yourDataSize);
void onBindViewHolder(VH holder, int position){
if(selectedPosition.get(position) == 1){
holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}else {
holder.mainlayout.setBackgroundColor(normalColor);
}
//when the item clicked
selectedPosition.add(position,1);
}
That function returns relative position not absolute so when screen is scrolled position is replaced with a new value. use position from your list for the desired result.
It is due to recycling of viewholder of recycler view. Try this
It worked for me.
public ListViewHolder(View itemView) {
super(itemView);
this.setIsRecyclable(false);
}
try to implement this method in your adapter class, may be it's solve your problem
#Override
public void onViewRecycled(ViewHolderProduct holder) {
super.onViewRecycled(holder);
holder.mainlayout.removeAllViews();
}
Just saved every item keys in an array and that selected array also passed through my Adapter class. Even simple colour change works fine in this format. Here the code is changed as per my the requirement.
#Override
public void onBindViewHolder(final ICConversationHomeAddConnectionsAdapter.ViewHolder holder, final int position) {
JsonObject object = dataArray.get(position).getAsJsonObject();
if(selectedArray.contains(object.get("userkey").getAsString()))
{
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(2, Color.parseColor("#60B9E1"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
//holder.mainlayout.setBackgroundColor(Color.parseColor("#e927a4d1"));
}
else
{
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(1, Color.parseColor("#e0e0e0"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
//holder.mainlayout.setBackgroundColor(Color.parseColor("#f1f1f1"));
}
holder.profileName.setText(object.get("name").getAsString());
holder.recycle_Listing.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.mainlayout.setSelected(!holder.mainlayout.isSelected());
if (holder.mainlayout.isSelected()) {
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(2, Color.parseColor("#60B9E1"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
// holder.mainlayout.setBackgroundColor(Color.parseColor("#11B5DA"));
} else {
GradientDrawable borCol = new GradientDrawable();
borCol.setCornerRadius(7);
borCol.setColor(Color.parseColor("#ffffff"));
borCol.setStroke(1, Color.parseColor("#e0e0e0"));
holder.recycle_Listing.setBackgroundDrawable(borCol);
// holder.mainlayout.setBackgroundColor(Color.parseColor("#f1f1f1"));
}
itemListener.connectionClicked(v,position, itemtype);
//holder.mainlayout.setBackgroundColor(Color.parseColor("#11B5DA"));
//holder.mainlayout.setBackgroundColor(Color.parseColor("#f1f1f1"));
}
});
}
This code works fine with no repeated colour change in recycler. If any queries feel free to ask via comments or chat
Recyclerview & Room Database in Kotlin.
in Activity: //to send position to Adapter
val putPosition = 5 // what you want to be position
Handler(Looper.getMainLooper()).postDelayed({
(Recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(putPosition-1,0)
}, 1500)
//
//
// out of onCreate (for companion)
fun getVersePosition() = putPosition
in Adapter: // to get position from Activity
override fun onBindViewHolder(holder: Adapter.Holder, position: Int) {
val roomData = listData[position]
holder.setRoomData(roomData)
val activity : MainActivity.Companion = MainActivity
val getPosition : Int? = activity.companionMainActivity?.getVersePosition()
if (position == getPosition-1){
holder.itemView.setBackgroundColor(Color.parseColor("#AFB42B"))
} else {
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
}
}
SparseBooleanArray() doesn't need
and bind doesn't need too.
But it's very important
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
If omitted, repetition will occur.
I have a RecyclerView that holds 10 or so views. Each view represents a course, which is stored in an ArrayList. Each Course has an enum "State" which controls coloring and ensures that only one course can be selected at a time. This worked perfectly until I unknowingly made a small change, and didn't realize until a day or more later.
Now, clicking on a course will cause the onClick() method to receive the wrong view, usually 4 or 5 more to the right. This view won't even necessarily be displayed by the RecyclerView at the time of the click, which will mean that the coloring cannot be updated correctly and big problems are caused down the line.
Here's the Fragment:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAdapter = new CoursesAdapter(courseList, context);
mRecyclerView = (RecyclerView) view.findViewById(R.id.courses_recyclerView);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnCourseClickListener(new CoursesAdapter.OnCourseClickListener() {
#Override
public void onCourseClick(Course course) {
//TODO when the course is clicked the course will be passed.
Log.e("TESTING ******", " Course Clicked " + course.name);
if ( currentCourse!=null) {
previousSelection = currentCourse;
previousSelection.setStates(ButtonStates.UNSELECTED);
}
currentCourse=course;
currentCourse.setStates(ButtonStates.SELECTED);
//refreshCourses();
//TODO broadcast Course change.
Intent intent = new Intent(COURSE_SELECTED);
context.sendBroadcast(intent);
}
#Override
public void onCourseDoubleClick(Course course) {
//TODO when the course is double clicked the course will be passed.
}
});
}
The Course passed to the onClick() is always wrong. State control is done at the fragment level, since a RecyclerView adapter would recycle a "selected" view to a non-selected view at times. Often, onBindViewHolder() will not run after a course has been selected, and since the switch statement used to check the states of buttons and update them is done in the adapter, colors are not updated properly.
Here's the adapter:
#Override
public holder onCreateViewHolder(ViewGroup parent, int viewType) {
//LayoutInflater.from(context).inflate(R.layout.course_layout, parent);
return new holder(new CourseRaceButton(context));
}
#Override
public void onBindViewHolder(final holder holder, final int position) {
currentCourse = courses.get(position);
holder.button.setData(courses.get(position), true);
if (!initialised) {
if (position == 0) {
OnCourseClickListener.onCourseClick(currentCourse);
selectedCourseView = holder.button;
}
initialised = true;
}
switch (currentCourse.getStates()) {
case UNSELECTED:
holder.button.colourAsDeselected();
break;
case SELECTED:
holder.button.colourAsSelected();
//selectedCourseView = holder.button;
break;
}
}
#Override
public int getItemCount() {
return courses.size();
}
public interface OnCourseClickListener {
void onCourseClick(Course course);
void onCourseDoubleClick(Course course);
}
public class holder extends RecyclerView.ViewHolder implements View.OnClickListener {
CourseRaceButton button;
public holder(CourseRaceButton view) {
super(view);
view.setOnClickListener(this);
button = view;
}
#Override
public void onClick(View v) {
Log.e("TESTING ******", " ON TOUCH COURSE ");
if (OnCourseClickListener != null) {
OnCourseClickListener.onCourseClick(currentCourse);
}
notifyDataSetChanged();
}
}
My natural reaction is to put notifyDataSetChanged() in the fragment level onClick() in order to properly update the colors, but that gives illegalState exception and would never stop the onClick() returning the wrong course.
The issue is in your onClick() method. You shouldn't be passing currentCourse as a param, as that variable will be storing the course of the last recycled view(last call to onBindViewHolder()), and not the one you clicked upon.
Try this line instead:
OnCourseClickListener.onCourseClick(courses.get(getAdapterPosition()));
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.