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.
Related
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.
The answers in this thread are not helping in any way.
I have one requirement where I want to do something when user long presses on CheckBoxPreference. I am dynamically creating the CheckBoxPreference inside activity. I am using below piece of code to populate the view:
m_PreferenceScreen = getPreferenceManager().createPreferenceScreen(this);
m_PreferenceCategory = new PreferenceCategory(this);
m_CheckBoxPreference = new CheckBoxPreference[m_Cursor.getCount()];
int i = 0;
while (m_Cursor.moveToNext()) {
//Create checkbox instances here
m_CheckBoxPreference[i].setOnPreferenceChangeListener(this);
m_PreferenceCategory.addPreference(m_CheckBoxPreference[i++]);
}
setPreferenceScreen(m_PreferenceScreen);
On what I am trying to achieve?
I have few entries in database. I want to populate a list of checkboxes corresponding to each entry in database(I am able to do it with above code). Now my requirement is, when user long presses on any checkbox, it should do something(Say, open a dialog box).
Is this thing doable with above piece of code?
I will also appreciate any alternate solution. However, I would prefer going with the first approach.
I'm not sure if CheckBoxPreference has a long press component or not. You might want to evaluate your UX to see if you can replace the action with a different UI component. Perhaps it is a regular preference that when click opens your details as well as a checkbox that can be checked.
If this doesn't suit your needs you can manually create checkbox views that can automatically load/update the shared preference object. I believe the standard view checkbox has the ability to be long pressed. It might be something to sample out since I've never directly tried long pressing a checkbox. If you cannot you might need to create a checkbox and a separate component that contains the text to be long pressed.
I managed to do this. The below trick works for me.
ListView listView = getListView();
listView.setOnItemLongClickListener(this);
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
ListView listView = (ListView) parent;
ListAdapter listAdapter = listView.getAdapter();
Object obj = listAdapter.getItem(position);
if (obj != null && obj instanceof View.OnLongClickListener) {
View.OnLongClickListener longListener = (View.OnLongClickListener) obj;
return longListener.onLongClick(view);
}
return false;
}
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"
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.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I want to add value getting from text view of android to an existing array list.e.g. my current array list contain values Cricket,Football and by text view I want to add hockey in array list at last position ..then my array list become Cricket ,football,hockey. My array list of cricket and football is coming from previous activity.
But now it add only cricket and football but does not add hockey
How can I do it?
resultArrGame+=resultArrGame.add(txtGame.getText().toString());
This will definitely work for you...
ArrayList<String> list = new ArrayList<String>();
list.add(textview.getText().toString());
list.add("B");
list.add("C");
You're trying to assign the result of the add operation to resultArrGame, and add can either return true or false, depending on if the operation was successful or not. What you want is probably just:
resultArrGame.add(txt.Game.getText().toString());
you can use this add string to list on a button click
final String a[]={"hello","world"};
final ArrayAdapter<String> at=new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1,a);
final ListView sp=(ListView)findViewById(R.id.listView1);
sp.setAdapter(at);
final EditText et=(EditText)findViewById(R.id.editText1);
Button b=(Button)findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
// TODO Auto-generated method stub
int k=sp.getCount();
String a1[]=new String[k+1];
for(int i=0;i<k;i++)
a1[i]=sp.getItemAtPosition(i).toString();
a1[k]=et.getText().toString();
ArrayAdapter<String> ats=new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1,a1);
sp.setAdapter(ats);
}
});
So on a button click it will get string from edittext and store in listitem.
you can change this to your needs.
item=sp.getItemAtPosition(i).toString();
list.add(item);
adapter.notifyDataSetChanged () ;
look up ArrayAdapter.notifyDataSetChanged()