I'm experiencing a strange behavior with a horizontal scroll bar that I made with RecyclerView,
Hint:
I use SharedPreferences in onItemClick interface to transfer the position of clicked item to the fragment where I set the adapter , then I use another interface to transfer the clicked item position from the fragment to the activity where I set current item via viewpager.
Current behavior:
Issue #1
when an item gets selected the background (highlight) color is not changing, it has to be clicked again to get the highlight color.
Issue #2
When an item is clicked the scroll state resets to 1
Question :
why is this happening? and how to fix it?
Expected behavior
when an item is clicked I want the following behavior (single item selection)
RecyclerView adapter class
public class PlanetAdapter extends RecyclerView.Adapter<PlanetAdapter.PlanetViewHolder> {
public interface OnItemClickListener {
void onItemClick(PlanetModel item);
}
private ArrayList<PlanetModel> episodeslist;
private OnItemClickListener listener;
SharedPreferences getPref1x1 = getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
int pos1x1 = getPref1x1.getInt("position",0);
int isPlanetSelected=pos1x1;
public PlanetAdapter(ArrayList<PlanetModel> episodeslist, OnItemClickListener listener) {
this.episodeslist = episodeslist;
this.listener = listener;
}
#Override
public PlanetAdapter.PlanetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.planet_row, parent,false);
PlanetViewHolder vh=new PlanetViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(PlanetAdapter.PlanetViewHolder vh, int position) {
TextView tv = (TextView) vh.itemView;
tv.setText(episodeslist.get(position).getPlanetName());
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bg, 0, 0, 0);
vh.bind(episodeslist.get(position), listener);
if (episodeslist.get(position).isPlanetSelected()) {
vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryDark));
}else{
vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryLight));
}
//holder.image.setImageResource(R.drawable.planetimage);
//vh.text.setText(episodeslist.get(position).toString());
}
#Override
public int getItemCount() {
return episodeslist.size();
}
public class PlanetViewHolder extends RecyclerView.ViewHolder{
protected TextView text;
public PlanetViewHolder(View itemView) {
super(itemView);
text= (TextView) itemView.findViewById(R.id.text_id);
}
public void bind(final PlanetModel item, final OnItemClickListener listener) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
int position = getPosition();
int previousItem = isPlanetSelected;
isPlanetSelected = position;
notifyItemChanged(previousItem);
notifyItemChanged(position);
SharedPreferences setPref = v.getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
setPref.edit().putInt("position", position).apply ();
listener.onItemClick(item);
//Toast.makeText(getContext(), "You have clicked " + item, Toast.LENGTH_SHORT).show();
//Toast.makeText(getContext(), "You have clicked " + ((TextView) itemView).getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
The SharedPreference has a wrong-stored position value. To get the correct position within the ViewHolder, you need to use getBindingAdapterPosition() instead of getPosition.
Also you should update the value of the item before using listener.onItemClick(item);
Also you need to toggle the value of the selection boolean on the object before using notifyItemChanged() in order to reflect the change.
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
int position = getBindingAdapterPosition();
int previousItem = selectedPosition;
selectedPosition = position;
// Updating the selector boolean before notifying the changes to the adapter
PlanetModel currentSelectedItem = episodeslist.get(position);
currentSelectedItem.setPlanetSelected(true);
PlanetModel previousSelectedItem = episodeslist.get(previousItem);
previousSelectedItem.setPlanetSelected(false);
// Notify the changes to the adapter
notifyItemChanged(previousItem);
notifyItemChanged(position);
SharedPreferences setPref = v.getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
setPref.edit().putInt("position", position).apply ();
// Here you should define `item` before triggering the interface callback.
listener.onItemClick(item);
}
});
UPDATE:
The click listener should not be in the bind() as this is called from onBindViewHolder, you are iterating many listeners while you should only need one, to fix this, you need to transfer that to the holder constructor:
So, change the onBindViewHolder to:
#Override
public void onBindViewHolder(PlanetAdapter.PlanetViewHolder vh, int position) {
TextView tv = (TextView) vh.itemView;
tv.setText(episodeslist.get(position));
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bg, 0, 0, 0);
// setting the highlight color
if (episodeslist.get(position).isPlanetSelected()) {
vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryLight));
} else {
vh.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.colorPrimaryDark));
}
}
And change the ViewHolder to:
public class PlanetViewHolder extends RecyclerView.ViewHolder{
protected TextView text;
public PlanetViewHolder(View itemView) {
super(itemView);
text= (TextView) itemView.findViewById(R.id.text_id);
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
int position = getAdapterPosition();
int previousItem = selectedPosition;
selectedPosition = position;
// Updating the selector boolean before notifying the changes to the adapter
PlanetModel currentSelectedItem = episodeslist.get(position);
currentSelectedItem.setPlanetSelected(true);
PlanetModel previousSelectedItem = episodeslist.get(previousItem);
previousSelectedItem.setPlanetSelected(false);
// Notify the changes to the adapter
notifyItemChanged(previousItem);
notifyItemChanged(position);
SharedPreferences setPref = v.getContext().getSharedPreferences("PlanetAdapter", Context.MODE_PRIVATE);
setPref.edit().putInt("position", position).apply ();
// Here you should define `item` before triggering the interface callback.
String item = episodeslist.get(position);
listener.onItemClick(item);
}
});
}
}
Related
So when i click on the 0 position of recyclerview to change the color of the view it also changes the color at 8th, 16th and 24th position (i have a total of 26 items in recyclerview), if i click on position 1 it changes color at 1st, 9th, 17th and 25th and so on. how do i fix this
My recyclerview adapter is
public class AdapterOccupiedRoomCleaning extends RecyclerView.Adapter<AdapterOccupiedRoomCleaning.ViewHolder> {
private List<ItemsAdapter> mList;
private Context mContext;
public AdapterOccupiedRoomCleaning(List<ItemsAdapter> list, Context context){
super();
mList = list;
mContext = context;
}
#NonNull
#Override
public AdapterOccupiedRoomCleaning.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.occupiedrooms_cleaning_item, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull AdapterOccupiedRoomCleaning.ViewHolder viewHolder, int position) {
ItemsAdapter itemAdapter = mList.get(position);
((ViewHolder) viewHolder).setUrduText.setText(itemAdapter.getUrdutext());
((ViewHolder) viewHolder).setEnglishText.setText(itemAdapter.getEnglishText());
((ViewHolder) viewHolder).setCleaningImage.setImageResource(itemAdapter.getImage());
// ((ViewHolder) viewHolder).background.setOnClickListener(new View.OnClickListener() {
// #Override
// public void onClick(View v) {
//
// //viewHolder.background.setBackgroundColor(Color.parseColor("#08A467"));
//
// Toast.makeText(mContext, "Recycle Click " + viewHolder.setEnglishText.getText().toString(), Toast.LENGTH_SHORT).show();
// }
// });
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.background.setBackgroundColor(Color.parseColor("#08A467"));
}
});
}
#Override
public int getItemCount() {
//notifyDataSetChanged();
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView setUrduText, setEnglishText;
public ImageView setCleaningImage;
public View background;
public ViewHolder(View itemView) {
super(itemView);
setUrduText = (TextView) itemView.findViewById(R.id.urduTextView);
setEnglishText = (TextView) itemView.findViewById(R.id.englishTextView);
setCleaningImage = (ImageView) itemView.findViewById(R.id.imageCleaning);
background = (View) itemView.findViewById(R.id.backgroundColor);
}
}
}
My java class is
public class OccupiedRoomCleaning extends AppCompatActivity {
String getQrCode, patientMRNO, roomNumber;
private RecyclerView mRecycleview;
private List<ItemsAdapter> mList = new ArrayList<>();
private AdapterOccupiedRoomCleaning mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_occupied_room_cleaning);
getQrCode = getIntent().getExtras().getString("qrcode");
init();
addList();
adapter();
}
private void init(){
mRecycleview = findViewById(R.id.recyclerview);
}
private void addList(){
ItemsAdapter itemAdapter = new ItemsAdapter();
itemAdapter.setImage(R.drawable.pillowclean);
itemAdapter.setUrdutext("sdf");
itemAdapter.setEnglishText("Pillow Parachute Cover Cleaning");
mList.add(itemAdapter);
itemAdapter = new ItemsAdapter();
itemAdapter.setImage(R.drawable.bedclean);
itemAdapter.setUrdutext("sdfs");
itemAdapter.setEnglishText("Patient Bed Cleaning(Blood Spots)");
mList.add(itemAdapter);
//... more items
}
private void adapter(){
Log.d("anhtt","mlist : " +mList.size());
mAdapter = new AdapterOccupiedRoomCleaning(mList, this);
mRecycleview.setAdapter(mAdapter);
mRecycleview.setLayoutManager(new LinearLayoutManager(this));
}
}
what am i doing wrong here that it changes color at every 8th item
This seems likely to be a problem with the view recycling behavior of RecyclerView.
Generally, you're going to run into problems like this one any time you have some property of your ViewHolder that you only conditionally set inside onBindViewHolder(). That is, because you do not always update the background color of your ViewHolder, you'll get the "wrong" color when it is recycled and reused.
You will have to somehow store the "clicked" or "selected" state in your list of items, and then update the background color of your ViewHolder every time in onBindViewHolder(). Something like this:
#Override
public void onBindViewHolder(#NonNull AdapterOccupiedRoomCleaning.ViewHolder viewHolder, int position) {
ItemsAdapter itemAdapter = mList.get(position);
...
if (itemAdapter.isSelected()) {
viewHolder.background.setBackgroundColor(Color.parseColor("#08A467"));
} else {
viewHolder.background.setBackgroundColor(Color.parseColor("#FFFFFF"));
}
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemAdapter.setSelected(true);
viewHolder.background.setBackgroundColor(Color.parseColor("#08A467"));
}
});
}
Note that it would be better to define the click listener inside onCreateViewHolder() and to use notifyItemChanged() instead of manually updating the background color, but that's outside the scope of this question.
Since, a Recycler view reuses the views and do not create new views, you need to set default color in onBindViewHolder function.
#Override
public void onBindViewHolder(#NonNull AdapterOccupiedRoomCleaning.ViewHolder viewHolder, int position) {
ItemsAdapter itemAdapter = mList.get(position);
((ViewHolder) viewHolder).setUrduText.setText(itemAdapter.getUrdutext());
((ViewHolder) viewHolder).setEnglishText.setText(itemAdapter.getEnglishText());
((ViewHolder) viewHolder).setCleaningImage.setImageResource(itemAdapter.getImage());
// Add this line
((ViewHolder) viewHolder).background.setBackgroundColor(Color.parseColor("default color"));
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.background.setBackgroundColor(Color.parseColor("#08A467"));
}
});
}
I've been searching for a solution for three days now.
If everyone has an Idea please help me, I don't even think it's actually that difficult to achieve but It's not really my branch, if you can say it like this.
In a RecyclerView containing a list with multiple Items, each having a Title(TextView) and a Cover image(ImageView).
This data is set in the Adapter, in the ViewHolder, more specifically in the OnBind function.
So where's my problem?
I've created a PopUp Window, which contains besides a few buttons, a placeholder ImageView and a placeholder TextView.
I cannot seem to find a way to place the data of the clicked Item in the list inside the placeholders.
I think it's similar to the OnBind method but it doesn't work.
Here's the Adapter (if the code for GameItem is needed I'll gladly post it):
import java.util.ArrayList;
public class GameViewAdapter extends RecyclerView.Adapter<GameViewAdapter.GameViewHolder> {
private ArrayList<GameItem> mGameList;
private OnItemClickListener mListener;
public interface OnItemClickListener{
void onGameClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener =listener;
}
public static class GameViewHolder extends RecyclerView.ViewHolder{
public ImageView Cover;
public TextView Title;
public TextView Description;
public PopupWindow popupWindow;
public ImageView popUpImage;
public TextView PopUpTitle;
public EditText customAmount;
public Button add;
private Button addcustom;
private Button exit;
public GameViewHolder(final View itemView, final OnItemClickListener listener) {
super(itemView);
add = itemView.findViewById(R.id.addaverage);
addcustom = itemView.findViewById(R.id.addcustom);
popUpImage = itemView.findViewById(R.id.popupimg);
PopUpTitle = itemView.findViewById(R.id.popuptitle);
customAmount = itemView.findViewById(R.id.gameamount);
Cover = itemView.findViewById(R.id.GameCover);
Title = itemView.findViewById(R.id.GameTitle);
Description = itemView.findViewById(R.id.GameAmount);
exit = itemView.findViewById(R.id.exit);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showPopUp();
}
});
}
public void showPopUp() {
final View popupView = LayoutInflater.from(itemView.getContext()).inflate(R.layout.popup, null);
final PopupWindow popupWindow = new PopupWindow(popupView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
exit = popupView.findViewById(R.id.exit);
exit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);
}
}
public GameViewAdapter(ArrayList<GameItem> gameList){
mGameList = gameList;
}
#Override
public GameViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
Context context = viewGroup.getContext();
View v = LayoutInflater.from(context).inflate(R.layout.game_entry, viewGroup, false);
GameViewHolder GVH = new GameViewHolder(v, mListener);
return GVH;
}
#Override
public void onBindViewHolder(#NonNull GameViewHolder gameViewHolder, int position){
GameItem currentItem = mGameList.get(position);
Glide.with(gameViewHolder.Cover).load(currentItem.getCover()).into(gameViewHolder.Cover);
gameViewHolder.Title.setText(currentItem.getTitle());
gameViewHolder.Description.setText(currentItem.getDescription());
}
#Override
public int getItemCount() {
return mGameList.size();
}
}
In the OnBind method, the Image and Text are associated to the Items
correctly, through Glide and
gameViewHolder.Title.setText(currentItem.getTitle());. Now, I have
created a PopUp, which should get the Image and the Text from the
clicked item in the RecyclerView.
I've reordered some methods and properties for the sake of clarity and then next, I'll explain.
public class GameViewAdapter extends RecyclerView.Adapter<GameViewAdapter.GameViewHolder> {
private ArrayList<GameItem> mGameList;
private OnItemClickListener mListener;
public GameViewAdapter(ArrayList<GameItem> mGameList, GameViewAdapter.OnItemClickListener mListener) {
this.mGameList = mGameList;
this.mListener = mListener;
}
#Override
public GameViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
Context context = viewGroup.getContext();
View v = LayoutInflater.from(context).inflate(R.layout.game_entry, viewGroup, false);
GameViewHolder GVH = new GameViewHolder(v, mListener);
return GVH;
}
#Override
public void onBindViewHolder(#NonNull GameViewHolder gameViewHolder, int position){
gameViewHolder.bind(mGameList.get(position));
}
#Override
public int getItemCount() {
return mGameList.size();
}
class GameViewHolder extends RecyclerView.ViewHolder {
private ImageView itemCover;
private TextView itemTitle;
private TextView itemDescription;
private PopupWindow popupWindow;
private ImageView popUpImage;
private TextView PopUpTitle;
private EditText customAmount;
private Button add;
private Button addcustom;
private Button exit;
public GameViewHolder(View itemView, GameViewAdapter.OnItemClickListener mListener) {
super(itemView);
setupViews(itemView);
}
public void bind(GameItem gameItem) {
Glide.with(this.itemCover).load(gameItem.getCover()).into(this.itemCover);
this.itemTitle.setText(gameItem.getTitle());
this.itemDescription.setText(gameItem.getDescription());
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showPopUp(itemView, gameItem);
}
});
}
private void setupViews(View itemView) {
add = itemView.findViewById(R.id.addaverage);
addcustom = itemView.findViewById(R.id.addcustom);
popUpImage = itemView.findViewById(R.id.popupimg);
PopUpTitle = itemView.findViewById(R.id.popuptitle);
customAmount = itemView.findViewById(R.id.gameamount);
itemCover = itemView.findViewById(R.id.GameCover);
itemTitle = itemView.findViewById(R.id.GameTitle);
itemDescription = itemView.findViewById(R.id.GameAmount);
exit = itemView.findViewById(R.id.exit);
}
private void showPopUp(View itemView, GameItem gameItem) {
final View popupView = LayoutInflater.from(itemView.getContext()).inflate(R.layout.popup, null);
final PopupWindow popupWindow = new PopupWindow(popupView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
final ImageView popupItemCover = popupView.findViewById(R.id.popupItemCover);
final TextView popupItemTitle = popupView.findViewById(R.id.popupItemTitle);
Glide.with(popupItemCover).load(gameItem.getCover()).into(popupItemCover);
popupItemTitle.setText(gameItem.getTitle());
exit = popupView.findViewById(R.id.exit);
exit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);
}
}
public interface OnItemClickListener{
void onGameClick(int position);
}
}
What changed?
First, we now have a method called bind inside our ViewHolder, we took the responsibility from setting the data from our Adapter and delegated it to the ViewHolder.
Second, as that method is called every time onBindViewHolder is called, you'll have the actual GameItem. So? With that item, instead of setting the onClickListener to open the PopUp in the GameViewHolderconstructor, we now set it in the bind method. But, what are the benefits from it? Every time we bind data to its views, we prepare our Popup and then show it.
What should be changed?
For performance purposes, I guess, you should instantiate the constructor and then populate it every time with new data, instead of creating a new instance again every time onBindViewHolder is called.
Edit: As I don't know your popup views IDs, I've created some dummies IDs. Hope you understand.
Best.
In my RecyclerView item, there is a button along with other views. I am hiding the button when it is clicked. Problem is, if I click the button on 1st item, the button on 8th item is auto clicked, if I click button on 2nd item, button on 9th item is auto clicked & so on. How to solve this problem?
Adapter class :
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
private List<Model> models;
Model model;
// public MyAdapterListener onClickListener;
SparseBooleanArray mStateButtons = new SparseBooleanArray();
public Adapter(List<Model> models){
this.models = models;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_row, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
String question = models.get(position).getQues();
final String optA = models.get(position).getOptA();
final String optB = models.get(position).getOptB();
final String optC = models.get(position).getOptC();
final String optD = models.get(position).getOptD();
final String answer = models.get(position).getAns();
holder.question.setText(question);
holder.optA.setText(optA);
holder.optB.setText(optB);
holder.optC.setText(optC);
holder.optD.setText(optD);
holder.options.setTag(position);
holder.options.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int radioButtonID = group.getCheckedRadioButtonId();
int clickedPos = (Integer) group.getTag();
models.get(clickedPos).setChecked(radioButtonID);
}
});
holder.options.check(models.get(position).getChecked());
final int currentPosition = holder.getAdapterPosition();
final Button button = holder.seeAnswer;
if(mStateButtons.valueAt(currentPosition)) {
button.setVisibility(View.GONE);
} else {
button.setVisibility(View.VISIBLE);
}
holder.seeAnswer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mStateButtons.put(position, true);
}
});
}
#Override
public int getItemCount() {
return Models.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
TextView question;
RadioButton optA, optB, optC, optD;
Button seeAnswer;
RadioGroup options;
public ViewHolder(View itemView) {
super(itemView);
options = (RadioGroup) itemView.findViewById(R.id.rgMcqOptions);
question = (TextView) itemView.findViewById(R.id.tvMcqQues);
optA = (RadioButton) itemView.findViewById(R.id.rbOptA);
optB = (RadioButton) itemView.findViewById(R.id.rbOptB);
optC = (RadioButton) itemView.findViewById(R.id.rbOptC);
optD = (RadioButton) itemView.findViewById(R.id.rbOptD);
seeAnswer = (Button) itemView.findViewById(R.id.btnSeeAnswer);
}
}
}
You need some way to keep track of which buttons should be hidden and which should not. This is the responsibility of your adapter, so you need to add some form of array to keep track of button states there. A SparseBooleanArray is an efficient and appropriate option:
private SparseBooleanArray hideButtons = new SparseBooleanArray();
In onBindView you need to update the view for the current item being bound, including updating the button visibility:
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.seeAnswer.setVisibility( hideButtons.get(position, false) ? View.GONE : View.VISIBLE );
...
}
And of course you need to actually set the visibility - and store it in the SparseBooleanArray when clicking the button. Putting this event handler in the ViewHolder is a good option:
class ViewHolder extends RecyclerView.ViewHolder{
Button seeAnswer;
...
ViewHolder(View itemView) {
super(itemView);
seeAnswer = (Button) itemView.findViewById(R.id.btnSeeAnswer);
seeAnswer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
seeAnswer.setVisibility(View.GONE);
hideButtons.put(getAdapterPosition(), true);
}
});
...
}
}
This is a tested and verified solution, so if you follow this and it still doesn't work, the cause of your problem is somewhere else.
This usually happens because you forgot to keep the state of the Button in case it's being recycled. You can use SparseBooleanArray to store the states. Something like this:
public class YourAdapter ... {
// variable to save the state of buttons.
// we use state true as hidden, false as visible
SparseBooleanArray mStateButtons = new SparseBooleanArray();
...
#Override
public void onBindViewHolder(ContactsAdapter.ViewHolder viewHolder, int position) {
final int currentPosition = viewHolder.getAdapterPosition();
// assume this is your button
Button button = viewHolder.yourButton;
// set the previous state to button
if(mStateButtons.valueAt(currentPosition)) {
// state is true, so the button need to be hide.
button.setVisibility(View.GONE);
} else {
// default value is valse, which is we set as visible.
button.setVisibility(View.VISIBLE);
}
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// save the state when clicked
mStateButtons.put(currentPosition, true);
}
});
}
}
UPDATE
Try moving the click handling on ViewHolder, something like this:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
...
Button seeAnswer;
public ViewHolder(View itemView) {
super(itemView);
...
seeAnswer = (Button) itemView.findViewById(R.id.btnSeeAnswer);
itemView.setOnClickListener(this);
}
// Handles the row being being clicked
#Override
public void onClick(View view) {
mStateButtons.put(getAdapterPosition(), true);
view.setVisibility(View.GONE);
}
}
then remove the button.setOnClickListener(new View.OnClickListener() in onBindViewHolder.
The problem seems to be that you are not initializing correctly the state of the Button. Cells in a RecyclerView are reused when they appear or hide in the screen. That means that if you hide the 1st position and then this view is recycled to create the 8th, the Button keeps its state, in this case INVISIBLE
Try to assign a value all cases or init the value to VISIBLE.
In my application i have horizontal list view. On Item select I want to change that selected Item background color and it's text view color. i have figured out that part. But how to reset background color and text view color of previously selected item. here's my adapter class.
public class DateRangeListViewAdapter extends RecyclerView.Adapter<DateRangeListViewAdapter.ContentViewHolder> {
private ItemClickListener itemClickListener;
private LayoutInflater inflater;
private ArrayList<String> data;
private Context context;
private int dataType;
private int previousSelectedPosition;
private static final int DATE_TYPE = 1;
private static final int STATUS_TYPE = 2;
public DateRangeListViewAdapter(ArrayList<String> data, Context context,int dataType) {
this.data = data;
this.context = context;
this.dataType = dataType;
inflater = LayoutInflater.from(context);
previousSelectedPosition = -1;
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
#Override
public ContentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_date_range_list_item,parent,false);
return new ContentViewHolder(view);
}
#Override
public void onBindViewHolder(ContentViewHolder holder, int position) {
String name = data.get(position);
holder.dateText.setText(name);
}
#Override
public int getItemCount() {
return data.size();
}
public class ContentViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private FrameLayout main;
private TextView dateText;
public ContentViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
main = (FrameLayout) itemView.findViewById(R.id.main_layout);
dateText = (TextView) itemView.findViewById(R.id.date_name);
}
#Override
public void onClick(View v) {
//Selected item color change
main.setBackground(ContextCompat.getDrawable(context,R.drawable.date_range_selected_item_background));
dateText.setTextColor(ContextCompat.getColor(context,R.color.colorPrimary));
if(itemClickListener!=null){
itemClickListener.onItemClick(v,this.getLayoutPosition(),dataType,getOldPosition());
}
}
}
public interface ItemClickListener{
public void onItemClick(View v, int position,int dataType,int oldPosition);
}
}
You basically want to make good use of your int flag, previousSelectedPosition, to keep track of the list item's position that was clicked, invoke notifyDataSetChanged(), and then set the flag as part of a conditional statement within onBindViewHolder() to update ViewHolder's views accordingly as they continuously get binded. Try the following changes:
ViewHolder's onClick():
#Override
public void onClick(View v) {
if (itemClickListener!=null) {
previousSelectedPosition = getAdapterPosition();
notifyDataSetChanged();
itemClickListener.onItemClick(v,this.getLayoutPosition(),dataType,getOldPosition());
}
}
RecyclerView.Adapter's onBindViewHolder():
#Override
public void onBindViewHolder(ContentViewHolder holder, int position) {
String name = data.get(position);
holder.dateText.setText(name);
if (previousSelectedPosition == position) {
main.setBackground(ContextCompat.getDrawable(context,R.drawable.date_range_selected_item_background));
dateText.setTextColor(ContextCompat.getColor(context,R.color.colorPrimary));
} else {
// TODO: Configure the FrameLayout and TextView here for initial runtime as well as back to default
}
}
... and yes, make sure to keep previousSelectedPosition initialized as -1 for the initial runtime just so the condition in onBindViewHolder() wouldn't matter until the flag updates (after a list item is clicked, that is).
Your OnClickListener does not work perfectly. You just need to implements your ViewHolder from you "ItemClickListener" interface. And add this line in onCreateViewHolder :
View view = inflater.inflate(R.layout.custom_date_range_list_item,parent,false);
ContentViewHolder cVh = ContentViewHolder(view);view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
cVh.onItemClick(v,this.getLayoutPosition(),cVh .dataType,cVh .getOldPosition());
}
});return cVh
I have a recyclerview that gets data as the realm results and then displays it as recyclerview item. My idea is that when a user clicks on an item a bottomsheet pops up and shows deatils.
Is there a way that in onBindViewHolder I can pass the data to bottomsheet and then in onClick method show bottomsheet? Thanks
Here's my code
public class EmployeeRVAdapterTable extends RecyclerView.Adapter<EmployeeRVAdapterTable.EmployeeViewHolderTable> implements RealmChangeListener {
private RealmResults<Predavanja> mEmployees;
public EmployeeRVAdapterTable(RealmResults<Predavanja> employee) {
this.mEmployees = employee;
mEmployees.addChangeListener(this);
}
#Override
public EmployeeViewHolderTable onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.table_row_item, parent, false);
return new EmployeeViewHolderTable(view);
}
#Override
public void onBindViewHolder(EmployeeViewHolderTable holder, int position) {
Predavanja predavanja = mEmployees.get(position);
holder.tablename.setText(predavanja.getPredmetPredavanja());
holder.tabletype.setText(predavanja.getRasponVremena());
holder.tablemjesto.setText(predavanja.getDvorana());
switch (predavanja.getPredavanjeIme()){
case("Predavanja,"):
holder.tableboja.setBackgroundResource(R.color.blue_nice);
break;
case("Auditorne vježbe,"):
holder.tableboja.setBackgroundResource(R.color.green_nice);
break;
case("Kolokviji,"):
holder.tableboja.setBackgroundResource(R.color.purple_nice);
break;
case("Laboratorijske vježbe,"):
holder.tableboja.setBackgroundResource(R.color.red_nice);
break;
case("Konstrukcijske vježbe,"):
holder.tableboja.setBackgroundResource(R.color.grey_nice);
break;
case("Seminar,"):
holder.tableboja.setBackgroundResource(R.color.blue_nice);
break;
case("Ispiti,"):
holder.tableboja.setBackgroundResource(R.color.purple_dark);
break;
}
}
#Override
public int getItemCount() {
return mEmployees.size();
}
public class EmployeeViewHolderTable extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView tablename, tabletype, tablemjesto;
RelativeLayout tableboja;
public EmployeeViewHolderTable(View itemView) {
super(itemView);
tablename = (TextView) itemView.findViewById(R.id.table_name);
tabletype = (TextView) itemView.findViewById(R.id.table_type);
tableboja = (RelativeLayout)itemView.findViewById(R.id.colorMe);
tablemjesto = (TextView)itemView.findViewById(R.id.table_mjesto);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Context context = view.getContext();
TextView info, info2,info3;
final BottomSheetDialog dialog;
View views = LayoutInflater.from(context).inflate(R.layout.bottom_sheep, null);
info = (TextView) views.findViewById(R.id.predavanjeImeDialog);
dialog = new BottomSheetDialog(context);
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
dialog.setContentView(views);
dialog.show();
}
}
#Override
public void onChange(Object element) {
notifyDataSetChanged();
}
}
The best approach is to get the position of the item and then get that item in the collection and use the data you want.
#Override
public void onClick(View v) {
int position= getAdapterPosition();
String detail= mEmployees.get(position).getDetail();
// Do what you want with the detail
}