I have attached a picture to help you understand my problem.
I'm making an Workout app.
When the button is pressed, the routine is added
In the added routine, it can use the button again to add detailed data (set, lbs, reps).
So, I am using a two type RecyclerView (except footer).
I used DiffUtil to update the items being added.
I found a strange phenomenon while experimenting for testing. (See photo)
It wasn't an error, so I can't figure out what's wrong with it.
After entering the data, when i added a number of detailed items, the value of the data entered first appeared in the items added later.
And if i scrolled after many items were added, the data moved randomly.
However, what is expected is this phenomenon when you add a lot of items or scroll.
What's wrong?
RoutineAdapter.java
public class RoutineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
final static int TYPE_ROUTINE_FOOTER = 3;
private Context context;
private List<Object> mItems = new ArrayList<>();
OnRoutineItemClickListener routinelistener;
OnRoutineAddClickListener routineAddListener;
public void updateRoutineList(List<Object> newRoutineList) {
final RoutineDiffUtil diffCallback = new RoutineDiffUtil(this.mItems, newRoutineList);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.mItems.clear();
this.mItems.addAll(newRoutineList);
diffResult.dispatchUpdatesTo(this);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View itemView;
if(viewType == TYPE_ROUTINE){
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder(itemView);
}
else if(viewType == TYPE_ROUTINE_DETAIL){
itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailViewHolder(itemView);
}
else {
itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, false);
return new RoutineAddFooterViewHolder(itemView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object obj;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
obj = mItems.get(position);
setRoutineData((RoutineViewHolder) holder, (RoutineModel) obj);
break;
case TYPE_ROUTINE_DETAIL:
obj = mItems.get(position);
RoutineDetailModel item = (RoutineDetailModel) obj;
((RoutineDetailViewHolder) holder).setDetailItem(item);
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
#Override
public int getItemCount() {
if(mItems == null)
return -1;
return mItems.size() + 1; // for footer
}
#Override
public int getItemViewType(int position) {
if(position == mItems.size()) {
return TYPE_ROUTINE_FOOTER;
}
else {
Object obj = mItems.get(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
else {
// obj instanceof RoutineDetailModel
return TYPE_ROUTINE_DETAIL;
}
}
}
// add routine interface
public interface OnRoutineAddClickListener {
public void onAddRoutineClick();
}
public void setOnAddRoutineClickListener(OnRoutineAddClickListener listener) {
this.routineAddListener = listener;
}
// details add / remove interface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
}
public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
this.routinelistener = listener;
}
private class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public Button comment;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
initViews();
addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onAddBtnClicked(getAdapterPosition());
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onDeleteBtnClicked(getAdapterPosition());
}
});
comment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onWritingCommentBtnClicked(getAdapterPosition());
}
});
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
public TextView set;
public TextView weight;
public RoutineDetailViewHolder(#NonNull View itemView) {
super(itemView);
initViews();
}
private void initViews() {
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
private void setDetailItem(RoutineDetailModel item) {
set.setText(item.getSet().toString() + "set");
}
}
}
RoutineDiffUtil.java
public class RoutineDiffUtil extends DiffUtil.Callback {
private List<Object> oldRoutineList;
private List<Object> newRoutineList;
public RoutineDiffUtil(List<Object> oldRoutineList, List<Object> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
#Override
public int getOldListSize() {
return oldRoutineList.size();
}
#Override
public int getNewListSize() {
return newRoutineList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
Object oldObj = oldRoutineList.get(oldItemPosition);
Object newObj = newRoutineList.get(newItemPosition);
if (oldObj instanceof RoutineModel && newObj instanceof RoutineModel) {
return ((RoutineModel) oldObj).id == ((RoutineModel) newObj).id;
}
else if (oldObj instanceof RoutineDetailModel && newObj instanceof RoutineDetailModel) {
return ((RoutineDetailModel) oldObj).id == ((RoutineDetailModel) newObj).id;
}
else if(oldObj instanceof RoutineModel && newObj instanceof RoutineDetailModel) {
return false;
}
else {
return false;
}
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return (oldRoutineList.get(oldItemPosition)).equals(newRoutineList.get(newItemPosition));
}
}
RoutineDetailModel.java
public class RoutineDetailModel {
public int id;
private int set = 1;
private int weight;
public RoutineDetailModel() {
Random random = new Random();
this.id = random.nextInt();
}
public RoutineDetailModel(int set) {
Random random = new Random();
this.id = random.nextInt();
this.set = set+1;
}
public Integer getSet() {
return set;
}
public int getId() {
return id;
}
#Override
public int hashCode() {
return Objects.hash(set, weight);
}
#Override
public boolean equals(#Nullable Object obj) {
if(obj != null && obj instanceof RoutineDetailModel) {
RoutineDetailModel model = (RoutineDetailModel) obj;
if(this.id == model.getId()) {
return true;
}
}
return false;
}
}
First of all, you should use ListAdapter class with diff util. The problem with your adapter is that, Recycler view recycles views again and again. That is to say that when you entered a text for your first item, this item is used for other views. To solve it after any text change you should keep this text in your model class then you should set this text to the field in onBind() method. To sum up, recycler view uses same view for different items so any data related to any item should be kept in a model and the model should be set to the view in onBind().
Related
Data before scrolling
Data after scrolling
The problem with my app is shown in the pictures above.
After entering the data, if i scroll after adding the item as scrollable, the data disappears.
As a further explanation, sometimes the entered data appears in other items that have been added.
To explain the app, it is an exercise recording app, and it uses a multi-type recycler view.
I used ListAdapter and also DiffUtil. And the picture is related to the Detail item.
TextWatcher was used to save the entered data.
I've been searching a lot to solve this.
The two most searched solutions are here
using getItemViewType(), getItemId()
->I used this method as shown in this link, but it didn't solve the problem.
Using setIsRecyclable(false) inside the holder
-> This method worked. But I heard that setIsRecyclable(false) is a function that does not recycle.
If i use this, isn't it a good way to do it because there is no advantage to using RecyclerView?
RoutineAdapter.java
public class RoutineListAdapter extends ListAdapter<Object, RecyclerView.ViewHolder> {
Context context;
RoutineListAdapter.OnRoutineItemClickListener routinelistener;
RoutineListAdapter.OnRoutineAddClickListener routineAddListener;
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
final static int TYPE_ROUTINE_FOOTER = 3;
public RoutineListAdapter(#NonNull DiffUtil.ItemCallback<Object> diffCallback) {
super(diffCallback);
}
// add routine interface
public interface OnRoutineAddClickListener {
public void onAddRoutineClick();
}
public void setOnAddRoutineClickListener(RoutineListAdapter.OnRoutineAddClickListener listener) {
this.routineAddListener = listener;
}
// add/remove detail interface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
}
public void setOnRoutineClickListener(RoutineListAdapter.OnRoutineItemClickListener listener) {
this.routinelistener = listener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View itemView;
if(viewType == TYPE_ROUTINE){
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineListAdapter.RoutineViewHolder(itemView);
}
else if(viewType == TYPE_ROUTINE_DETAIL){
itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineListAdapter.RoutineDetailViewHolder(itemView);
}
else {
itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, false);
return new RoutineListAdapter.RoutineAddFooterViewHolder(itemView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object curItem;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
curItem = getItem(position);
setRoutineData((RoutineListAdapter.RoutineViewHolder) holder, (RoutineModel) curItem);
break;
case TYPE_ROUTINE_DETAIL:
curItem = getItem(position);
RoutineDetailModel item = (RoutineDetailModel) curItem;
((RoutineListAdapter.RoutineDetailViewHolder) holder).bind(item);
((RoutineDetailViewHolder) holder).weight.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
item.setWeight(((RoutineDetailViewHolder) holder).weight.getText().toString());
}
});
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
private void setRoutineData(RoutineListAdapter.RoutineViewHolder holder, RoutineModel routineItem){
holder.routine.setText(routineItem.getRoutine());
}
public Object getRoutineItem(int position) {
if(getCurrentList() == null || position < 0 || position >= getCurrentList().size())
return null;
return getItem(position);
}
#Override
public int getItemCount() {
return getCurrentList().size() + 1;
}
#Override
public int getItemViewType(int position) {
if(position == getCurrentList().size()) {
return TYPE_ROUTINE_FOOTER;
}
else {
Object obj = getItem(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
else {
// obj instanceof RoutineDetailModel
return TYPE_ROUTINE_DETAIL;
}
}
}
private class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public Button comment;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
comment = itemView.findViewById(R.id.write_comment);
addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onAddBtnClicked(getAdapterPosition());
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onDeleteBtnClicked(getAdapterPosition());
}
});
comment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onWritingCommentBtnClicked(getAdapterPosition());
}
});
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
private TextView set;
private EditText weight;
public RoutineDetailViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
private void bind(RoutineDetailModel item) {
set.setText(item.getSet().toString() + "set");
weight.setText(item.getWeight());
}
}
private class RoutineAddFooterViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public RoutineAddFooterViewHolder(#NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.add_text);
ConstraintLayout regionForClick = itemView.findViewById(R.id.clickable_layout);
regionForClick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (routineAddListener != null) {
routineAddListener.onAddRoutineClick();
}
}
});
}
}
}
UPDATED
Adapter
public class RoutineListAdapter extends ListAdapter<Object, RecyclerView.ViewHolder> {
// detail add / remove iterface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
public void onWritingWeight(int curRoutinePos, View view); // write weight
}
public void setOnRoutineClickListener(RoutineListAdapter.OnRoutineItemClickListener listener) {
if(this.routinelistener != null)
this.routinelistener = null;
this.routinelistener = listener;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object curItem;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
curItem = getItem(position);
setRoutineData((RoutineListAdapter.RoutineViewHolder) holder, (RoutineModel) curItem);
break;
case TYPE_ROUTINE_DETAIL:
((RoutineListAdapter.RoutineDetailViewHolder) holder).bind();
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
private TextView set;
private EditText weight;
public RoutineDetailViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
weight.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
routinelistener.onWritingWeight(getAdapterPosition(), itemView);
}
});
}
private void bind() {
RoutineDetailModel item = (RoutineDetailModel) getItem(getAdapterPosition());
set.setText(item.getSet().toString() + "set");
weight.setText(item.getWeight()); // Setting the saved value
}
}
Activity
public class WriteRoutineActivity extends AppCompatActivity implements WritingCommentDialogFragment.OnDialogClosedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
diffUtil2 = new RoutineDiffUtil2();
listAdapter = new RoutineListAdapter(diffUtil2);
items = new ArrayList<>();
routine_rv.setAdapter(listAdapter);
listAdapter.setOnRoutineClickListener(new RoutineListAdapter.OnRoutineItemClickListener() {
#Override
public void onWritingWeight(int curRoutinePos, View v) {
RoutineDetailModel item = (RoutineDetailModel) listAdapter.getRoutineItem(curRoutinePos);
EditText weight = v.findViewById(R.id.weight);
item.setWeight(weight.getText().toString()); // This is saved to set the value again when recycled.
});
}
}
If you need any other extra code, please tell me
The issue lies with your TextWatcher that you're adding in onBindViewHolder.
At the moment you have it setup so that every time the RecyclerView binds a view (which can happen multiple times per actual view), you're adding a new TextWatcher and then also setting the text to the item's weight, which is then triggering the previous watchers you added, setting the item's weight to something else, in this case an empty string.
What you should be doing is either removing all listeners before you add another one, or add the listener in onCreateViewHolder and use the holder's adapter position to get your item.
Here is some pseudocode to clarify my suggestions:
Adding the listener in onCreateViewHolder
RoutineDetailViewHolder {
private EditText weight;
RoutineDetailViewHolder {
weight.addTextChangedListener {
items[adapterPosition].setWeight(...)
}
}
}
Removing the listeners before binding again:
RoutineDetailViewHolder {
private EditText weight;
private TextWatcher weightWatcher;
void bind() {
weight.removeTextChangedListener(weightWatcher);
weightWatcher = new TextWatcher();
weight.addOnTextChangedListener(weightWatcher);
}
}
I am developing the Workout log app now.
Two items (routine and routine detail) are expressed using one recycler view and adapter.
If i click the Add Routine button, a routine item is added, and the routine basically has one routine detail item.
Routine items have buttons to add or delete routine detail items.
I used DiffUtil to update the item.
In areItemsTheSame(), I used hashCode to compare oldList and newList.
However, there is a problem of unknown cause when adding or deleting items. (Not an error).
If i click the delete button after adding the routine item and the detail item, it works well at first.
The routine detail buttons of the next routine item cannot be added or deleted unless the delete button of the previous routine item is pressed.
If the delete button of the previous routine item is pressed and then the button of the next item is pressed, the addition or deletion is performed.
Why is this?
This happens when you use hashCode comparison.
However, with equals() this does not happen and works fine.
Instead, every time an item is added or deleted, the entire item is updated with blinking.
How did I have to define the DiffUtil class?
CODE
RoutineModel.java
public class RoutineModel {
private ArrayList<RoutineDetailModel> routineDetailList;
private String routine;
public RoutineModel(String routine) {
this.routine = routine;
}
public void addDetail(RoutineDetailModel item) {
if(routineDetailList == null) {
routineDetailList = new ArrayList<>();
}
this.routineDetailList.add(item);
}
public ArrayList<RoutineDetailModel> getDetailItemList() {
return routineDetailList;
}
public int getDetailItemSize() {
return routineDetailList.size();
}
public String getRoutine() {
return routine;
}
public void removeDetails(int index) throws Exception {
this.routineDetailList.remove(index);
}
#Override
public int hashCode() {
return Objects.hash(routineDetailList, routine);
}
#Override
public boolean equals(#Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
RoutineModel that = (RoutineModel) obj;
return Objects.equals(routine, that.routine) && Objects.equals(routineDetailList, that.routineDetailList);
}
}
RoutineAdapter.java
public class RoutineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
private Context context;
private List<Object> mItems = new ArrayList<>();
OnRoutineItemClickListener listener;
public void updateRoutineList(List<Object> newRoutineList) {
final RoutineDiffUtil diffCallback = new RoutineDiffUtil(this.mItems, newRoutineList);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.mItems.clear();
this.mItems.addAll(newRoutineList);
diffResult.dispatchUpdatesTo(this);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
if (viewType == TYPE_ROUTINE) {
View itemView = LayoutInflater.from(context).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder(itemView);
}
View itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object object = mItems.get(position);
if(object instanceof RoutineModel) {
setRoutineData((RoutineViewHolder) holder, (RoutineModel) object, position);
}
else if(object instanceof RoutineDetailModel) {
}
}
private void setRoutineData(RoutineViewHolder holder, RoutineModel routineItem, int position){
holder.routine.setText(routineItem.getRoutine());
holder.addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null) listener.OnAddBtnClick(position);
}
});
holder.deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null) listener.OnDeleteBtnClick(position);
}
});
}
public Object getRoutineItem(int position) {
if(mItems == null || position < 0 || position >= mItems.size())
return null;
return mItems.get(position);
}
#Override
public int getItemCount() {
if(mItems == null)
return -1;
return mItems.size();
}
#Override
public int getItemViewType(int position) {
Object obj = mItems.get(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
return TYPE_ROUTINE_DETAIL;
}
// detail add,delete click interface
public interface OnRoutineItemClickListener {
public void OnAddBtnClick(int curRoutinePos);
public void OnDeleteBtnClick(int curRoutinePos);
}
public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
this.listener = listener;
}
public class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
}
public class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
public TextView set;
public TextView weight;
public RoutineDetailViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
}
}
RoutineDiffUtil.java
public class RoutineDiffUtil extends DiffUtil.Callback {
private List<Object> oldRoutineList;
private List<Object> newRoutineList;
public RoutineDiffUtil(List<Object> oldRoutineList) {
this.oldRoutineList = oldRoutineList;
newRoutineList = new ArrayList<>();
}
public RoutineDiffUtil(List<Object> oldRoutineList, List<Object> newRoutineList) {
this.oldRoutineList = oldRoutineList;
this.newRoutineList = newRoutineList;
}
#Override
public int getOldListSize() {
return oldRoutineList.size();
}
#Override
public int getNewListSize() {
return newRoutineList.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
// boolean result = oldRoutineList.equals(newRoutineList); // work well
boolean result = oldRoutineList.get(oldItemPosition).hashCode() == newRoutineList.get(newItemPosition).hashCode();
return result;
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldRoutineList.get(oldItemPosition).equals(newRoutineList.get(newItemPosition));
}
}
MainActivity.java
public class WriteRoutineActivity extends AppCompatActivity {
Button add_routine_btn;
TextView title;
RecyclerView routine_rv;
LinearLayoutManager routineLayoutManger;
RoutineAdapter routineAdapter;
List<RoutineModel> items;
List<String> titleData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
items = new ArrayList<>();
routineAdapter = new RoutineAdapter();
routine_rv.setAdapter(routineAdapter);
add_routine_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
routineDialog.show(getSupportFragmentManager(), "RoutineListDialog");
}
});
routineAdapter.setOnRoutineClickListener(new RoutineAdapter.OnRoutineItemClickListener() {
#Override
public void OnAddBtnClick(int routinePos) {
Object obj = routineAdapter.getRoutineItem(routinePos);
if(obj instanceof RoutineModel) {
RoutineModel item = (RoutineModel) obj;
item.addDetail(new RoutineDetailModel());
routineAdapter.updateRoutineList(getDataToBeDisplayed());
}
}
#Override
public void OnDeleteBtnClick(int routinePos) {
Object item = routineAdapter.getRoutineItem(routinePos);
if(item instanceof RoutineModel) {
RoutineModel routineModel = (RoutineModel) item;
if(routineModel.getDetailItemSize() > 1) {
try {
routineModel.removeDetails(routineModel.getDetailItemSize() - 1);
} catch (Exception e) {
e.printStackTrace();
}
}
else { // if delete item exists only one
items.remove(routineModel);
}
routineAdapter.updateRoutineList(getDataToBeDisplayed());
}
}
});
}
public void addRoutine(String routine) {
RoutineModel routineModel = new RoutineModel(routine);
RoutineDetailModel routineDetailModel = new RoutineDetailModel();
routineModel.addDetail(routineDetailModel);
items.add(routineModel);
routineAdapter.updateRoutineList(getDataToBeDisplayed());
}
private List<Object> getDataToBeDisplayed() {
List<Object> mixedList = new ArrayList<>();
for(RoutineModel rm: items){
mixedList.add(rm);
if(rm.getDetailItemList() != null && rm.getDetailItemSize() > 0){
for(RoutineDetailModel rmdetilas: rm.getDetailItemList()){
mixedList.add(rmdetilas);
}
}
}
return mixedList;
}
}
I am using Recyclerview with multiple ViewTypes.I have two Model classes for two different type of item in recyclerview as.I want to display two different type of item in same RecyclerView
My RecyclerViewAdapter is
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
ArrayList<OneView> oneList;
ArrayList<TwoView> twoList;
public RecyclerAdapter(Context context,ArrayList<OneView> oneList,ArrayList<TwoView> twoList){
this.context=context;
this.oneList=oneList;
this.twoList=twoList;
}
#Override
public int getItemViewType(int position) {
switch (position){
case 0:
return 0;
case 1:
return 1;
}
return -1;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layout=0;
RecyclerView.ViewHolder viewHolder;
if(viewType==0){
layout=R.layout.oneview;
View one= LayoutInflater.from(parent.getContext()).inflate(layout,parent,false);
return new ViewHolder(one);
}else if(viewType==1){
layout=R.layout.twoview;
View two= LayoutInflater.from(parent.getContext()).inflate(layout,parent,false);
return new SecondView(two);
}else {
return null;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType=holder.getItemViewType();
switch (viewType){
case 0:
OneView oneView=oneList.get(position);
((ViewHolder)holder).name.setText(oneView.getName());
break;
case 1:
TwoView twoView=twoList.get(position);
((SecondView)holder).name.setText(twoView.getName());
((SecondView)holder).address.setText(twoView.getAddress());
break;
default:
break;
}
}
#Override
public int getItemCount() {
return (this.oneList.size()+this.twoList.size());
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
public ViewHolder(View itemView) {
super(itemView);
name=(TextView)itemView.findViewById(R.id.nameOne);
}
}
public class SecondView extends RecyclerView.ViewHolder {
private TextView name,address;
public SecondView(View itemView) {
super(itemView);
name=(TextView)itemView.findViewById(R.id.nameTwo);
address=(TextView)itemView.findViewById(R.id.address);
}
}
}
I want to display two type of item in same RecyclerView.But I am getting Attempt to write to field 'int android.support.v7.widget.RecyclerView$ViewHolder.mItemViewType' on a null object reference exception. How to resolve this ?
here is the complete example. I've used a RecyclerView with Multiple View Type. My scenario was, I have to show "cuisine list" [which is a horizontal view of multiple cuisines], then "number of restaurant", then "restaurant list".
so, i've used VIEW_RESTAURANT for restaurant view, VIEW_CUISINE for cuisine, VIEW_INFO for number of restaurant. As for my case, there's pagination, so VIEW_PROGRESS is used for showing progress bar at the bottom [by that time new set of data is fetch, if avaiable], VIEW_NO_DATA is used if restaurant list is empty.
hopefully from here, You can manage based on your requirement.
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private String TAG = HomeAdapter.class.getSimpleName();
class ViewType {
int VIEW_PROGRESS = 1;
int VIEW_NO_DATA = 2;
int VIEW_RESTAURANT = 3;
int VIEW_CUISINE = 4;
int VIEW_INFO = 6;
}
private ViewType mViewType = new ViewType();
private Context mContext;
private int mCuisineItem = 1;
private int mInfoItem = 1;
private int mShowNoItem = 0;
private boolean mIsLoading;
private int mRestaurantNumber;
private int mCurrentPage;
private List<Data> mDataList;
private List<CuisineData> mCuisines;
private PaginationListener mPaginationListener;
private HorizontalPaginationListener mHorizontalPaginationListener;
private SearchResultClickListener mSearchResultClickListener;
private String mFrom;
public HomeAdapter(Context context, List<Data> dataList, List<CuisineData> cuisines, RecyclerView recyclerView, SearchResultClickListener searchResultClickListener) {
mContext = context;
mDataList = dataList;
mCuisines = cuisines;
mSearchResultClickListener = searchResultClickListener;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager llManager = (LinearLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visible_threshold = 1;
int total_item_count = llManager.getItemCount();
int last_visible_item = llManager.findLastVisibleItemPosition();
if (!mIsLoading && total_item_count <= (last_visible_item + visible_threshold)) {
if (mPaginationListener != null) mPaginationListener.onPagination();
mIsLoading = true;
}
}
});
}
}
public void setmPaginationListener(PaginationListener paginationListener) {
mPaginationListener = paginationListener;
}
public void setmHorizontalPaginationListener(HorizontalPaginationListener horizontalPaginationListener) {
mHorizontalPaginationListener = horizontalPaginationListener;
}
public void setLoaded() {
mIsLoading = false;
}
public void setmRestaurantNumber(int restaurantNumber) {
mRestaurantNumber = restaurantNumber;
}
public void setmCurrentPage(int currentPage) {
mCurrentPage = currentPage;
}
public void addCuisine(String from) {
mFrom = from;
notifyDataSetChanged();
}
public void addCuisines(List<CuisineData> cuisines, String from) {
mFrom = from;
mCuisines.clear();
mCuisines = cuisines;
notifyDataSetChanged();
}
public void addRestaurants(List<Data> dataList) {
mShowNoItem = 0;
if(mCurrentPage == 1) notifyItemChanged(1);
if (mCurrentPage == 1) notifyItemChanged(2);
int insert_index = mDataList.size();
int pos_start = insert_index + mCuisineItem + mInfoItem;
mDataList.addAll(insert_index, dataList);
notifyItemRangeInserted(pos_start, dataList.size());
}
public void removeRestaurants() {
mShowNoItem = 1;
mDataList.clear();
mDataList = new ArrayList<>();
notifyItemRangeRemoved(mCuisineItem + mInfoItem, mDataList.size());
notifyItemChanged(2);
}
public void addProgressBar(Data data) {
mDataList.add(data);
notifyItemInserted(getItemCount() - 1);
}
public void removeProgressBar() {
if (mDataList.size() > 0) {
mDataList.remove(mDataList.size() - 1);
notifyItemRemoved(mCuisineItem + mInfoItem + mDataList.size());
}
}
public void removeAll() {
mDataList.clear();
mCuisines.clear();
mShowNoItem = 0;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mDataList.size() + mShowNoItem + mCuisineItem + mInfoItem;
}
#Override
public int getItemViewType(int position) {
if (position == 0) return mViewType.VIEW_CUISINE;
if (position == 1) return mViewType.VIEW_INFO;
return mShowNoItem == 0 ? (mDataList.get(position - (mCuisineItem + mInfoItem)) != null ? mViewType.VIEW_RESTAURANT : mViewType.VIEW_PROGRESS)
: mViewType.VIEW_NO_DATA;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == mViewType.VIEW_CUISINE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_horizontal_cuisines, parent, false);
return new ViewHolderCuisinesList(mContext, view, mHorizontalPaginationListener);
}
if (viewType == mViewType.VIEW_INFO) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_restaurant_number, parent, false);
return new ViewHolderRestaurantNumber(mContext, view);
}
if (mShowNoItem == 0) {
if (viewType == mViewType.VIEW_RESTAURANT) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.food_item_nearest_restaurant, parent, false);
return new ViewHolderRestaurant(mContext, view, mSearchResultClickListener);
} else if (viewType == mViewType.VIEW_PROGRESS) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_progress, parent, false);
return new ViewHolderProgress(view);
}
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_empty, parent, false);
return new ViewHolderEmpty(view);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolderSearch) {
((ViewHolderSearch) holder).onBind();
}
if (holder instanceof ViewHolderCuisinesList) {
if (!TextUtils.isEmpty(mFrom)) {
if (mFrom.equalsIgnoreCase(AppConstants.CUISINES))
((ViewHolderCuisinesList) holder).onBind(mCuisines);
else if (mFrom.equalsIgnoreCase(AppConstants.CUISINE))
((ViewHolderCuisinesList) holder).onBind_();
}
}
if (holder instanceof ViewHolderRestaurantNumber) {
((ViewHolderRestaurantNumber) holder).onBind(mRestaurantNumber);
}
if (mShowNoItem == 0) {
if (holder instanceof ViewHolderRestaurant) {
((ViewHolderRestaurant) holder).onBind(position - (mCuisineItem + mInfoItem), mDataList.get(position - (mCuisineItem + mInfoItem)));
} else if (holder instanceof ViewHolderProgress) {
((ViewHolderProgress) holder).onBind();
}
} else {
if (holder instanceof ViewHolderEmpty) {
((ViewHolderEmpty) holder).onBind(mContext.getString(R.string.no_result_found));
}
}
}
}
By overwriting getItemPosition() method I can refresh my FragmentStatePagerAdapter, which works fine. But the change of the contents is not nice to see.
Is there possibility to apply any kind of animation as I refresh the FragmentStatePagerAdapter?
I was also facing same issue.So, what should you do. Make global variables in your adapter for what you want continuous animate.
Now when you start animation on button click. Pass value to global variables.
When you notifydatasetchanged it doesn't effect your animation.
I haven't found any correct way. So, did this thing.It's working perfect.
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private ArrayList<Response.DataBean> feedArrayList = new ArrayList<>();
FeedClickListener mListener;
#Inject
ImageUtility mImageUtility;
#Inject
AppUtils mAppUtils;
#Inject
PreferenceManager mPref;
private float prevPosition = -1;
private YoYo.YoYoString ropeText,ropeImage;
private int tempHandlePlayPause = 0;
private TextView tempTextView;
private ImageView tempImageView;
/**
* click listeners
*/
interface FeedClickListener {
void onPlayAudioClicked( FeedResponse.DataBean feedItem, SeekBar audioProgress, int position);
void onPauseAudioClicked(FeedResponse.DataBean feedItem);
}
public HomeAdapter(Context context, ArrayList<Response.DataBean> mDataArrayList, HomeClickListener mFeedListener) {
((BaseApplication) context.getApplicationContext()).getAppComponent().inject(this);
this.mContext = context;
this.feedArrayList = mDataArrayList;
mListener = mFeedListener;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
ItemViewHolder holderItem = (ItemViewHolder) holder;
int adapterPosition = holder.getAdapterPosition() - 1;
holderItem.normalContainer.setVisibility(View.VISIBLE);
holderItem.bannerContainer.setVisibility(View.GONE);
setUpNormalItem(adapterPosition, holderItem);
}
}
private void setUpNormalItem(int adapterPosition, ItemViewHolder holder) {
FeedResponse.DataBean feedItem = feedArrayList.get(adapterPosition);
// set up data to current view holder items
setUpItems(holder, feedItem, adapterPosition);
}
private void setUpItems(ItemViewHolder holder, FeedResponse.DataBean feedItem, int position) {
if (feedItem.isstop()) {
holder.playImageView.setVisibility(View.GONE);
holder.pauseImageView.setVisibility(View.VISIBLE);
} else {
holder.playImageView.setVisibility(View.VISIBLE);
holder.pauseImageView.setVisibility(View.GONE);
holder.audioProgress.setProgress(0);
}
holder.playImageView.setOnClickListener(v -> {
feedItem.setIscomplete(false);
if (prevPosition >= 0) {
feedArrayList.get((int)prevPosition).setIsstop(false);
feedArrayList.get((int)prevPosition).setIsPrepare(false);
}
feedArrayList.get(position).setIsstop(true);
feedArrayList.get(position).setIsPrepare(true);
mListener.onPlayAudioClicked(feedItem, holder.audioProgress, position);
prevPosition = position;
tempHandlePlayPause = 1;
tempTextView = holder.tvPlayingBlink;
tempImageView = holder.pauseImageView;
notifyDataSetChanged();
});
holder.pauseImageView.setOnClickListener(v -> {
feedArrayList.get(position).setIsstop(false);
tempTextView = holder.tvPlayingBlink;
tempImageView = holder.pauseImageView;
tempHandlePlayPause = 0;
mListener.onPauseAudioClicked(feedItem);
notifyDataSetChanged();
});
if (feedItem.isPrepare()) {
holder.tvPlayingBlink.setVisibility(View.VISIBLE);
if (tempHandlePlayPause == 1){
startAnimation();
}else {
stopAnimation();
}
} else {
holder.tvPlayingBlink.setVisibility(View.INVISIBLE);
if (tempHandlePlayPause == 1){
startAnimation();
}else {
stopAnimation();
}
}
// play on complete audio
if (feedItem.iscomplete()) {
holder.playImageView.setVisibility(View.VISIBLE);
holder.pauseImageView.setVisibility(View.GONE);
}
if ((!feedItem.iscomplete()) && feedItem.getTotalLength() > 0) {
holder.audioProgress.setMax(feedItem.getTotalLength());
holder.audioProgress.setProgress(0);
}
}
private void startAnimation(){
if (ropeText != null || ropeImage != null) {
ropeText.stop(true);
ropeImage.stop(true);
}
ropeText = YoYo.with(Techniques.Pulse)
.duration(800)
.repeat(YoYo.INFINITE)
.pivot(YoYo.CENTER_PIVOT, YoYo.CENTER_PIVOT)
.interpolate(new AccelerateDecelerateInterpolator())
.playOn(tempTextView);
ropeImage = YoYo.with(Techniques.Pulse)
.duration(800)
.repeat(YoYo.INFINITE)
.pivot(YoYo.CENTER_PIVOT, YoYo.CENTER_PIVOT)
.interpolate(new AccelerateDecelerateInterpolator())
.playOn(tempImageView);
}
private void stopAnimation(){
if (ropeText != null || ropeImage != null) {
ropeText.stop(true);
ropeImage.stop(true);
}
}
public void updateData(List<FeedResponse.DataBean> items) {
feedArrayList.clear();
feedArrayList.addAll(items);
notifyDataSetChanged();
}
public void deleteItem(int position) {
feedArrayList.remove(position);
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return (feedArrayList.size());
}
public static class ItemViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.image_view_3)
ImageView imageView3;
#BindView(R.id.tvplayingblink)
TextView tvPlayingBlink;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
I am implementing sticky header with RecyclerView. I have three sections (i.e 3 sticky headers) and they are working fine.Now I have taken three arraylists inside each section, I have initialized these list in my adapter and I am trying to get data of these lists on basis of header id inside onBindViewHolder. But it is not giving me the full list,just one string from each list (i.e under first section--data on first position of mylist,,,under second section-- data on second position of mylist1 ---under third section-- data on third position of mylist2)
Please Help !!
Code in Context:
StickyTestAdapter
public class StickyTestAdapter extends RecyclerView.Adapter<StickyTestAdapter.ViewHolder> implements
StickyHeaderAdapter<StickyTestAdapter.HeaderHolder> {
private Context mContext;
private ArrayList<String> mylist;
private ArrayList<String> mylist1;
private ArrayList<String> mylist2;
private static int countposition;
private String HEADER_FIRIST="HEADER_FIRIST";
private String HEADER_SECOND="HEADER_SECOND";
private String HEADER_THIRD="HEADER_THIRD";
public StickyTestAdapter(Context context) {
prepareData();
prepareData1();
prepareData2();
this.mContext=context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.item_test, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
long rowType= getHeaderId(position);
Log.e("getHeaderId----",""+rowType);
if (rowType==0){
if(!mylist.equals(""))
{
Log.e("list_data----", "" + mylist.get(position));
viewHolder.item.setText(mylist.get(position));
}
else
{
Log.e("Error--0--", "" + "error");
}
} else if (rowType==1){
if(!mylist1.equals(""))
{
Log.e("list_data1----", "" + mylist1.get(position));
viewHolder.item.setText(mylist1.get(position));
}
else
{
Log.e("Error---1-", "" + "error");
}
} else if (rowType==2){
if(!mylist2.equals(""))
{
Log.e("list_data2----", "" + mylist2.get(position));
viewHolder.item.setText(mylist2.get(position));
}
else
{
Log.e("Error----2", "" + "error");
}
}
}
#Override
public int getItemCount() {
if (getHeaderId(countposition)==0){
Log.e("mylist",""+mylist.size());
return mylist.size();
}else if (getHeaderId(countposition)==1){
Log.e("mylist1",""+mylist1.size());
return mylist1.size();
}else if (getHeaderId(countposition)==2){
Log.e("mylist2",""+mylist2.size());
return mylist2.size();
}
return 0;
}
#Override
public long getHeaderId(int pos) {
return pos;
}
#Override
public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.header_test, parent, false);
return new HeaderHolder(view);
}
#Override
public void onBindHeaderViewHolder(HeaderHolder viewholder, int count) {
countposition=count;
if (getItemViewType(count)==0){
viewholder.headertext.setText(HEADER_FIRIST);
}else if (getItemViewType(count)==1){
viewholder.headertext.setText(HEADER_SECOND);
}else if (getItemViewType(count)==2){
viewholder.headertext.setText(HEADER_THIRD);
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
public TextView item;
public ViewHolder(View itemView) {
super(itemView);
item = (TextView)itemView.findViewById(R.id.text_item);
}
}
static class HeaderHolder extends RecyclerView.ViewHolder {
public TextView headertext;
public HeaderHolder(View itemView) {
super(itemView);
headertext = (TextView)itemView.findViewById(R.id.header_text);
}
}
#Override
public int getItemViewType(int position) {
return position;
}
public void prepareData()
{
mylist=new ArrayList<>();
mylist.add("rajendra");
mylist.add("rani");
mylist.add("rahul");
}
public void prepareData1()
{
mylist1=new ArrayList<>();
mylist1.add("ravi");
mylist1.add("vikram");
mylist1.add("rakesh");
}
public void prepareData2()
{
mylist2=new ArrayList<>();
mylist2.add("apple");
mylist2.add("ashok");
mylist2.add("vikash");
}
}
Question is quite old. But that code looks complicated to read, personally I try to not reinvent what others did quite well by creating libraries.
GitHub is full of source that you can import. Some examples: FlexibleAdapter, FastAdapter, Epoxy and others.
I have build the first one, but you can try the others as well.
With mine, having multiple views and headers with sections is quite easy, I point here the wiki page where I define the section and how to initialize it.
Along with this feature, you have a lot more functionalities that makes your life easier.
I divide arraylist into 3 section based on alphabet like contactlist.
For that i use SectionedRecyclerViewAdapter
MainActivity.java
public class MainActivity extends AppCompactActivity {
private SectionedRecyclerViewAdapter sectionAdapter;
#Override
public View onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
sectionAdapter = new SectionedRecyclerViewAdapter();
for(char alphabet = 'a'; alphabet <= 'z';alphabet++) {
List<String> contacts = getContactsWithLetter(alphabet);
if (contacts.size() > 0) {
sectionAdapter.addSection(new ContactsSection(String.valueOf(alphabet), contacts));
}
}
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
return view;
}
#Override
public void onResume() {
super.onResume();
if (getActivity() instanceof AppCompatActivity) {
AppCompatActivity activity = ((AppCompatActivity) getActivity());
if (activity.getSupportActionBar() != null)
activity.getSupportActionBar().setTitle(R.string.nav_example1);
}
}
private List<String> getContactsWithLetter(char letter) {
List<String> contacts = new ArrayList<>();
for (String contact : getResources().getStringArray(R.array.names_)) {
if (contact.charAt(0) == letter) {
contacts.add(contact);
}
}
return contacts;
}
private class ContactsSection extends StatelessSection {
String title;
List<String> list;
ContactsSection(String title, List<String> list) {
super(new SectionParameters.Builder(R.layout.section_ex1_item)
.headerResourceId(R.layout.section_ex1_header)
.build());
this.title = title;
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size();
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
return new ItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
final ItemViewHolder itemHolder = (ItemViewHolder) holder;
String name = list.get(position);
itemHolder.tvItem.setText(name);
itemHolder.imgItem.setImageResource(name.hashCode() % 2 == 0 ? R.drawable.ic_face_black_48dp : R.drawable.ic_tag_faces_black_48dp);
itemHolder.rootView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(), String.format("Clicked on position #%s of Section %s", sectionAdapter.getPositionInSection(itemHolder.getAdapterPosition()), title), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new HeaderViewHolder(view);
}
#Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
headerHolder.tvTitle.setText(title);
}
}
private class HeaderViewHolder extends RecyclerView.ViewHolder {
private final TextView tvTitle;
HeaderViewHolder(View view) {
super(view);
tvTitle = (TextView) view.findViewById(R.id.tvTitle);
}
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
private final View rootView;
private final ImageView imgItem;
private final TextView tvItem;
ItemViewHolder(View view) {
super(view);
rootView = view;
imgItem = (ImageView) view.findViewById(R.id.imgItem);
tvItem = (TextView) view.findViewById(R.id.tvItem);
}
}
}
use structure similar to
public class MultiArray<T> {
List<ItemGroup> lists = new ArrayList<>();
public void addList(String headerText, List<T> list) {
lists.add(new ItemGroup(headerText, list));
}
public int itemCount() {
int count = 0;
for (ItemGroup group : lists) {
count += group.count();
}
return count;
}
public T getItem(int position) {
int count = 0;
for (ItemGroup group : lists) {
if (count + group.count() >= position) {
return group.item(position - count);
}
count += group.count();
}
return null;
}
public int getGroupIndex(int position) {
int count = 0;
int groupIndex = 0;
for (ItemGroup group : lists) {
if (count + group.count() >= position) {
return groupIndex;
}
count += group.count();
groupIndex++;
}
return -1;
}
public String getHeaderText(int position){
int count = 0;
for (ItemGroup group : lists) {
if (count + group.count() >= position) {
return group.headerText;
}
count += group.count();
}
return "";
}
class ItemGroup {
public final String headerText;
public final List<T> list;
public ItemGroup(String headerText, List<T> list) {
this.headerText = headerText;
this.list = list;
}
public int count() {
return list.size();
}
public T item(int position) {
return list.get(position);
}
}
}
you can optimize it for faster performance