Adding RadioButtons depending on array programatically - android

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());
// ...
});

Related

How to access view inside activity from adapter in android?

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

Picasso + RecycleView insert image in wrong view

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

adding checkmarks to recyclerview in android

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.

ListView: grouping items by section

Taking for example Gmail App, on my Navigation Drawer, I want a ListView that is grouped by section, similar to inbox, all labels.
Is this behavior achieved by using multiple ListView separated by a "header" TextView (which I have to build manually obviously), or is this section-grouped behavior supported by the Adapter or ListView?
Don't use multiple ListViews, it will mess things up for the scroll.
What you describe can be achieve by using only one ListView + adapter with multiple item view types like this:
public class MyAdapter extends ArrayAdapter<Object> {
// It's very important that the first item have a value of 0.
// If not, the adapter won't work properly (I didn't figure out why yet)
private int TYPE_SEPARATOR = 0;
private int TYPE_DATA = 1;
class Separator {
String title;
}
public MyAdapter(Context context, int resource) {
super(context, resource);
}
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public int getItemViewType(int position) {
if (getItem(position).getClass().isAssignableFrom(Separator.class)) {
return TYPE_SEPARATOR;
}
return TYPE_DATA;
}
#Override
public int getViewTypeCount() {
// Assuming you have only 2 view types
return 2;
}
#Override
public boolean isEnabled(int position) {
// Mark separators as not enabled. That way, the onclick and onlongclik listener
// won't be triggered for those items.
return getItemViewType(position) != TYPE_SEPARATOR;
}
}
You just have to implement your own getView method for a correct rendering.
I am not sure exactly how the Gmail app achieves this behavior, but it seems as though you should work on a custom adapter. Using multiple list views would not be a productive way to approach this problem, as one wants to keep the rows of data (messages) together in single list items.

Android: reusable adapter and different onclicklistener

I want to know if is it a good way to use the same adapter for more than one listview.
in my code i have many listviews and each one contains the same UL components like imageview and textview, so is it good to use `MyAdapter extends BaseAdapter` for each of them ? or it is better to make adapter for each one?
if i have to use one adapter, how to handle the different onclick actions for the button, imageview and textview for each listview ?
class MyAdapter extends BaseAdapter {
public MyAdapter() {
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return data.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}
}
It will make no difference resource-wise either way, as you will have to create a new instance of the adapter for each listview anyway. But trying to incorporate the features of two different adapters into one even just sounds overly complex. I would say for clarity of design, just make two different adapters. It'll make your life so much easier in the long run when it comes to debugging as well.
Keep in mind this is when the behaviors of each list are different, if the lists are supposed to function the same go ahead and use the same adapter for each.
Are you talking about reusing the instance of the adapter or its class? The class can be reused ad infinatum.
The instance, however, is safer not to be reused. The reason for this is you will likely have collsions or artifacts from the previous AdapterView. Adapter creation is menial, so why not just be safe and create a new one for each AdapterView?
This is a really good question I often struggle with. Seems so unnecessary duplicating so much adapter code just for different actions. I still struggle with this questions as a design issue, so my answer is not intended to provide an answer on that. However, for the part of the question about reusing the adapter or not, what I do if I wish to reuse a list/adapter is this:
For each type of list I create a global constant value to act as an identifier for that type of list. When I create a new instance of the adapter I supply the requestId/listTypeId to the adapter:
//first i create the constants somewhere globally
TYPE_ID_A = 0;
TYPE_ID_B = 1;
TYPE_ID_C = 2
//then i feed them to my adapter and set the clickListener on my list
mList.setAdapter(new MyListAdapter(mContext, listData, TYPE_ID_A));
mList.setOnItemClickListener(this);
In my adapter I set this typeId as a member variable and further then create a public function to return this id:
public class MyListAdapter extends ArrayAdapter<JSONArray> {
private final Context mContext;
private final JSONArray mItems;
private final int mListType;
//assign the values in the constructor of the adapter
public SearchListAdapter(Context context, JSONArray items, int listType) {
super(context, R.layout.item_filter_list);
mItems = items;
mContext = context;
mListType = listType;
}
//function to return the list id
public int getListType(){
return mListType;
}
}
Finally, inside my onClick listener I call this function inside my adapter to return the listTypeId which I then compare the global constants to identify what do to further:
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
MyListAdapter adapter = (MyListAdapter) adapterView.getAdapter();
int listType = adapter.getListType(); //get the listTypeId now
//now see which list type was clicked:
switch(listType){
case(TYPE_ID_A):
//to action for list A
break;
case(TYPE_ID_B):
//to action for list B
break;
}
}
This works for me but I dont think its great. If any one has another proper design pattern please let us know!

Categories

Resources