Android: Get Spinner Data from a Recycler view - android

I creating a feedback screen in an application in which user will have to select their opinion(displayed as Spinner) for each question(TextView).
Multiple questions & options will be displayed in a RecylcerView. On clicking submit button, I need to get all the data(Both Question and Selected spinner value) from recycler view and send to the server.
I saw the many suggesting to implement onItemSelectedListener on the Spinner. It will not suitable for me because Each spinner will have default value. Also user may not select each and every spinner.
How to get the value of each spinner(Opinion) & Textview(Question) inside ViewHolder of a RecyclerView

Step 1: You must have Model class say Question like below
public class Question {
String questionTitle;
List<Answer> answerList;
// getter/setter
}
public class Answer {
boolean isSelected;
boolean isDefault;
// getter/setter
}
Step 2: You must be passing list of Question to your adapter. And on item click simply set the answer as selected using answer.setSelected(true) for the particular question.
Step 3: Create a method in adapter class like below which will give you the selected answers for all the question.
public Map<String,Answer> getSelectedAnswers() {
Map<String,Answer> map = new HashMap();
for(Question question : questionList){
List<Answer> answers = question.getAnswerList();
Answer defaultAnswer;
for (Answer answer: answers){
if(answer.isSelected()){
map.put(question.questionTitle(),answer);
defaultAnswer = null;
break;
}
if(answer.isDefault()) {
defaultAnswer = answer;
}
}
if(defaultAnswer != null) {
map.put(question.questionTitle(),defaultAnswer);
}
}
return map;
}
Note : If step 3 want to avoid then keep a Map<String, Answer> selectionMap in the adapter itself where is key would be question and value would be a selected answer for that question, and keep updating on spinner item selection.
Step 4: At the time of calling API on submit button you need to traverse Map and then wrap into required fields what API expects.

Related

fetch and store data in recycler view from other activity selected list items in android

how to store only selected item list data in recycler view from other activity list.
i used this code -- successfully select data but don't know how to add only selected data items in new activity recycler view -
i used this code snippet ite working fine-
//
https://en.proft.me/2018/03/3/multi-selection-recyclerview/
StringBuilder stringBuilder = new StringBuilder();
for (ExcercisesSelectedModel.DataBean data : getList()) {
if (selectedIds.contains(data.getId()))
stringBuilder.append("\n").append(data.getName());
Change
public interface OnClickAction {
public void onClickAction();
}
to
public interface OnClickAction {
public void onClickAction(Item item);
}
In adapter class, on item click
receiver.onClickAction(item);
In Activity class
private List<Item> selectedItem = new ArrayList()
public void onClickAction(Item item) {
selectedItem.add(item);
}
Now use this selected item list in new activity.
Edit: Don't forget to use or rename the interface to Callback, something along the lines of ActivityCallback or OnClickCallback.
This is for the sake of naming conventions.
A simple flag isSelected in parent list will save your day.
So whenever user select/unselect the item just change the value of isSelected flag to true or false.
Now you have a final list from which you can easily identify selected items, simple store in separated list named selectedItemList.
At last use the selectedItemList and fill your Recycleview. Hope it make sense.

Alternative to HIGHLY FLAWED SPINNER CLASS in Android [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
in my current project i have dealt with spinner class which has been customized from almost every aspect possible. Thus i have gained some detailed aspects as i deal with it . So i will start with the part it shows why it is flawed.
1_There is no default listener that will be fired on pop up window/layout/dialog created-showed(layout inflated) event. There are some workarounds such as ontouch listener on spinner, then check if on touch finish happened in spinner area, then you know popup will be shown but still not reliable since you can fill popup with async task..
2_On item selected event does not fire when same index is selected again. This is really annoying since i may be updating the adapter depending on other conditions which will change current selection and list order etc... Of course there is a workaround way by creating own spinner class and adding it in xml like com.myproject.customspinner etc.....(Spinner : onItemSelected not called when selected item remains the same)
3_There is no working functional OnClickListener and OnItemLongTouchListener event for spinner.
4_Changing Spinner DropDown list divider element's attributes such as color requires more labor than changing all dropdrown and spinner' background views itself which is very absurd.
5_Spinner the name itself is very absurd =))).
So what can i use instead of Spinner? Which is best way to go?
You can create a custom spinner using ListPopupWindow to a TextView means when a TextView is clicked a ListPopupWindow open like spinner dropdown list and you can choose a element. If you need I will help you in that.
ListPopupWindow numberList;
TextView spDays;
ArrayList<Map<String, String>>() listTrans;
in oncreate() spDays.setonclicklistner(this);spDays.setText("Select");
setNumberListSpinnerView();
in onclick(){
when spDays clicked :- numberList.show();
}
void setNumberListSpinnerView() {
numberList= new ListPopupWindow(this);
numberList.setAnchorView(spDays);
numberList.setOnItemClickListener((new AdapterView.OnItemClickListener() {
#Override
getListItem();
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map map = listTrans.get(position);
spDays.setText(map.get("circle_name").toString());
circle_name = map.get("circle_name") + "";
circle_id = map.get("circle_id").toString();
circleList.dismiss();
Log.d("Circle id:", circle_id + "");
getRetails();
}
}));
}
void getListItem(){
String[] numbers = {"1","2","3","4","5","6"};
listTrans = new ArrayList<Map<String, String>>();
LinkedHashMap<String, String> tran = new LinkedHashMap<String, String>();
for (String number : numbers) {
tran.put("numbers", number);
listTrans.add(tran);
}
SimpleAdapter adapter = new SimpleAdapter(AddRetailSurvey.this, listTrans,
android.R.layout.simple_spinner_dropdown_item,
new String[]{"numbers"},
new int[]{android.R.id.text1});
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
numberList.setAdapter(adapter);
}
Check this code and modify it according to your requirement. If you found any problem I am here to help you. :)
Putting a simplified kotlin version of the accepted answer here, which may help. At first make a ListPopupWindow member in your Activity or other class-
private val listPopupView by lazy { ListPopupWindow(this) }
Then initialize it in the onCreate() method-
val dataList = arrayOf("item1", "item2", "item3", "item4")
listPopupView.setAdapter(ArrayAdapter(this, android.R.layout.simple_list_item_1, dataList))
listPopupView.setOnItemClickListener { _, _, position, _ ->
selectionTextView.text = dataList[position]
listPopupView.dismiss()
// do other things on selection
}
listPopupView.anchorView = selectionTextView
selectionTextView.setOnClickListener { listPopupView.show() }
And you are done!
This shows you how to replace Spinner with your own implementation. It's pretty simple, the important thing is to use a PopupWindow containing a list view to imitate Spinner's layout behavior.
https://www.androidcode.ninja/show-listview-as-drop-down-android/
This fixes the issues with weird event handlers in Spinner's implementation. It's also much easier to customize.
The only problem with this approach is that like Spinner, it still uses PopupWindow, which causes weird bugs in the system UI when you're in immersive/fullscreen mode. But it's easier to handle those bugs when you don't also have to deal with Spinner's specific issues.

ArrayList values changed without modification in android

I'm developing one Android Application.
I have one screen-A with show button. When user clicks on this show button, then I'm showing one dialog which contains Listview with checkboxes and two buttons submit , cancel.
editGroupUsersDialog = new Dialog(mcontext);
editGroupUsersDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
editGroupUsersDialog.setContentView(R.layout.edit_group_users_layout);
editGroupUsersDialog.setCanceledOnTouchOutside(true);
editGroupUsersDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0));
editGroupUsersDialog.show();
ArrayList<User> users = //got from the server;
Now I have taken one more static variable in the screnn-A and initialized with above users.
public static ArrayList<User> editUsers = users;
Now I have created adapter object and send the static users to the adapter.
ListView users_listview = (ListView)editGroupUsersDialog.findViewById(R.id.user_listView);
EditGroupUsersAdapter edit_group_users_adapter = new EditGroupUsersAdapter(
mcontext, edit_group_users);
users_listview.setAdapter(edit_group_users_adapter);
Now when the user check or uncheck any item in the dialog, then i'm setting the state of that item in the bean class User.
checkbox.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// add this line too for getting position of clicked
int pos = (Integer) v.getTag();
Log.v("Position is:", ""+pos);
Log.v("Befoer checkbox status is:", ""+GroupsAdapter.edit_group_users.get(position).isSelected());
if(!GroupsAdapter.edit_group_users.get(pos).isSelected()) {
//set the value to true in the user bean
GroupsAdapter.edit_group_users.get(pos).setSelected(true);
//checkbox.setChecked(true);
} else {
//set the value to false in the user bean
GroupsAdapter.edit_group_users.get(pos).setSelected(false);
//checkbox.setChecked(false);
}
}
});
Here i'm changing the state in static user variable only. i.e. GroupsAdapter.edit_group_users which is of type ArrayList
But the objects int non static variable users of type ArrayList is also changing.
I don't know what was the wrong I'm doing here.
The main purpose i'm taking another static variable of type **ArrayList is to store the modified User objects. But I don't want to change the original users which are available in users variable.
Please tell me how to solve my problem.
Thanks.
Both your static and non-static Arraylist are referencing to list of user.
Now say for some user A, you changes its state from X to Y. Then, this change will be reflected to both Arraylist because both Arraylist are pointing to same object, and you are changing the state of object.

Reset background color in Android ListView

In my QuestionsActivity, I am showing a question and bunch of answers. The question is displayed on TextView and answers are displayed on ListView that is composed of TextViews. There is an ActionButton named "Check Answer" and when it is clicked, it shows the correct answer by changing the background color of the TextView in the ListView.
The background changing code looks like this:
if (allAnswers.get(i).isCorrect())
{
mAnswerList.getChildAt(i).setBackgroundColor
(getResources().getColor(R.color.correct_answer_background));
return;
}
and now there are two Buttons at the footer section of this QuestionsActivity called PreviousQuestionButton and NextQuestionButton and they are basically navigation buttons between questions.
The problem is, when I go to the next question after clicking on "Check Answer" button, the answer background color doesn't go away and remains in the next question answer options. I tried invalidate(), refreshDrawableState() method of ListView but no luck!
This is the method which displays the answers for a given question:
private void showAnswers(int questionLocation)
{
int questionId = mAllQuestions.get(questionLocation).getQuestionId();
List<Answer> answers = mAnswerRepository.getAllByQuestionId(questionId);
mAnswerAdapter.clear();
for (int i = 0; i < answers.size(); i++)
{
mAnswerAdapter.add(mOptionLetters[i] + ". "
+ answers.get(i).getAnswerText());
}
mAnswerAdapter.notifyDataSetChanged();
}
My Question
What I want is that when I click on next or previous buttons, the background color of the correct answer in ListView should disappear so that next and previous question button can show non-selected answer options list to the user. Is there any method which resets ListView to a state which does not have any background applied?
For selected answer option, I am using mAnswerList.clearChoices() in order to unselect but it does not apply for correct answer background color.
Well, to reset the color you can very well hard-reset the adapter by creating a new one. So don't clear and add as that may keep the views in the state they were before. I am not too sure about this since I am not clearing or adding from an adapter, but always creating a new one to fulfill my new needs.
Anyway, another reason why things may not go in the direction you want is that the views may get recycled, since we're talking about a ListView. So if you want to highlight a list item, you should keep in the data model the information about highlight by initializing it to false and if the user selects one set the highlight state to true. I suppose the Answer class has as a minimum the following:
public class Answer {
private String data;
private boolean correct;
public String getData() {
return data;
}
public boolean isCorrect() {
return correct;
}
#Override
public String toString() {
return data;
}
}
So your adapter could look close to this - getView method is the most important to notice (don't forget to set to default background if the answer is incorrect or the adapter should not highlight correct answer):
public class MyAdapter extends ArrayAdapter<Answer> {
private boolean showCorrectAnswer;
private List<Answer> modelAnswers;
public MyAdapter(Context context, List<Answer> answers) {
super(context, android.R.layout.simple_list_item_1, answers);
this.modelAnswers = answers;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if(showCorrectAnswer && modelAnswers.get(position).isCorrect()) {
view.setBackgroundColor(getContext().getResources().getColor(R.color.correct_answer_background));
} else {
view.setBackgroundColor(getContext().getResources().getColor(R.color.default_background));
}
}
public void setShowCorrectAnswer(boolean showCorrectAnswer) {
this.showCorrectAnswer = showCorrectAnswer;
notifyDataSetChanged();
}
}
What you need to do is to keep a reference to this custom adapter and if you need to highlight the correct answer or not simply call setShowCorrectAnswer(true / false);. It will trigger a redraw and in the getView() it will decide what to do based on adapter state and correct answer.
Hope it make sense ... I wrote all this while drinking a beer :)
Basically, I agree with the answer from payeli - you should change the background of selected answer in the "next/previous question" button.
But then there is this question:
"Is there any method which resets ListView to a state which does not have any background applied?"
Answer to this (as far as I know) is: not directly. But there are two workarounds.
If you call notfiyDataSetChange, not all views are completely redrawn. If possible, just the appropriate values are changed. It's mainly for performance reasons. (Imagine having 1000 contacts with images and names dumped and redrawn)
So how can you deal with this? (Other then reseting the background in the onClick method) Since you said these items are answers for a question, I assume you are not concerned about performance because there won't be too many of them.
Then you can simply:
A) Create new instance of Adapter instead of changing data in the old one. When you switch adapters, all views in list are removed, so, no recycling can happen in the new adapter.
B) Create custom Adapter, override getView method and make sure every time view is requested, you return a new view, so no recycling can happen.
Again: this really isn't "performance friendly" and should not be used with big lists with a lot of items!
If you want to know more, feel free to ask in comments or read the reference of Adapter class, especially the parameter convertView of getView method. (http://developer.android.com/reference/android/widget/Adapter.html)
As per my understanding U need to change color of text view in one of the list view row i.e row containing correct answer.
If this is your problem then On Click of button simply clear list view and recreate list view. Not when list view is recreated then check out position of row which contains correct answer. After getting row position simply change color of text view.
For Ex: In your Adapter class check as fallows in your getView();
for ( int i = 0; i < position.length ; i++ )
{
if(position == your_required_position){
{
textview.setColor(Color.BLUE);
}else{
textview.setColor(Color.BLACK);
}
}
In the onClickListener of "Check Answer" button, you need to reset the color:
void onClick (View v){
..............
mAnswerList.getChildAt(currentQuestion).setBackgroundColor
(getResources().getColor(R.color.default_answer_background));
}
you can save the default background somewhere, and when you press next/previous question button you should apply that color. Example:
TypedArray array = getTheme().obtainStyledAttributes(new int[] {
android.R.attr.colorBackground,
android.R.attr.textColorPrimary,
});
int backgroundColor = array.getColor(0, 0xFF00FF);
int textColor = array.getColor(1, 0xFF00FF);
array.recycle();
I found this code online to get background and text color (you can just keep the background part), when the onClick activates just set the background of your view to "backgroundColor"

Bind RadioGroup inside MvxListView

I've got some very tricky problem. I already tried to search the web and even looked into the MvvmCross sources, but I don't seem to be able to figure it out.
I have an MvxListView with a custom Adapter. The reason is, that depending on the "DataContext" of the current ListItem, I want to display some different view.
The list itself represents some sort of questionnaire. So the items in the list are in the form of
new Question("do you need help?"){
new Answer("yes"),
new Answer("no"),
new Answer("maybe")
}
Now the answers shall be shown as a radio button list.
So in my custom adapter on "GetChildView", I retrieve the view with the radiogroup and then I
"just want to bind that group to my answers" --> so for each answer, there has to be a corresponding radiobutton.
I would love to have the "Answer" object as datacontext for each radiobutton.
radioButton.Bind("Checked", "Chosen"); // where "Chosen" is the boolean property on "Answer"
But it would already be fine if the "Question" object could be the datacontext that I bind to
radioGroup.Bind("CheckedRadioButtonId", "ChosenAnswer"); // where "ChosenAnswer" is an int property
on "Question"
So basically I want to bind my radiobutton to the MvxListItem.DataContext in code inside my customadapter.
But I just cannot figure out how to do that. :/
Can you please give me a hint?
Of course I would love to do the same with a list of checkboxes as soon as multiple answers would be allowed.
Setting the datacontext is easy: just set it :)
What you do is you create a ViewModel called something like QuestionViewModel, which has what you need as a separet ViewModel.
Then create some component to use in your View for the complete questionnaire. Below is some example code for a bindable component.
public class BindableLinearLayout : ClickableLinearLayout, IMvxDataConsumer, IMvxBindingContextOwner
{
public BindableLinearLayout(Orientation orientation, object dataContext)
: base(orientation)
{
BindingContext = new MvxBindingContext();
DataContext = dataContext;
}
public object DataContext { get { return BindingContext.DataContext; }
set { BindingContext.DataContext = value; }
}
public IMvxBindingContext BindingContext { get; set; }
}
In the Questionnaire View, create this component and assign the datacontext (in the above example as a parameter). Then you can create the binding in the normal way:
var bindings2 = layout.CreateBindingSet<BindableLinearLayout, ParagraphViewModel>();
bindings2.Bind(numberText.View).For(t => t.Text).To(vm => vm.Paragraph.Number);
bindings2.Apply();
This code is called for each element you add to the collection, eauch with its own Datacontext.
I known this code is not for a list adapter, but I hope this will give you enough hints how to do this yourself.

Categories

Resources