Is there a way to make the RecyclerView have checkmarks like in ListView? Most of the example i see don't have checkmark-like features. They instead highlight the row when the user clicks on an item in the list which is not the behavior i desire. Please any suggestions? Thanks
I would do it like this :
First, Have a SparseBooleanArray in your adapter that you initialize in the constructor. SparseBooleanArrays map integers to booleans so you can know which view has been selected and which hasn't.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.DataViewHolder> {
private SparseBooleanArray selectedItems;
public RecyclerAdapter(Context context, List<Data> dataList){
selectedItems = new SparseBooleanArray();
}
The ViewHolder implements a ClickListener:
public class DataViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public DataViewHolder(View itemView) {
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// the condition below returns false if the item isn't in the array.
if (selectedItems.get(getPosition(), false)) {
//if it is in the array we delete it. So clicking a second time on an item will uncheck it.
selectedItems.delete(getPosition());
((CheckBox)v.findViewById(R.id.checkbox)).setChecked(selectedItems.get(position, false));
}
else {
selectedItems.put(getPosition(), true);
// here get a ref to the view checkbox and *check* it
((CheckBox)viewHolder.itemView.findViewById(R.id.checkbox)).setChecked(true);
}
}
}
At this point you will have multiples items with a checked checkbox in your recyclerview since views are recycled. To prevent this and have only the checkboxes you want to be checked change your onBindViewHolder method so it checks or unchecks the checkbox at bind time :
#Override
public void onBindViewHolder(DataViewHolder viewHolder, int position) {
((CheckBox)viewHolder.itemView.findViewById(R.id.checkbox)).setChecked(selectedItems.get(position, false));
}
I didn't test it but I've almost the same code and it works. If you have a problem let me know.
Related
I want to add radio buttons to a question as the options for answering, but these have to be added on the code because the amount of options depends on each item. These as the multiple choice answer options aren't always limited to one option selected, that can change also sometimes 2 or more options are required to be selected.
I'm doing this in a recyclerView, is there a way to add them without being in a radio group in order to be able to select more than one button when required?
You can do it very easily with android's built-in dialogs. See here is Android's official documentations.
If you don't mind using checkboxes, I'd probably go for that approach.
You could introduce an Answer model class and add a Checkbox with an OnCheckedChangeListener to the RecyclerView.Adapter's ViewHolder:
public class Answer {
private String text;
private boolean isChecked;
// Constructor, Getters, Setters
}
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {
private static final int MAX_ANSWERS = 2;
private List<Answer> mData;
private Context context;
MyRecyclerViewAdapter(List<Answer> data, Context context) {
this.mData = data;
this.context = context;
}
// ...
static class ViewHolder extends RecyclerView.ViewHolder {
CheckBox checkAnswer;
TextView myTextView;
ViewHolder(View itemView) {
super(itemView);
checkAnswer = itemView.findViewById(R.id.check_answer);
myTextView = itemView.findViewById(R.id.txt_answer);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Answer answer = mData.get(position);
holder.myTextView.setText(answer.getText());
holder.checkAnswer.setOnCheckedChangeListener((v, isChecked) -> {
answer.setChecked(isChecked);
// Notify adapter so that onBindViewHolder is called
notifyDataSetChanged();
});
// Check whether maximum number of answers is reached
if (mData.stream().filter(Answer::isChecked).count() >= MAX_ANSWERS) {
holder.checkAnswer.setEnabled(holder.checkAnswer.isChecked());
} else {
holder.checkAnswer.setEnabled(true);
}
}
// ...
}
After creating a list of answers, it can be passed to the RecyclerView.Adapter.
(For further information on how to update the data, please refer to the official documentation)
List<Answer> answers = new ArrayList<>();
answers.add(new Answer("Answer 1"));
answers.add(new Answer("Answer 2"));
recyclerView.setAdapter(new MyRecyclerViewAdapter(answers, this));
//...
Whenever an answer is submitted, you can filter the list for checked answers:
submitButton.setOnClickListener(v -> {
List<Answer> selected = answers.stream().filter(Answer::isChecked).collect(Collectors.toList());
// ...
});
Does anyone knows how to access adapter imageView inside activity to hide the view. Please specify any example.
I hope this will work for you.
By using SharedPreferences we can easily hide the view from activity or fragment.
Save flag in SharedPreferences i.e true from activity.
If you are using Recyclerview then in onBindViewHolder method check condition
if(flag==true){
holder.yourView.setVisibility(View.GONE);
}else{
holder.yourView.setVisibility(View.VISIBLE);
}
Go to onBindViewHolder of the adapter and take the id of your imageview and code like this
holder.mImgVw.setVisibility(View.GONE);
You should not directly interact with the ImageView, instead you can use notifyItemChanged() to update the ImageView state in the Adapter. But, you need to slightly modify your Adapter code by adding a flag in your model data or using SparseBooleanArray as a mechanism to saving the ImageView state.
Here the example:
public class Adapter ... {
private SparseBooleanArray mSelectedItems;
private List<YourModel> mItems;
public Adapter(List<YourModel> items) {
mItems = items;
mSelectedItems = new SparseBooleanArray();
}
...
public void onBindViewHolder(....) {
int itemPosition = viewHolder.getAdapterPosition();
YourModel item = items.get(itemPosition);
boolean visible = mSelectedItems.get(itemPosition);
viewHolder.imageView.setVisibility(visible? View.VISIBLE: View.GONE);
...
}
public void setItemVisibilityByPosition(int position, boolean visible) {
mSelectedItems.put(position, visible);
notifyItemChanged(position);
}
}
You can change the image visibility with:
// Assume the mAdapter is your Adapter
mAdapter.setItemVisibilityByPosition(5, true);
I have a ViewHolder with an OnClickListener, which sends the click over an Interface to the underlying Activity. The only information i send is the AdapterPosition. In the Activity i then get the information out of the Activitie's reference to the ArrayList (which also fills the Adapter), using the position i get passed from the Adapter. I then use this to open a new Activity where i show this data.
Now, this works, but i just came to mind that this could cause problems, if i don't properly call notifyDataSetChanged/ItemInserted/Removed etc. and the Adapter shows a different List than the actualy up-to-date List in the Activity. I am right about this? Should i get the values out of the Objects IN the Adapter and pass them to the Activity or is my approach correct?
ViewHolder:
public class ExampleViewHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public TextView mTextViewCreator;
public TextView mTextViewLikes;
public ExampleViewHolder(View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.image_view);
mTextViewCreator = itemView.findViewById(R.id.text_view_creator);
mTextViewLikes = itemView.findViewById(R.id.text_view_likes);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mListener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
mListener.onItemClick(position);
}
}
}
});
}
}
MainActivity OnItemClick implementation:
#Override
public void onItemClick(int position) {
Intent detailIntent = new Intent(this, DetailActivity.class);
ExampleItem clickedItem = mExampleList.get(position);
detailIntent.putExtra("imageUrl", clickedItem.getImageUrl());
detailIntent.putExtra("creatorName", clickedItem.getCreator());
detailIntent.putExtra("likeCount", clickedItem.getLikeCount());
startActivity(detailIntent);
}
The answer is simple: Always make sure that you notify the adapter about changes.
Otherwise its behavior becomes unpredictable.
Apart from that, your code seems to be fine
My personal preference is that you send the model to the activity, that's what it's interested in, it doesn't care about your adapter position, and even if in the future your list became an expandable list or even a ViewPager, all the Callback (Activity) wants is the model to start the new screen.
You can change your listener method to
onItemClick(ExampleItem item)
and then you can set your listener when onBindViewHolder is called.
how to get selected items in recyclerview with checkbox
for example: item 1,2,3 and 4 are checked. when i clicked the button create it will toast make text the item selected with their name.
This is my viewholder
public class UserViewGroupHolder extends RecyclerView.ViewHolder {
#Bind(R.id.image_account)
CircleImageView mImageAccount;
#Bind(R.id.text_contact_email)
TextView mTextContactEmail;
#Bind(R.id.text_contact_name)
TextView mTextContactName;
#Bind(R.id.check_box_user)
CheckBox mCheckBoxUser;
public UserViewGroupHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public void bind(final Account account, final User contact) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
mTextContactEmail.setText(contact.getEmail());
mTextContactName.setText(contact.getDisplayName());
}
}
And this is my Activity with onBindViewholder
#Override
public void onBindViewHolder(final UserViewGroupHolder holder, int position) {
holder.bind(mAccount, mUsers.get(position));
}
And this is my Activity with onBindViewholder
I suppose you meant Adapter, not Activity.
mTextContactEmail.setText(contact.getEmail());
mTextContactName.setText(contact.getDisplayName());
should be inside bind(...)
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
}
Must be set not on itemView in Holder, but on "Create" button in options menu.
You can obtain checked state of particular view by few means. One* is to keep in User class boolean field "checked" with getter/setter, and write function with for loop on users list kept in adapter, returning list of users with checked state "true".
Naturally, checked state of user have to be changed in bind(...).
Toast with names for create button click is easy-peasy right now: your new adapter's function returns list of checked users (can return list of Strings with their names as well).
*Notice, this could make User class mutable if it was immutable before.
Oww and you CAN'T base your checked state knowledge on Holders or Views, because they're reusable. If there were many users (more than 10), such state would work on few ListView items at once, which is really poor.
I have a simple adapter
public class ConversationListAdapter extends
RecyclerView.Adapter<Conversation.ViewHolder> {
private List<Conversation> items;
private Activity activity;
public ConversationListAdapter(Activity activity, List<Conversation> items) {
super();
this. items = items;
this.activity = activity;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
Conversation conversation = mItems.get(i);
viewHolder.name.setText(conversation.getName());
if ( conversation.getUrl() != null) {
Picasso.with(activity.getApplicationContext()).load(conversation.getUrl())
.into(viewHolder.imageView);
}
}
and a basic
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {..}
}
Now in the fragment as always:
mRecyclerView.setAdapter(new ConversationAdapter(getActivity(), mItems));
Now Im calling my rest api to get the data and the first time it works great everything is where it should be (let's say in c there is only 2 items and the order is conv1 and conv2)
private void handleResult(List<Conversation> c) {
items.clear()
items.addAll(c)
adapter.notifyDataSetChanged()
}
But... now if I refresh for example and the data in the List comes in a different order (conv2 and then conv1) after the adapter.notifyDataSetChanged() both of my imageView in the list have the same pictures.. ! But the textView however has the right text
This only happens with view filled with Picasso and cannot understand why
Could someone help me on this ?
you use if condition in any adapter you also have to set else part of it. i also don't know exactly why this is happen that if condition is given it takes same condition for child which are not match this, may be a bug in android. please try else part of it also. may be this work for you.
You have to replace your items in your adapter or create a new adapter with the new items
1st solution:
private void handleResult(List<Conversation> c) {
mRecyclerView.setAdapter(new ConversationAdapter(getActivity(), c));
}
2nd solution:
private void handleResult(List<Conversation> c) {
adapter.setList(c);
adapter.notifyDataSetChanged();
}
And don't to forget to create setList(List<Conversation> c) method in your Adapter