having a few dynamically inflated/added checkbox, when some are checked the rotate or minimize the app (for easy to trigger the case, turn on the 'Dont keep activity alive'), the restored UI view shows all checkbox checked.
When os do saveInstance we store the checked items in a list, and when the OS to restore the fragment, we get the list of checked items and when re inflate the checkbox row it calls either setChecked(true) or setChecked(false) based on the list.
But after that all the checkbox shows as checked, although in the debug it clearly shows only the checked ones are used 'true' and others are used 'false' with setChecked().
Anyone experienced the same, or knows why setChecked() on the individual checkBox instance does not do what is called?
itemList = [A, B, C, D, E]
checkedListSet = [A, B]
void insertOneRow(ViewGroup container, Item item) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemLayout = inflater.inflate(R.layout.item_row, null, false);
TextView txt = (TextView)itemLayout.findViewById(R.id.text);
txt.setText(item.toString());
container.addView(itemLayout, container.getChildCount());
CheckBox checkbox = (CheckBox)itemLayout.findViewById(R.id.checkbox);
if (checkbox != null) {
boolean isChecked = (checkedListSet.get(item) != null);
Log.i(“insertOneRow(), isChecked:"+ isChecked +", item:”+item);
checkbox.setChecked(isChecked); //<== trace shows only A and B are set with true
}
}
item_row.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="36dp"
android:orientation="horizontal"
>
<CheckBox
android:id="#+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:checked="false" />
<TextView
android:id="#+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout>
The reason all your checkboxes are getting checked is that they all have the same id. If either one of them is checked and they are recreated(either because of orientation change or reattaching a fragment), Android system tries to restore their state and it identifies them based on their ids. Hence all of them get checked.
android:saveEnabled flag tells Android system whether to save their state and try to restore later or not.
Since you already have a mechanism in place to restore their state setting android:saveEnabled to false works for you.
See this question where a similar thing happens with EditText : Why does Android change the value of EditTexts with same id?
still not sure why, but after put in android:saveEnabled="false" the same code start to work. does anyone know why it has to have android:saveEnabled="false"?
<CheckBox
android:saveEnabled="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
/>
checkedListSet.get(item) != null
This method will return true if there is an item in your list and it's not null.
You are assigning the result of this expression to your isChecked boolean.
Thus every time this expression returns true, your check box will be setChecked true.
I do not know what exact condition you want to check before setting the check box so i can't help you with that.
Related
Surely a simple question to ask but I've tried for hours and I can't seem to get the problem !
I have a DialogFragment which contains a
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/interval_input_layout"
style="#style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxStrokeWidth="0dp">
<AutoCompleteTextView
android:id="#+id/time_interval_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
tools:ignore="LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
This is the listener set on this AutoCompleteView
binding.untilInput.onItemClickListener =
AdapterView.OnItemClickListener { parent, view, position, id ->
when (position) {
0 -> {
binding.numberLayout.visibility = View.GONE
}
1 -> {
binding.numberLayout.visibility = View.GONE
}
2 -> {
binding.numberLayout.visibility = View.VISIBLE
}
}
}
While number layout is just a linear layout like this
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/number_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="16dp">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="For number of events"
android:textColor="?android:textColorSecondary" />
</androidx.appcompat.widget.LinearLayoutCompat>
At first the listener wasn't being called so I set it like this instead of directly calling the function setOnItemClickListener (don't know why that wasn't working) , Now the listener is being called , I even put a breakpoint and debugged it and its setting visibility but its not taking any effect and it does not cause any error so I can't seem to get the problem here
Using a DialogFragment I inflated the view layout with the use of databinding in the method OnCreateView & then in OnViewCreated I worked with my view and caused changes to UI and set listeners to it and again cause changes to UI
in OnCreateDialog I called both of these methods onCreateView (to get the view and set it to dialog) and onViewCreated to setup the view
I tried to find the view using findViewById just to ensure if the databinding was working fine and it was working fine but visibility still won't change and the view won't update !
I still have't gotten to the root of the problem but it seems you have to store the view inside a variable so you don't lose a reference to it when onClickListener is called and in my opinion just when the onClickListener is called getting the view from the binding becomes invalid and it should but the small problem is that I am not calling onDestroy to make the binding null because the binding was invalid at that time !
Now I am just storing a refrence to binding like this
val myvar = binding.monthLayout
and I am capturing the value of myvar inside the onClickListener rather than using the binding
I have a listview, each item has 2 checkboxes, I want one of these to be checked automatically if the other is checked (that is in the same item). But the result I have is that when I check the second checkbox in any item, it is the first checkbox of the first item that is checked! (and not in the same item)
The code of checkboxes in xml:
`
<CheckBox
android:id="#+id/checkBox4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CheckBox"
android:onClick="handler"
/>`
The code of the function handler:
`
public void handler(View v) {
CheckBox rb = (CheckBox) findViewById(R.id.checkBox3);
rb.setChecked(true);
}
`
Does anyone know how to solve it and indicate that the checkbox to check is the one in the same item (it has the same id no?) ?
The first problem is that you really don't need the handler() method at all. A CheckBox's default behavior is to check when it is clicked, without you calling setChecked(). setChecked() is typically used when you want to explicitly check or uncheck a CheckBox outside of the standard click-to-check/uncheck flow.
However, if you really want the handler method to work (and assuming all of your CheckBoxes call hanlder() on click), you may find you have better luck using the View that is passed to the function as so:
public void handler(View v) {
CheckBox rb = (CheckBox) v;
rb.setChecked(true);
}
The Context
My application architecture is very similar to the default called "Swipe Views" when you create a new android project.
One activity, 3 Fragments, you can swipe through them. It uses the support library.
everything is up to date.
The device I'm testing on is a galaxy nexus running android 4.1.2
Support library r11, and SDK versions min=14 target=16
The Problem
The second Fragment holds a bunch of EditText which contain various numerical values read from sharedPreferences. When the Activity is first created, everything is fine the EditViews all have their correct value, but if I modify one of the values to, for instance 555, and then I rotate the device, all the EditText are now displaying 555 !
There is only one setText in the whole program and it is displayed later in this post, I tried to remove it, as expected when starting the activity all the EditTexts display the default value (666), I modify one and rotate the device, they all display the new value!
The details
One activity with a SectionsPagerAdapter, a ViewPager and several fragments for each page.
In one of the fragments (ConfigurationFragment) in the onViewCreated method I dynamically create lines with and EditText and an ImageButton.
those lines are actually in a layout (xml file) which I inflate for each line to add, then I get the editText and call setText to set it's value to what it should be.
onViewCreated method:
public void onViewCreated(View view, Bundle savedInstanceState)
{
// populate ports list on view creation
ArrayList<String> listenPorts = mServerManagerThread.getListenPorts();
for (String port : listenPorts)
{
EditText v = ((EditText) addListenPortItem().getChildAt(0));
v.setText(port);
}
super.onViewCreated(view, savedInstanceState);
}
naturally the method getListenPorts returns the same list each time it is called (on startup and when the device is rotated)
what addListenPortItem basically does:
// Instantiate a new "row" view.
final ViewGroup newView = (ViewGroup) LayoutInflater.from(viewGroup.getContext()).inflate(itemResource,
viewGroup, false);
// Set a click listener for the "X" button in the row that will remove
// the row.
newView.findViewById(R.id.delete_button).setOnClickListener(
new OnRemoveClickListener(mContainer, viewGroup, newView, emptyListTextResource));
viewGroup.addView(newView);
viewGroup is a linearLayout which will hold the newly created views
itemResource is the following layout
layout of one line:
<?xml version="1.0" encoding="utf-8"?>
<!--
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:layout_marginTop="8dp"
android:divider="?android:dividerVertical"
android:dividerPadding="8dp"
android:gravity="right"
android:orientation="horizontal"
android:showDividers="middle" >
<EditText
android:id="#+id/editPort"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="666" >
</EditText>
<ImageButton
android:id="#+id/delete_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/action_remove_item"
android:src="#drawable/content_remove" />
</LinearLayout>
any idea?
I've been staring at this bug for a couple hours now and I really have no clue what's going on ... I'm starting to suspect a bug in the SDK or the support library, but I know from experience the bug is most often in my brain :)
I found the solution: I moved the code from onViewCreated to onViewStateRestored(..) so that filling the EditTexts with their value is done after the state has been restored.
The problem was that all the EditTexts have the same id so when restoring the state it was messing it up.
That's not a bug, so don't be worried. The problem is that if you rotate the screen the Activity and the Fragments will be recreated. onCreateView() will be called again and this mixes things up and therefore you have such a weird behaviour. You can fix this by overriding these thwo methods:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Read values from the "savedInstanceState"-object and put them in your textview
}
#Override
protected void onSaveInstanceState(Bundle outState) {
// Save the values you need from your textview into "outState"-object
super.onSaveInstanceState(outState);
}
Android version: 3.1 API version: Android 2.2 Device: Motorola MX604
I dynamically create a multi-select ListView of CheckedTextView items, and attach a OnItemClickListener to the ListView. In the onItemClick method of the listener, I invoke the isChecked method of CheckedTextView to determine if the associated checkbox is checked or unchecked. Simple enough.
The problem: When I select a previously unselected item, the isChecked method returns false. When I select a previously selected item, the method returns true. The checkbox icon itself checks and unchecks correctly.
Here is the layout for the CheckedTextView:
<CheckedTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:drawableLeft="?android:attr/listChoiceIndicatorMultiple"
android:paddingLeft="6dip" android:paddingRight="6dip"
/>
This is how I create the ListView:
private void createSortedChannelList() {
emptyViewContainer();
ListView sortedListView = new ListView(this);
sortedListView.setId(CHANNEL_LISTVIEW_ID);
sortedListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
sortedListView.setItemsCanFocus(false);
sortedListView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
CheckedTextView selectedItem = (CheckedTextView) view;
boolean isChecked = selectedItem.isChecked();
Log.e(mLogTag,"item clicked position = " + position + " isChecked = " + isChecked);
}
});
ArrayAdapter<Channel> listAdapter =
new ArrayAdapter<Channel>(this,R.layout.favorite_channel_list_select_channel_row,mAllChannels);
sortedListView.setAdapter(listAdapter);
for(int channelIndex = 0;channelIndex < mChannelIds.length;channelIndex++){
if(mSelectedChannelIds.contains(mChannelIds[channelIndex]))
sortedListView.setItemChecked(channelIndex, true);
}
addViewToViewContainer(sortedListView);
}
This is the log output that is produced when I select a previously unselected item:
09-23 09:08:59.650: item clicked position = 19 isChecked = false
and when I select a previously selected item
09-23 09:10:20.800: item clicked position = 18 isChecked = true
I have done an extensive search and I can only find one other report of similar behavior. This leads me to believe that the problem probably lies in my code, rather than the android class :p I have also looked at numerous examples that are set up in a similar fashion. Can anyone spot a problem?
thanks
PS This is my first post on any forum, so if I'm missing something that would be helpful to the readers of this post, please let me know.
I believe the code is behaving the way it should. Selecting a previously unselected method will invoke the click listener before changing the checked state of the item in the list. In other words, isChecked() won't return true for the previously unselected item until after the onClick() method is finished.
I've noticed that for me, at least, the behavior of when the state changes isn't consistent; on an emulator, isChecked() returned the pre-click state, but on a device it returned the post-click state.
I got around this by bypassing the "isChecked" altogether, and just looking at the state of the underlying object I am toggling, since that won't change unless I explicitly do so. However, this solution may depend on how your code is set up, and there may be other gotcha's that I am overlooking.
You should be using MultiChoiceModeListener for listening to checks. Here is the documentation
Just wanted to see if anyone knows if there's a radiogroup or radiobutton attribute or something else quick that will allow radio buttons to be unchecked when they're in checked mode. I'm looking to build functionality that works like a radio group (i.e. only one can be checked) but I also want them to be able to be all unchecked.
Maybe I don't get the question here, but this is something I wanted to do. I have a single activity that I use to classify many pictures. I am classifying using radio buttons. After the user checks one of the options he is allowed to switch with the next picture. I needed to clear the selection, when switching the picture, but decided not to create new activity.
So I initialize my radio group in the layout like that:
<RadioGroup
android:id="#+id/radio_selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<RadioButton
android:id="#+id/radio_true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="#string/true" />
<RadioButton
android:id="#+id/radio_false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="#string/false" />
</RadioGroup>
This initializes my radio group and both RadioButton are not selected initially. Afterwards when I change the picture I need to clear the selection (because user has not yet chosen for the new picture). I do like that:
RadioGroup radioGroup = (RadioGroup)findViewById(R.id.radio_selection);
radioGroup.clearCheck();
This is doing exactly what I need: making again none of the radio buttons being selected. I hope I understood the question and this will help somebody in the future.
You can use a CheckBox to mimic the functionality you want as shown below. The code assumes that you have two check boxes but you could have more than two.
public void onClick(View v) {
int id = v.getId();
if (id == R.id.checkBox1) {
// Toggle status of checkbox selection
checkBox1Selected = checkBox1.isChecked();
// Ensure that other checkboxes are not selected
if (checkBox2Selected) {
checkBox2.setChecked(false);
checkBox2Selected = false;
}
else if (id == R.id.checkBox2) {
// Toggle status of checkbox selection
checkBox2Selected = checkBox2.isChecked();
// Ensure that other checkboxes are not selected
if (checkBox1Selected) {
checkBox1.setChecked(false);
checkBox1Selected = false;
}
}