Error in Android's clearCheck() for RadioGroup? - android

I'm having an issue with RadioGroup's clearChecked(). I'm displaying a multiple choice question to the user and after the user selects an answer I check the answer, give him some feedback and then move to the next question. In the process of moving to the next question I clearCheck on the RadioGroup.
Can anyone explain to me why the onCheckedChanged method is called 3 times? Once when the change actually occurs (with the user changes), once when I clearCheck(with -1 as the selected id) and once in between (with the user changes again)?
As far as I could tell the second trigger is provoked by clearCheck. Code below:
private void checkAnswer(RadioGroup group, int checkedId){
// this makes sure it doesn't blow up when the check is cleared
// also we don't check the answer when there is no answer
if (checkedId == -1) return;
if (group.getCheckedRadioButtonId() == -1) return;
// check if correct answer
if (checkedId == validAnswerId){
score++;
this.giveFeedBack(feedBackType.GOOD);
} else {
this.giveFeedBack(feedBackType.BAD);
}
// allow for user to see feedback and move to next question
h.postDelayed(this, 800);
}
private void changeToQuestion(int questionNumber){
if (questionNumber >= this.questionSet.size()){
// means we are past the question set
// we're going to the score activity
this.goToScoreActivity();
return;
}
//clearing the check
gr.clearCheck();
// give change the feedback back to question
imgFeedback.setImageResource(R.drawable.question_mark); //OTHER CODE HERE
}
and the run method looks like this
public void run() {
questionNumber++;
changeToQuestion(questionNumber);
}

What I've discovered is that if an item is checked and you call clearCheck() on the radio group it will call onCheckedChanged twice. The first time with the id of the item that was checked and the second time with -1/View.NO_ID. IMHO, this is a bug and apparently it has been around since at least 1.6. See this google code bug report: http://code.google.com/p/android/issues/detail?id=4785
It seems to be that the only solution is to check the actual RadioButton.isChecked() and test if it is true or false. This sort of defeats the purpose of the onCheckedChanged returning the id of the item since you now have to either keep references to those buttons or call findViewById every time.
I doubt they will fix this since changing it would probably break existing code in unexpected ways.

I faced the same problem and i solved with other work-around.
Set CheckedChangeListener as NULL
Do your operation
Reset your OnCheckedChangeListener again
Code snnipet :
rdbGroup.setOnCheckedChangeListener(null);
rdbGroup.clearCheck();
rdbGroup.setOnCheckedChangeListener(checkedChangeListener);
Hope this will help ツ

I had similar problem. My solution:
in procedure:
public void onCheckedChanged(RadioGroup rGroup, int checkedId)
I check checkedId. It equals to -1 if you use clearCheck()
else it equals to selected radiogroup child

Related

Really strange behaviour in android firebaseRecycleAdapter checkbox

I have to following problem:
I have a FirebaseRecyclerAdapter with checkboxes. It looks like this:
Everything works fine until you click two checkboxes in a row. In this case: if I where to click on listItem 3 this happends:
It duplicates the behaviour in the previous checked listitem
The code that is responsible for this is:
listCheckbox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("boxPosition", String.valueOf(position));
if (listCheckbox.isChecked()) {
Log.i("boxChecked", String.valueOf(position));
mDoneListRef.child(list_id).child("ticked").setValue(Boolean.valueOf(true));
} else if (!listCheckbox.isChecked()){
Log.i("boxChecked", String.valueOf(position));
mDoneListRef.child(list_id).child("ticked").setValue(Boolean.valueOf(false));
}
}
});
I found it only happens when the else if statement is added. If I only work with the logic of the if statement everything works like expected (execpt that you cant set the value false because the missing else if / else statement).
I assume there is something wrong with the else if statement. So again, the problem only happends when the else if statement is added and you tick two checkboxes to the same value in a row.
Am I perhaps missing something to break to loop or something that resets the value of the position?
edit : I tried different onclickevents, creating and calling another databaseRefference in the clickevent. Neither of these called things seem to work. They all give the same result
After a lot of researching I found out that after the box is ticked, it should call notifyItemChanged(position); instead of notifyDataSetChanged();

Stopping Score from Counting if user clicks twice

I am learning how to code this game and I have noticed that once the answer entered is correct one can click on the answer and still add on the score. I was wondering how can i make sure that the answer is only entered once? if the answer is true for someone to get 1 score?
if(answer == q.getAnswer()){
scoreTxt.setText("Score: "+(putScore+1));
correct = true;
}else if(answer != q.getAnswer()){
setHighScore();
scoreTxt.setText("Score: 0");
There are several options. One example is disabling the button after it has been clicked in the OnClickListener:
button.setEnabled(false);
Don't forget to enable the button once moving on to the next question (I'm assuming your game has questions and answers).
You're marking a bool as true. Why not use it to make sure the check can only succeed once?
if(answer == q.getAnswer() && !correct) {

Android UI shows RadioButton as selected, although it is not

When my UI is recreated on orientation change, I use super.onCreate(savedInstanceState) and getLastNonConfigurationInstance() to access custom data I stored to fill the dynamic parts of my layout.
I have a RadioGroup which has two RadioButtons and is already defined in the XML file. The XML automatically makes the first one selected.
When an orientation change happens and the SECOND RadioButton is selected, everything seems to work fine; the second RadioButton is still selected in the UI.
But RadioGroup.getCheckedRadioButtonId() says the first RadioButton is selected. And I actually WANT the first one to be selected. But nothing changes when I call rb1.setChecked(true) - second one still shown as selected, and still the RadioGroup tells me the first one is selected (now it would make sense, but it's not shown).
This is REALLY strange behavior, does anyone have tips?
(edit)
Parts of my code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.itemselected);
(...)
radioGroupServingType = (RadioGroup) findViewById(R.id.radioGroupServingType);
(...)
RadioButton radioOwnServing = (RadioButton) findViewById(R.id.radioOwnServing);
RadioButton radioUseServing = (RadioButton) findViewById(R.id.radioUseServing);
radioOwnServing.setOnClickListener(this);
radioUseServing.setOnClickListener(this);
//FIXME WTF
Log.d("", radioOwnServing.isChecked()+"/"+radioUseServing.isChecked()+" own/use checked");
radioOwnServing.setChecked(true);
Log.d("", radioOwnServing.isChecked()+"/"+radioUseServing.isChecked()+" own/use checked");
If I select radioUseServing in the UI and change the orientation, the log says true/false own/use checked both times - although radioUseServing is shown as selected in the UI.
By the way, logcat also outputs
W/asset(4040): deep redirect failure from 0x0103003e => 0x02060007, defStyleAttr=0x0101007e, defStyleRes=0x0103001a, style=0x00000000
when changing the orientation, sometimes multiple times. I haven't found anything with google on what that means.
I already spoke with you on IRC, but I believe that if you get the view, and post a runnable to it that will call .setChecked(), this will in effect cause the setChecked call to occur at the proper time, and thus avoiding having called setChecked before the view hierarchy was ready for it.
Something like this:
final View myRadioButton = findViewById(R.id.myradiobutton);
myRadioButton.post(
new Runnable() {
#Override
public void run() { myRadioButton.setChecked(true); }
}
);

How to use Android Spinner like a drop-down list

It's taken me quite a while to get my head around the Android Spinner. After several failed implementation attempts, and after reading many questions partially similar to my own but without satisfactory answers, and some without any answers at all, e.g. here and here, I finally get that a "spinner" in Android isn't meant to be the same thing as a "drop-down list" from desktop apps, or a select in HTML. However, what my app (and I'm guessing the apps of all the other posters whose questions are similar) needs is something that works like a drop-down box, not like a spinner.
My two problems are with what I first considered to be idiosynchrasies the OnItemSelectedListener (I've seen these as separate questions on this site but not as one):
An initial selection of the first list item is triggered automatically without the user's interaction.
When the item that was already selected is selected again by the user, it is ignored.
Now I realise that, when you think about it, it makes sense for this to happen on a spinner - it has to start with a default value selected, and you spin it only to change that value, not to "re-select" a value - the documentation actually says: "This callback is invoked only when the newly selected position is different from the previously selected position". And I've seen answers suggesting that you set up a flag to ignore the first automatic selection - I guess I could live with that if there's no other way.
But since what I really want is a drop-down list which behaves as a drop-down list should (and as users can and should expect), what I need is something like a Spinner that behaves like a drop-down, like a combo-box. I don't care about any automatic pre-selection (that should happen without triggering my listener), and I want to know about every selection, even if it's the same one as previously (after all, the user selected the same item again).
So... is there something in Android that can do that, or some workaround to make a Spinner behave like a drop-down list? If there is a question like this one on this site that I haven't found, and which has a satisfactory answer, please let me know (in which case I sincerely apologise for repeating the question).
+1 to David's answer. However, here's an implementation suggestion that does not involve copy-pasting code from the source (which, by the way, looks exactly the same as David posted in 2.3 as well):
#Override
void setSelectionInt(int position, boolean animate) {
mOldSelectedPosition = INVALID_POSITION;
super.setSelectionInt(position, animate);
}
This way you'll trick the parent method into thinking it's a new position every time.
Alternatively, you could try setting the position to invalid when the spinner is clicked and setting it back in onNothingSelected. This is not as nice, because the user will not see what item is selected while the dialog is up.
Ok, I think I've come up with a solution for my own situation with the help of both David's and Felix' answer (I believe David's helped Felix', which in turn helped mine). I thought I'd post it here together with a code sample in case someone else finds this approach useful as well. It also solves both of my problems (both the unwanted automatic selection and the desired re-selection trigger).
What I've done is added a "please select" dummy item as the first item in my list (initially just to get around the automatic selection problem so that I could ignore when it was selected without user interaction), and then, when another item is selected and I've handled the selection, I simply reset the spinner to the dummy item (which gets ignored). Come to think of it, I should've thought of this long ago before deciding to post my question on this site, but things are always more obvious in hindsight... and I found that writing my question actually helped me to think about what I wanted to achieve.
Obviously, if having a dummy item doesn't fit your situation, this might not be the ideal solution for you, but since what I wanted was to trigger an action when the user selected a value (and having the value remain selected is not required in my specific case), this works just fine. I'll try to add a simplified code example (may not compile as is, I've ripped out a few bits from my working code and renamed things before pasting, but hopefully you'll get the idea) below.
First, the list activity (in my case) containing the spinner, let's call it MyListActivity:
public class MyListActivity extends ListActivity {
private Spinner mySpinner;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: other code as required...
mySpinner = (Spinner) findViewById(R.id.mySpinner);
mySpinner.setAdapter(new MySpinnerAdapter(this));
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> aParentView,
View aView, int aPosition, long anId) {
if (aPosition == 0) {
Log.d(getClass().getName(), "Ignoring selection of dummy list item...");
} else {
Log.d(getClass().getName(), "Handling selection of actual list item...");
// TODO: insert code to handle selection
resetSelection();
}
}
#Override
public void onNothingSelected(AdapterView<?> anAdapterView) {
// do nothing
}
});
}
/**
* Reset the filter spinner selection to 0 - which is ignored in
* onItemSelected() - so that a subsequent selection of another item is
* triggered, regardless of whether it's the same item that was selected
* previously.
*/
protected void resetSelection() {
Log.d(getClass().getName(), "Resetting selection to 0 (i.e. 'please select' item).");
mySpinner.setSelection(0);
}
}
And the spinner adapter code could look something like this (could in fact be an inner class in the above list activity if you prefer):
public class MySpinnerAdapter extends BaseAdapter implements SpinnerAdapter {
private List<MyListItem> items; // replace MyListItem with your model object type
private Context context;
public MySpinnerAdapter(Context aContext) {
context = aContext;
items = new ArrayList<MyListItem>();
items.add(null); // add first dummy item - selection of this will be ignored
// TODO: add other items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int aPosition) {
return items.get(aPosition);
}
#Override
public long getItemId(int aPosition) {
return aPosition;
}
#Override
public View getView(int aPosition, View aView, ViewGroup aParent) {
TextView text = new TextView(context);
if (aPosition == 0) {
text.setText("-- Please select --"); // text for first dummy item
} else {
text.setText(items.get(aPosition).toString());
// or use whatever model attribute you'd like displayed instead of toString()
}
return text;
}
}
I guess (haven't tried this) the same effect could be achieved using setSelected(false) instead of setSelection(0), but re-setting to "please select" suits my purposes fine. And, "look, Ma, no flag!" (Although I guess ignoring 0 selections is not that dissimilar.)
Hopefully, this can help someone else out there with a similar use case. :-) For other use cases, Felix' answer may be more suitable (thanks Felix!).
Look. I don't know if this will help you, but since you seem tired of looking for an answer without much success, this idea may help you, who knows...
The Spinner class is derived from AbsSpinner. Inside this, there is this method:
void setSelectionInt(int position, boolean animate) {
if (position != mOldSelectedPosition) {
mBlockLayoutRequests = true;
int delta = position - mSelectedPosition;
setNextSelectedPositionInt(position);
layout(delta, animate);
mBlockLayoutRequests = false;
}
}
This is AFAIK taken from 1.5 source. Perhaps you could check that source, see how Spinner/AbsSpinner works, and maybe extend that class just enough to catch the proper method and not check if position != mOldSelectedPosition.
I mean... that's a huge "maybe" with a lot of "ifs" (android versioning comes to mind etc.), but since you seem frustrated (and I've been there with Android many times), maybe this can give you some "light". And I assume that there are no other obvious answers by looking at your previous research.
I wish you good luck!
Here is an alternative solution to differentiate between any (intended or unintended) programmatic and user-initiated changes:
Create your listener for the spinner as both an OnTouchListener and OnItemSelectedListener
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
// Your selection handling code here
userSelect = false;
}
}
}
Add the listener to the spinner registering for both event types
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
This wouldn't handle the case in which the re-selection of the same item by the user doesn't trigger the onItemSelected method (which I have not observed), but I guess that could be handled by adding some code to the onTouch method.
Anyway, the problems Amos pointed out were driving me crazy before thinking of this solution, so I thought I'd share as widely as possible. There are many threads that discuss this, but I've only seen one other solution so far that is similar to this: https://stackoverflow.com/a/25070696/4556980.
Modifying the Spinner is useful if you want to have multiple selections simultaneously in the same activity.
If you only desire the user to have a hierarchical selection, for example:
What do you want to eat?
Fruit
Apples
Bananas
Oranges
Fast Food
Burgers
Fries
Hot dogs,
then the ExpandableListView might be better for you. It allows the user to navigate a hierarchy of different groups and choose a child element. This would be similar to having several Spinners for the user to choose from - if you do not desire a simultaneous selection, that is.
I worked through several of the issues mentioned in this thread before I realized that the PopupMenu widget is what I really wanted. That was easy to implement without the hacks and workarounds needed to change the functionality of a Spinner. PopupMenu was relatively new when this thread was started in 2011, but I hope this helps someone searching for similar functionality now.

Android RadioButton not able to unset using setChecked(false) method

If I set a radio button to be selected on the first time, it works fine. But if I unselect it by calling
((RadioButton) findViewById(R.id.ID)).setChecked(false);
then, later even if I try to make it selected by calling setChecked(true) will not work unless the user select it from the screen.
Have any one come across this? or is it only me?
if(Val != null){
if( ((RadioButton) findViewById(R.id.ID1)).getText().toString().trim().equals(Val))
((RadioButton) findViewById(R.id.ID1)).setChecked(true);
else if(((RadioButton) findViewById(R.id.ID2)).getText().toString().trim().equals(Val))
((RadioButton) findViewById(R.id.ID2)).setChecked(true);
}
else {
((RadioButton) findViewById(R.id.ID1)).setChecked(false);
((RadioButton) findViewById(R.id.ID2)).setChecked(false);
}
If the else part is executed atleast once then everything gets mess up.
When I step thro my debugger, I can see the execution goes in the correct path and setting it to true. It is getting executed only once, I checked that. And I am not resetting it back to false in any other part of the code.
I found the solution.
It is not possible to uncheck a particular radio button. You can only set the other item to true.
So to clear all the checked items, you should call the clearcheck() method on the RadioGroup.
So my else part is
else {
((RadioGroup) findViewById(R.id.ID0)).clearCheck();
}
Take one invisible radio button and check it. All other radio buttons of group will be unchecked automatically..

Categories

Resources