RecyclerView.onBindViewHolder not populating all items - android

The problem is in onBindViewHolder, where I am trying to set the text of my buttons depending on a condition.
Whatever I try it seems that only the first if statement is evaluated (none of the else-if or else ones) and I only get the buttons that match it, all other button texts are set to null.
Here is my custom RecyclerViewAdapter
public class ParallelRecyclerViewAdapter
extends RecyclerView.Adapter<ParallelRecyclerViewAdapter.ParallelViewHolder> {
private int listItemLayout;
private ArrayList<ParallelClusterItem> itemListForAdapter;
public ParallelRecyclerViewAdapter(int layoutId, ArrayList<ParallelClusterItem> inputList) {
listItemLayout = layoutId;
itemListForAdapter = inputList;
}
#NonNull
#Override
public ParallelViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(listItemLayout, parent, false);
return new ParallelViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ParallelViewHolder holder, int position) {
Button button = holder.year;
String oldPhotoYear = itemListForAdapter.get(position).getmOldPhotoYear();
String relativeYearFrom = itemListForAdapter.get(position).getmRelativeYearFrom();
if (oldPhotoYear != null) {
button.setText(oldPhotoYear);
} else if (relativeYearFrom != null) {
button.setText(relativeYearFrom);
} else {
button.setText("unknown");
}
}
#Override
public int getItemCount() {
return itemListForAdapter == null ? 0 : itemListForAdapter.size();
}
public class ParallelViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
Button year;
ParallelViewHolder(View itemView) {
super(itemView);
year = itemView.findViewById(R.id.row_item);
year.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int pos = getAdapterPosition();
ParallelClusterItem item = itemListForAdapter.get(pos);
Intent intent = new Intent(ParallelMapFragmentActivity.instance, ImageSliderActivity.class);
intent.putExtra("path", item.getPath());
ParallelMapFragmentActivity.instance.startActivity(intent);
}
}
}

Related

Why do values ​disappear after scrolling in Recycler View?

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

Hide the visibility of the nested recyclerview in android on ChildItem Click

I have a nested recycler view on which child item click I want the whole child recycler view to set visibility GONE.
I have tried some way to do it. But keeps on loosing the recycler view state and wrong data show's up. Help, please.
Here is ParentAdapter.java code
public class MessageListAdapter extends RecyclerView.Adapter {
private Context mContext;
private static final int VIEW_TYPE_MESSAGE_SENT = 1;
private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
private List<Payload> chatMessageList;
private RecyclerView.RecycledViewPool recycledViewPool;
private OnItemClickListener onItemClickListener;
MessageListAdapter(Context context, List<Payload> chatMessageList, OnItemClickListener onItemClickListener) {
this.mContext = context;
this.chatMessageList = chatMessageList;
this.onItemClickListener = onItemClickListener;
recycledViewPool = new RecyclerView.RecycledViewPool();
}
#Override
public int getItemCount() {
return chatMessageList.size();
}
// Determines the appropriate ViewType according to the sender of the message.
#Override
public int getItemViewType(int position) {
Payload message = chatMessageList.get(position);
if (TextUtils.equals(message.getUserType(), "USER")) {
// If the current user is the sender of the message
return VIEW_TYPE_MESSAGE_SENT;
} else {
// If some other user sent the message
return VIEW_TYPE_MESSAGE_RECEIVED;
}
}
// Inflates the appropriate layout according to the ViewType.
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == VIEW_TYPE_MESSAGE_SENT) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_user_msg, parent, false);
return new SentMessageHolder(view);
} else {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_bot_msg, parent, false);
return new ReceivedMessageHolder(view);
}
}
// Passes the message object to a ViewHolder so that the contents can be bound to UI.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Payload chatMessage = chatMessageList.get(position);
switch (holder.getItemViewType()) {
case VIEW_TYPE_MESSAGE_SENT:
((SentMessageHolder) holder).bind(chatMessage.getMessage(), getFormattedTime());
break;
case VIEW_TYPE_MESSAGE_RECEIVED:
((ReceivedMessageHolder) holder).bind(chatMessage.getMessage(), getFormattedTime());
((ReceivedMessageHolder) holder).bindRv(chatMessage, mContext, recycledViewPool, onItemClickListener);
break;
}
}
private static class SentMessageHolder extends RecyclerView.ViewHolder {
TextView txtViewChatMsgUser;
TextView txtViewDateTimeUser;
SentMessageHolder(View itemView) {
super(itemView);
txtViewChatMsgUser = itemView.findViewById(R.id.txtViewChatMsgUser);
txtViewDateTimeUser = itemView.findViewById(R.id.txtViewDateTimeUser);
}
void bind(String message, String time) {
txtViewChatMsgUser.setText(message);
txtViewDateTimeUser.setText(time);
}
}
private class ReceivedMessageHolder extends RecyclerView.ViewHolder {
TextView txtViewChatMsgBot;
RecyclerView rvDeepLink;
TextView txtViewDateTimeBot;
ReceivedMessageHolder(View itemView) {
super(itemView);
txtViewChatMsgBot = itemView.findViewById(R.id.txtViewChatMsgBot);
rvDeepLink = itemView.findViewById(R.id.itemRvDeepLink);
txtViewDateTimeBot = itemView.findViewById(R.id.txtViewDateTimeBot);
}
void bind(String message, String time) {
txtViewChatMsgBot.setText(message);
txtViewDateTimeBot.setText(time);
}
void bindRv(Payload chatMessage, Context context, RecyclerView.RecycledViewPool recycledViewPool, OnItemClickListener onItemClickListener) {
if (chatMessage.getData() != null && !chatMessage.isOptionSelected()) {
DeepLinkAdapter deepLinkAdapter = new DeepLinkAdapter(context, chatMessage.getData(), onItemClickListener, getAdapterPosition());
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
linearLayoutManager.setOrientation(RecyclerView.VERTICAL);
rvDeepLink.setLayoutManager(linearLayoutManager);
rvDeepLink.setAdapter(deepLinkAdapter);
rvDeepLink.setRecycledViewPool(recycledViewPool);
deepLinkAdapter.notifyDataSetChanged();
} else {
rvDeepLink.setVisibility(View.GONE);
}
}
}
private String getFormattedTime() {
SimpleDateFormat dateFormat = new SimpleDateFormat("hh.mm aa", Locale.ENGLISH);
return dateFormat.format(new Date());
}
public void clearData() {
chatMessageList.clear();
notifyDataSetChanged();
}
public void onSelected(int position) {
chatMessageList.get(position).setOptionSelected(true);
notifyItemChanged(position);
}
}
Here is my ChildAdapter.java code:
public class DeepLinkAdapter extends RecyclerView.Adapter<DeepLinkAdapter.DeepLinkViewHolder> {
private List<DataItem> dataItemList;
private Context context;
private OnItemClickListener onItemClickListener;
private int parentPos;
DeepLinkAdapter(Context context, List<DataItem> dataItemList, OnItemClickListener onItemClickListener, int parentPos) {
this.context = context;
this.dataItemList = dataItemList;
this.onItemClickListener = onItemClickListener;
this.parentPos = parentPos;
}
#NonNull
#Override
public DeepLinkViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new DeepLinkViewHolder(LayoutInflater.from(context).inflate(R.layout.inner_bot_msg, parent, false), onItemClickListener, parentPos);
}
#Override
public void onBindViewHolder(#NonNull final DeepLinkViewHolder holder, final int position) {
DataItem dataItem = dataItemList.get(position);
if (TextUtils.isEmpty(dataItem.getDeeplink())) {
holder.deepLinkText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
} else {
holder.deepLinkText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_right_arrow, 0);
}
if (!TextUtils.isEmpty(dataItem.getText())) {
holder.deepLinkText.setVisibility(View.VISIBLE);
holder.deepLinkText.setText(dataItem.getText());
} else {
holder.deepLinkText.setVisibility(View.GONE);
}
}
#Override
public int getItemCount() {
if (dataItemList != null) {
return dataItemList.size();
} else {
return 0;
}
}
class DeepLinkViewHolder extends RecyclerView.ViewHolder {
private TextView deepLinkText;
DeepLinkViewHolder(View itemView, OnItemClickListener onItemClickListener, int parentPos) {
super(itemView);
deepLinkText = itemView.findViewById(R.id.innerDeepLinkMsgText);
if (onItemClickListener != null) {
deepLinkText.setOnClickListener(v -> {
onItemClickListener.onItemClick(getAdapterPosition(), parentPos);
});
}
}
}
}
Here is my Click Listener Code in the Activity:
#Override
public void onItemClick(int childPos, int parentPos) {
if (TextUtils.isEmpty(chatMessageList.get(parentPos).getData().get(childPos).getDeeplink())) {
String message = chatMessageList.get(parentPos).getData().get(childPos).getText();
if (!viewModel.sendMessage(message)) {
setMessage(getString(R.string.error_something_went_wrong), ChatBotManager.UserType.BOT.toString());
} else {
messageListAdapter.onSelected(parentPos);
setMessage(message, ChatBotManager.UserType.USER.toString());
}
} else {
if (TextUtils.equals(chatMessageList.get(parentPos).getData().get(childPos).getDeeplink(), "restart")) {
if (messageListAdapter != null) {
messageListAdapter.clearData();
viewModel.sendMessage("start");
}
} else {
String uri = chatMessageList.get(parentPos).getData().get(childPos).getDeeplink();
Intent newIntent = new Intent(ApplicationClass.getContext(), TalkLinkingActivity.class);
newIntent.setData(Uri.parse(uri));
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);
finish();
}
}
}
Please help what I am missing here, My goal is to have a click listener from the child adapter on Activity, and also after the click on the item, I want to remove the whole recycler view. Just like a chatbot in Swiggy.
Have a method in DeepLinkAdapter that clears the whole list:
public class DeepLinkAdapter extends RecyclerView.Adapter<DeepLinkAdapter.DeepLinkViewHolder> {
.......
public void clearTheWholeList(){
dataItemList.clear();
notifyDataSetChanged();
}
}
Have a method in your MessageListAdapter that removes an item completely:
public class MessageListAdapter extends RecyclerView.Adapter {
......
......
private int positionToClear = -1;
public void removeItem(int position){
positionToClear = position;
notifyDataSetChanged();
}
//in onBindViewHolder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
............
............
//pass the positionToClear here
((ReceivedMessageHolder) holder).bindRv(chatMessage, mContext, recycledViewPool, onItemClickListener , positionToClear);
}
.......
}
In ReceivedMessageHolder
private class ReceivedMessageHolder extends RecyclerView.ViewHolder {
//here add new param for removed position
void bindRv(Payload chatMessage, Context context, RecyclerView.RecycledViewPool recycledViewPool, OnItemClickListener onItemClickListener, int positionToClear) {
if (chatMessage.getData() != null && !chatMessage.isOptionSelected()) {
DeepLinkAdapter deepLinkAdapter = new DeepLinkAdapter(context, chatMessage.getData(), onItemClickListener, getAdapterPosition());
//do a check here
if(getAdapterPosition() == positionToClear){
deepLinkAdapter.clearTheWholeList();
}
.......
.......
.......
} else {
rvDeepLink.setVisibility(View.GONE);
}
}
}
Assuming the onItemClick interface is working:
#Override
public void onItemClick(int childPos, int parentPos) {
...........
...........
//somewhere where you want the item to be removed
messageListAdapter.removeItem(parentPos);
............
............
}

Make onClickListener accessible in Activity from RecyclerViewAdapter

Actually in my RecyclerView i'm able to create Child items by programmatically adding view and TextView.
I even have a onClickListener for my parent item from the same RecyclerView and i've added a onClick method for my Child view's but now my main issue is how can i use the Child onClick in my Activity?
Here is my Adapter code
public class AdapterPTERM extends RecyclerView.Adapter<AdapterPTERM.ExampleViewHolder> {
private List<ItemPTERM> mExampleList;
private final LayoutInflater mInflater;
private OnItemClickListener mListener;
// ONCLICK FOR THE CHILD ITEMS
private View.OnClickListener varientClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.getTag()!= null){
int position = (int) v.getTag();
}
}
};
public interface OnItemClickListener{
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener = listener;
}
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_pterm,parent,false);
return new ExampleViewHolder(v,mListener);
}
AdapterPTERM(Context context, List<ItemPTERM> exampleList){
mExampleList = exampleList;
mInflater = LayoutInflater.from(context);
}
#Override
public void onBindViewHolder(#NonNull final ExampleViewHolder holder, final int position) {
final ItemPTERM item = mExampleList.get(position);
holder.desc.setText(item.getBtnName());
holder.qta.setText(String.valueOf(item.getQuant()));
holder.variantsContainer.removeAllViews();
// CREATING CHILD ITEM
List<Variant> variants = item.getVariants();
if (variants != null && variants.size() > 0){
for(Variant v : variants){
View vView = mInflater.inflate(R.layout.variant_layout,holder.variantsContainer,false);
TextView nameTV = vView.findViewById(R.id.variant_name);
nameTV.setText(v.getName());
vView.setTag(position);
vView.setOnClickListener(varientClickListener);
holder.variantsContainer.addView(vView);
}
}
// NOT IMPORTANT (CHANGING FIELDS COLOR)
if(position % 2 == 0 ){
holder.itemView.setBackgroundColor(Color.parseColor("#C0C0C0"));
}else if(position % 2 == 1){
holder.itemView.setBackgroundColor(Color.parseColor("#D3D3D3"));
}
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public class ExampleViewHolder extends RecyclerView.ViewHolder {
public TextView desc;
public TextView qta;
private LinearLayout variantsContainer;
ExampleViewHolder(View itemView, final OnItemClickListener listener) {
super(itemView);
desc = itemView.findViewById(R.id.Desc);
qta = itemView.findViewById(R.id.Qta);
variantsContainer = itemView.findViewById(R.id.ll_child_items);
// onClick method for Parent Item
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
listener.onItemClick(position);
}
}
}
});
}
}
public void removeItem(int position) {
mExampleList.remove(position);
notifyItemRemoved(position);
}
public void removeVariant(int position,int positionbtn){
mExampleList.get(positionbtn).getVariants().remove(position);
notifyDataSetChanged();
}
}
While here is my code from the Activity where i build my RecyclerView and here i'm using the onClick for the parent for make appear an AlertDialog but i would be able to make appear that AlertDialog by clicking onChild instead of parent.
public void buildTopRecyclerView() {
mRecyclerViewTOP = findViewById(R.id.listView);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
exampleAdapter = new AdapterPTERM(this,dummyDataItems);
mRecyclerViewTOP.setLayoutManager(mLayoutManager);
mRecyclerViewTOP.setAdapter(exampleAdapter);
mRecyclerViewTOP.setHasFixedSize(true);
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
if (direction == ItemTouchHelper.RIGHT) {
customAllertDelete(position);
exampleAdapter.notifyItemChanged(position);
}
if (direction == ItemTouchHelper.LEFT) {
customAllertQuantity(position);
exampleAdapter.notifyItemChanged(position);
}
}
}).attachToRecyclerView(mRecyclerViewTOP);
exampleAdapter.setOnItemClickListener(new AdapterPTERM.OnItemClickListener() {
#Override
public void onItemClick(int position) {
if (dummyDataItems.get(position).getVariants() != null) {
customAlertVar(position);
}else {
//
}
}
});
}
SOLVED
I just had to remove
// ONCLICK FOR THE CHILD ITEMS
private View.OnClickListener varientClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.getTag()!= null){
int position = (int) v.getTag();
}
}
};
From my adapter as it was handling another click and was blocking the AlertDialog and even removed the
vView.setTag(position);
vView.setOnClickListener(varientClickListener);

Setting up a click listener on a button within a RecycleView

I have it clicked and it gets up to it and shows the right getText() method but the setText method is not working...
userAdapter.setOnEntryClickListener(new UserAdapter.OnEntryClickListener() {
#Override
public void onEntryClick(View view, int position) {
DatabaseUser user = dbUsersList.get(position);
TextView clickedView = (TextView) view.findViewById(R.id.userAdapterFollowBtn);
if(view == clickedView) {
if (clickedView.getText().equals("following")) {
Log.d(Constants.DEBUG, " THE CLICK VIEW IS " + clickedView.getText());
//APPLY Following
String txtFollow = "follow";
clickedView.setText(txtFollow);
if (user.getIsChanged() == 0) {
user.setIsChanged(1);
} else {
user.setIsChanged(0);
}
user.setIsType(3);
db.updateFollow(user);
userAdapter.notifyDataSetChanged();
} else {
clickedView.setText("following");
if (user.getIsChanged() == 0) {
user.setIsChanged(1);
} else {
user.setIsChanged(0);
}
user.setIsType(0);
db.updateFollow(user);
userAdapter.notifyDataSetChanged();
}
} else {
Toast.makeText(getApplicationContext(), user.getUsername() + " is selected!", Toast.LENGTH_SHORT).show();
takeToUserProfile(dbUsersList.get(position));
}
}
});
Here is the adapter class:
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.MyViewHolder> {
private List<DatabaseUser> dbUsersList, followingList;
private DatabaseHelper db;
private Context context;
private Typeface typeFace, italicTypeface, boldTypeface;
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView userAdapterUsername, userAdapterFollowBtn;
public ImageView userAdapterUserPicture;
public MyViewHolder(View view) {
super(view);
userAdapterUsername = (TextView) view.findViewById(R.id.userAdapterUsername);
userAdapterFollowBtn = (TextView) view.findViewById(R.id.userAdapterFollowBtn);
userAdapterUserPicture = (ImageView) view.findViewById(R.id.userAdapterUserPicture);
Log.d(Constants.DEBUG, "IN MY VIEW HOLDER");
view.setOnClickListener(this);
userAdapterFollowBtn.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (mOnEntryClickListener != null) {
Log.d(Constants.DEBUG, "IN On click");
mOnEntryClickListener.onEntryClick(v, getAdapterPosition());
}
}
}
private static OnEntryClickListener mOnEntryClickListener;
public interface OnEntryClickListener {
void onEntryClick(View view, int position);
}
public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
mOnEntryClickListener = onEntryClickListener;
}
public UserAdapter(Context mContext, List<DatabaseUser> usersList, List<DatabaseUser> passedFollowing, Typeface myTypeface, Typeface myTypefaceItalic, Typeface myTypefaceBold) {
context = mContext;
dbUsersList = usersList;
followingList = passedFollowing;
typeFace = myTypeface;
italicTypeface = myTypefaceItalic;
boldTypeface = myTypefaceBold;
Log.d(Constants.DEBUG, "IN MY User ADAPTER CONSTRUCTOR");
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.follow_item, parent, false);
Log.d(Constants.DEBUG, "RETURN ITEM VIEW HOLDER");
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
DatabaseUser user = dbUsersList.get(position);
holder.userAdapterUsername.setTypeface(boldTypeface);
holder.userAdapterUsername.setText(user.getUsername());
final int pos = getItemViewType(position);
//TODO Create pic link
if(containsId(dbUsersList.get(pos), followingList)) {
//Then show following
holder.userAdapterFollowBtn.setText("following");
} else {
//show follow
holder.userAdapterFollowBtn.setText("follow");
}
String userspic = dbUsersList.get(pos).getPicture();
if(userspic == null) {
//SET DEFAULT OR PUT DEFAULT IN XML AND DO NOTHING IT SHOULD SHOW DEFAULT PIC
} else {
//TODO setupUser Pic
String img1 = "http://www.hindustantimes.com/Images/popup/2015/6/kungfu2.jpg";
Picasso.with(context).load(img1).transform(new RoundedTransformation()).into(holder.userAdapterUserPicture);
}
}
#Override
public int getItemCount() {
return dbUsersList.size();
}
public static boolean containsId(DatabaseUser currentUser, List<DatabaseUser> list) {
for (DatabaseUser object : list) {
if (currentUser.getUserId().equals(object.getUserId())) {
return true;
}
}
return false;
}
#Override
public int getItemViewType(int position) {
return position;
}
}
I have done the same in this project, go here and copy this itemclicklistener class:
https://github.com/isaacurbina/MyMovies/blob/c985f28311f25522c7907d090e48dab5fc108c01/app/src/main/java/com/mobileappsco/training/mymovies/Listeners/RecyclerItemClickListener.java
Then on your Activity or Fragment do this with your recyclerview object.
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(context, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
// HERE GOES YOUR CODE FOR THE CLICK OF THE ITEM
}
})
);
Then you can use the position to reference the original object inside the list of objects that your adapter is using (the list should be public so that you can access it from the Activity or Fragment), or do like me and put a hidden textview with the ID instead of the position that you can get with:
TextView clickedItem = (TextView) view.findViewById(R.id.hidden_id_textview);
int id = clickedItem.getText();
You can also check at the whole project that contains the class that I sent you in the link above. I hope it helps.
Kind regards!

Android recyclerView findViewHolderForAdapterPosition returns null

I want to click an item in recyclerView programmatically, I found a way do that:
recyclerView.findViewHolderForAdapterPosition(0).itemView.performClick();
but it doesn't work for me, the findViewHolderForAdapterPosition just returns null.
Did I miss something in my code?
HistoryListAdapter:
public class HistoryListAdapter extends RecyclerView.Adapter<HistoryListAdapter.ViewHolder> {
private static ArrayList<RecordItem> recordItems;
private static FragmentActivity activity;
private static RecordList recordList;
public HistoryListAdapter(ArrayList<RecordItem> recordItems, FragmentActivity FA, RecordList FRL) {
this.recordItems = recordItems;
this.activity = FA;
this.recordList = FRL;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recorditem, parent, false);
return new ViewHolder(itemLayoutView);
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
// TextView setText ...
}
// Return the size of the itemsData (invoked by the layout manager)
#Override
public int getItemCount() {
return recordItems.size();
}
// inner class to hold a reference to each item of RecyclerView
public static class ViewHolder extends RecyclerView.ViewHolder{
public TextView result, datetime;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
itemLayoutView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (activity != null) {
// do something...
}
}
});
result = (TextView) itemLayoutView.findViewById(R.id.result);
heartrate = (TextView) itemLayoutView.findViewById(R.id.heartrate);
datetime = (TextView) itemLayoutView.findViewById(R.id.datetime);
}
}
}
RecordList:
RecyclerView listView = (RecyclerView) layoutView.findViewById(R.id.listView);
HistoryListAdapter listadapter = new HistoryListAdapter(itemsToShow, getActivity(), RecordList.this);
listView.swapAdapter(listadapter, false);
listView.findViewHolderForAdapterPosition(0).itemView.performClick();
I omitted some code, But it should not affect the overall structure of my code.
According to the official documentation:
if notifyDataSetChanged() has been called but the new layout has not
been calculated yet, this method will return null since the new
positions of views are unknown until the layout is calculated.
It's not safe to use findViewHolderForAdapterPosition().
While you call this method after listView.swapAdapter(listadapter, false); you'll always get a null as result because notifyDataSetChanged() will be called.
you can do this:
listView.postDelayed(new Runnable()
{
#Override
public void run()
{
if(listView.findViewHolderForAdapterPosition(0)!=null )
{
listView.findViewHolderForAdapterPosition(0).itemView.performClick();
}
}
},50);
I know that is very late, but maybe is helpful for other people in the Kotlin era:
binding.recycler.post {
val view = binding.recycler.findViewHolderForAdapterPosition(position)?.itemView?.performClick()
}
you can do this:
postAndNotifyAdapter(new Handler(),mRecyclerView);
protected void postAndNotifyAdapter(final Handler handler, final RecyclerView recyclerView) {
handler.post(new Runnable() {
#Override
public void run() {
if ( recyclerView.findViewHolderForLayoutPosition(0)!=null) {
// This will call first item by calling "performClick()" of view.
recyclerView.findViewHolderForLayoutPosition(0).itemView.performClick();
} else {
//
postAndNotifyAdapter(handler, recyclerView);
}
}
});
}
Hey I've solved the problem this way:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private TextView mTotalSum;
protected int mSum;
protected ArrayList<BasketRVAdapter.ViewHolder> mViewHolders;
public BasketRVAdapter(JSONArray recyclerItems, Context context, TextView totalSum) {
super(recyclerItems, context);
mTotalSum = totalSum;
mViewHolders = new ArrayList<>();
}
#Override
public RecyclerView.ViewHolder onCreateSwipeViewHolder(ViewGroup parent, int i) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_basket_rv_item, parent, true);
return new ViewHolder(view);
}
#Override
public void onBindSwipeViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {
final BasketRVAdapter.ViewHolder holder = (BasketRVAdapter.ViewHolder) viewHolder;
JSONObject object;
try {
object = mRecyclerItems.getJSONObject(position);
final int priceValue = object.getInt("price");
final int quantityValue = object.getInt("quantity");
holder.setId(object.getInt("id"));
holder.title.setText(object.getString("title"));
holder.price.setText(String.format(mContext.getString(R.string.price), priceValue));
holder.quantity.setText(String.valueOf(quantityValue));
holder.sum.setText(String.valueOf(priceValue * quantityValue));
holder.quantity.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String value = holder.quantity.getText().toString();
if (value.equals("")) {
return;
}
if (value.equals("0")) {
Toast.makeText(mContext, R.string.null_value_error, Toast.LENGTH_SHORT).show();
return;
}
holder.sum.setText(String.valueOf(priceValue * Integer.valueOf(value)));
setTotalSum();
}
});
mViewHolders.add(holder);
mSum += priceValue * quantityValue;
if (position == getItemCount() - 1) {
mTotalSum.setText(String.format(mContext.getString(R.string.total_sum), mSum));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public SwipeConfiguration onCreateSwipeConfiguration(Context context, int i) {
return new SwipeConfiguration.Builder(context)
.setLeftSwipeBehaviour(SwipeConfiguration.SwipeBehaviour.NORMAL_SWIPE)
.setRightSwipeBehaviour(SwipeConfiguration.SwipeBehaviour.NO_SWIPE)
.setLeftBackgroundColorResource(R.color.dt_basket_deleted)
.setLeftUndoDescription(R.string.deleted)
.setDescriptionTextColorResource(R.color.dt_white)
.setLeftUndoable(true)
.build();
}
#Override
public void onSwipe(int i, int i1, RecyclerView.ViewHolder holder) {
try {
if (i1 == SWIPE_LEFT) {
remove(i);
notifyItemRemoved(i);
mViewHolders.remove(i);
setTotalSum();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
public int getSum() {
return mSum;
}
public void setTotalSum() {
int totalSum = 0;
for (BasketRVAdapter.ViewHolder holder : mViewHolders) {
totalSum += Integer.valueOf(holder.sum.getText().toString());
}
mSum = totalSum;
mTotalSum.setText(String.format(mContext.getString(R.string.total_sum), totalSum));
}
public static class ViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.trade_title)
public TextView title;
#Bind(R.id.trade_price)
public TextView price;
#Bind(R.id.trade_quantity)
public EditText quantity;
#Bind(R.id.sum)
public TextView sum;
private int mId;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public int getId() {
return mId;
}
public void setId(int id) {
mId = id;
}
}}
Added ArrayList mViewholders field inside class
added each viewholder instance to mViewHolders arraylist inside onBindSwipeViewHolder method
used mViewholders arraylist instead of findViewHolderForadapterposition method inside setTotalSum method
I wouldnt recommend using post delay. Although it works the timing is arbitrary.
Using OnPreDrawListener on the recyclerview, timing will be aligned with the next draw cycle and visible items are accessible.

Categories

Resources