Th question: Can I re-use RadioButton objects over and over again in an child activity?
I have a parent activity and a child activity. In the child activity, I have a large number of radio button displayed in a UI. In order to provide databinding from the parent down to the child, I have created a class (below) which contains a collection of RadioButtons. To populate the child activity, I pass a reference to this class down to the child which then groups the radioButtons into RadioGroups and displays them. I do this because the checked status of each button is now automatically available in the parent class, without the need to transfer any data through bundles.
public class GeneralAttribute{
Activity mThis;
public class Gender { // Mutually exclusive members
String categoryDesc = "Gender of user";
RadioButton isUnspecified = initRadioButton("Unspecified", true);
RadioButton isMale = initRadioButton("Male" , false);
RadioButton isFemale = initRadioButton("Female" , false);
} ;
<....more subclasses....>
RadioButton initRadioButton(String str, Boolean b) { // Factory
float cLayoutWeight = 0.5f;
RadioButton rb = new RadioButton(mThis);
rb.setText (str);
rb.setChecked(b);
rb.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, cLayoutWeight));
return rb;
}
GeneralAttribute(Activity localThis){ // Constructor
mThis = localThis;
gender = new Gender();
handedness = new Handedness();
location = new Location();
}
}
In the parent activity i have:
public class Parent(...)
public GeneralAttribute mGeneralAttribute; // Member class of RadioButtons
public static SPIGestureLoggerActivity TopLevelActivity;// Reference to the parent activity
public void onCreate(Bundle savedInstanceState) {
TopLevelActivity = this; // Assign this to the reference
mGeneralAttribute = new GeneralAttribute(this); // Initialize the class of RBs
startActivity(child); // Start the child
In the child activity i have this:
radiogroup = new RadioGroup(this);
radiogroup.setOrientation(RadioGroup.VERTICAL);
radiogroup.addView(Parent.TopLevelActivity.mGeneralAttribute.gender.isUnspecified);
radiogroup.addView(Parent.TopLevelActivity.mGeneralAttribute.gender.isMale);
radiogroup.addView(Parent.TopLevelActivity.mGeneralAttribute.gender.isFemale);
Parent.TopLevelActivity.mGeneralAttribute.gender.isUnspecified.setChecked(true);
mLinearLayout.addView(radiogroup);
This works fine....the first time the child activity is displayed. The second time it is displayed I get an exception.
In summary, here is the chain of events:
create the class of RadioButtons,
pass them to the child,
add them to a new RadioGroup
collect user choices
finish the child acitivty (which should destroy the RadioGroups)
use the data in the parent,
start the child activity again,
attempt to add the RadioButtons to new RadioGroups...
...Exception.
I can avoid this problem, if I null the class and reconstruct it. However, I would like to re-show the choices made from the first viewing with the second viewing.
Ideas:
Are the radioButtons saving pointers to the non-existant RadioGroups from the first viewing?
Is there a way to re-assign the view parent on each radio button in the class?
P.S. You may ask why I'm not using XML. For one, I will have 100+ of these radio buttons and I think it will be too painful to manage through XML. For another, I just like working programmatically on these things.
make sure you remove all the radiobuttons from the all radiogroups. Basically your right the radiobuttons are saving pointers to the non-existant raidogroups and no there isn't isn't a way to reassigned without calling removeAllViews on all the radiogroups. The best place to do that will be the onDestroy if your sure thats being called.
Related
I am adding a layout on a Button click that layout contains a TextView and the 2 Spinners name spGrades and spMarks.
User can add multiple layout. So all I want is to get the value of Spinners on the TextView click . well I am getting of values of Spinner but I am facing following problems.
In Case1: Suppose there is just one layout added by the user let say
Student1MarksLayout then on the click of the TextView I am getting
the values of Spinners.
In Case 2: Suppose now user has added and other layout let say
Student2MarksLayout with same 2 spinners and a TextView . Now in
this case on the click listener of TextView I am getting the value
of spinners which are currently added. even though i click on the
TextView of the Student1MarksLayout. It brings the values of
Student2MarksLayout values.
Question:
So in fact I want to get the values of spinners differently from the
same view group in which the TextView click has been originated by the
user.
Please help me and any suggestion about this on how to maintain this .
On the function that creates the layout, add to final variables that hold the 2 Spinners. Then create the onClick function, and reference these 2 final variables. I have written a quick example (in a bit of shorthand)
void createLayout()
{
View v = inflate....
final Spinner a = v.findViewById...
final Spinner b = v.findViewById...
TextView t = v.findById...
t.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
int val = a.get....
}
});
}
So I am currently in FragmentA.java, a class which consists of different EditTexts and Checkboxes for users to press and input.
My question is, I have this reset button, how can I reset the entire fragment view? (e.g. an EditText will be set to empty string, or with the value 0, as it is when created).
P.S. of course I can set the editText/Checkboxes one by one programically, however there are quite a lot of them and other views, so I would like to know if there is a good way to reset all of them.
Let's break it down into steps:
1. Getting the references
How this is done depends on what you already have. If the fields are created in-code, it's easy: Just store the references in a List<CommonBaseType>.
If they are loaded from an XML Layout, there are multiple options. If you just want all views of certain type(s) to be reset, you can iterate through the view hierarchy by getting a reference to their holding ViewGroup (the layout) and iterate over the children with getChildCount() and getChildAt(int). Then, check the type of the child. If it's a ViewGroup, check it's children. If it's an EditText or CheckBox, add them to the list.
If you need more control and don't want all views to be reset, you can tag the ones you want with something. You can tag a view in XML by using the android:tag-attribute and find them after inflation using the View.findViewWithTag(Object)-method.
2. Resetting
Now that you have the references, you can reset them by simply iterating over the collection you made in step 1 and handle them depending on their type. Some pseudo code with that:
List<View> form_elements = findViewsToReset();
for (View element : form_elements){
if (element instanceof EditText){
((EditText) element).setText("");
} else if (element instanceof CheckBox){
((CheckBox) element).setChecked(false);
}
// and so forth...
}
Something like this will reset all fields in your form to a default-value, depending on their type.
3. Resetting back to their original values
If you want to reset the views to their original values, you should "index" those when the initial values are set (which might be directly after inflation, if you set the values via XML).
To do this, simply run through your list from step 1 and make a mapping from their ID to their value at that point:
List<View> form_elements = findViewsToReset();
Map<Integer, Object> default_values = new HashMap<>(form_elements.size());
for (View element : form_elements){
if (element.getId() == View.NO_ID){
// We have nothing to identify this view by...
continue;
}
// Store the default values away:
if (element instanceof EditText){
default_values.put(
element.getId(),
((EditText) element).getText()
);
} else if (element instanceof CheckBox){
default_values.put(
element.getId(),
((CheckBox) element).isChecked()
);
}
// and so forth...
}
Later when you want to reset the form-elements, you can just iterate the list again and get the default values from the map. Cast them depending on the type of field (EditText -> String, CheckBox -> Boolean, etz) and set the values.
Bonus: Nasty RadioGroup
Resetting a RadioGroup is simply archived by calling clearCheck() on it, which has the nasty side-effect of triggering the associated OnCheckedChangeListener (which you might not want, depending on what you're doing in the listener).
The simplest way around this is to un-register the listener before calling clearCheck() and re-registering it afterwards. This can be archived by overriding RadioGroup.clearCheck():
/**
* When {#link #clearCheck()} is called, the registered (if any) {#link android.widget.RadioGroup.OnCheckedChangeListener} will <b>not</b> be called.
* #author Lukas Knuth
* #version 1.0
*/
public class CustomRadioGroup extends RadioGroup {
private OnCheckedChangeListener checked_change_listener;
public CustomRadioGroup(Context context) {
super(context);
}
public CustomRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
// We need to store this ourselves, since there is no getter-method for the listener -.-
this.checked_change_listener = listener;
super.setOnCheckedChangeListener(listener);
}
#Override
public void clearCheck() {
// 1. unregister the listener:
super.setOnCheckedChangeListener(null);
// 2. Clear
super.clearCheck();
// 3. restore the listener like it was before:
super.setOnCheckedChangeListener(this.checked_change_listener);
}
}
I was wondering how to refer to an View if I create it programmatically.
I have to create new passenger Views with "Add Passenger" and respectively "Remove Passenger" buttons to my app. The promts are kept LinearLayouts called "#+id/passenger" which have two EditTexts called "#+id/passenger_name" and "#+id/passenger_weight". Those are then kept in a yet another parent LinearLayout called passenger_layout that can hold all the passenger LinearLayouts in a bunch
Adding new passengers is easy, but I have no idea how to refer to the newly created elements. I guess they get a identifier of some sort automatically? I'd prefer them to be "passenger_name%" and "passenger_weight%", where % is an index _passengerCount.
addPassenger.Click += delegate {
//Add to index
++passengerCount;
//Prep new passenger layout
var newLayout = new LinearLayout(Activity);
//Set LayoutParameters from the existing passenger LayoutParameters
newLayout.LayoutParameters = newPassenger.LayoutParameters;
//Prep the new EditTexts
var name = new EditText(Activity);
var weight = new EditText(Activity);
//Set the EditTexts' LayoutParameters from existing LayoutParameters
name.LayoutParameters = new ViewGroup.LayoutParams(passengerName.LayoutParameters);
weight.LayoutParameters = new ViewGroup.LayoutParams(passengerWeight.LayoutParameters);
//These cleary don't work :<
// name.Id = Resources.GetIdentifier( "passenger_name" + passengerCount, "id", Activity.PackageName);
// weight.Id = "passenger_weight" + passengerCount;
//Add EditTexts to the new passenger layout and then and then add the new passenger to the parent LinearLayout
newLayout.AddView(name);
newLayout.AddView(weight);
passengerLayout.AddView(newLayout);
Log.Debug(GetType().FullName, "Add clicked");
};
That is my click delegate to create a new passenger, but again even if I create them like this I don't know how I can find them later if I have to for example remove them or get the name or weight data.
How do I refer to programmatically created UI elements?
When you create a view, try giving it some ID, and holding that ID as a static reference somewhere.
Then, you could simply call the containing view's findViewById(MY_VIEWS_ID) and get the view.
Of course, alternatively, you could always hold a reference to the view you created somewhere in your code when you create it. If you're afraid of memory leaks, you could use WeakReference.
Hope this helps.
You could maintain a static variable within the Activity that will contain the UI element after it gets initialized.
Something like:
// Definition
private static TextView textViewToSave = null;
textViewToSave = // create the TextView programatically.
// Do stuff with the saved TextView
textViewToSave.setText("O Hai world!");
My app contains 25 edittexts. I am getting this 25 edittexts with the help of adapter class by giving count=25 and fitting in gridView by gridView.setAdapter(new TextAdapter(this)); in the activity class. So, the edittexts are dynamically generated. But the thing is I am unable to set the initial values in the edittexts. This is because the edittext objects are unavailable to set the values.
Suppose if I don't set any initial values in the edittexts and continue with my app. The same problem repeats while setting the values back in the edittexts which are entered in previous mode after changing the orientation. Because change in orientation creates new activity. Even I tried android:configChanges="orientation|keyboardHidden", but no use while I am setting the values back in the **onConfigurationChanged()**. Because I am setting the setContentView(); in the onConfigurationChanged() as I need the respective view, but still the edittext objects are unavailable to set their values.
Is there any solution to set back the values? If not, I am thinking(Might be completely wrong way, but as a newbie please go easy) to move the onCreate() method content to Application class. So the initial part goes to Application class including the creation of edittexts. and getting that edittext objects in the onCreate() method to set the values. Is it possible? Please suggest. Code snippet would be appreciated.
You will need to modify TextAdapter. Store the initial values in a String array, with the position of the String array element aligned to the position of the EditText in your GridView.
Pseudo-code (untested):
public class TextAdapter extends BaseAdapter {
String [] initial_value = {"Initial Value 1", "Initial Value 2", "Initial Value 3", ..., };
public View getView(int pos, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.edit_text_container, null);
}
(EditText) edtTemp = (EditText) view.findViewById(R.id.edit_text_id);
edtTemp.setText(initial_value[pos]);
}
}
I have a ListActivity with each row in the list containing 2 TextViews and 4 RadioButtons.
The 2 TextViews are populated from a SimpleCursorAdapter pulling data from a table in my database and the 4 RadioButtons are just placed into the xml code (with their ids being assigned to 4 RadioButton variables in the Java code.
Basically what I want to do is check the state of each radio button in each row to update my database accordingly.
How would I access each row of RadioButtons to check to see if they are checked or not?
I apologize if this is vague, I will try to add more detail if needed.
Many thanks.
private void processAttendance(){
int index = 0;
this.allStudentsCursor = mDbHelper.fetchEnrolledStudents(mRowId);
lvList=(ListView)findViewById(android.R.id.list);
this.allStudentsCursor.moveToFirst();
while (allStudentsCursor.isAfterLast() == false) {
this.radGroup = (RadioGroup) lvList.getChildAt(index).findViewById(R.id.attendanceGroup);
this.mAttended = (RadioButton)lvList.getChildAt(index).findViewById(R.id.presentRadio);
this.mLate = (RadioButton)lvList.getChildAt(index).findViewById(R.id.lateRadio);
this.mExcused = (RadioButton)lvList.getChildAt(index).findViewById(R.id.excusedRadio);
this.mMissed = (RadioButton)lvList.getChildAt(index).findViewById(R.id.absentRadio);
this.mStudentId = allStudentsCursor.getLong(allStudentsCursor.getColumnIndexOrThrow("_id"));
this.mResult = "";
if(mAttended.isChecked()){
this.mResult ="attended";
radGroup.clearCheck();
}
else if(mExcused.isChecked()){
this.mResult ="excused";
}
else if(mMissed.isChecked()){
this.mResult ="missed";
}
else {
this.mResult ="late";
}
Calendar cal = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
String date = dateFormat.format(cal.getTime());
this.mDbHelper.addAttendance(date, this.mResult, this.mStudentId, mRowId);
allStudentsCursor.moveToNext();
index++;
}
finish();
}
This is fundamentally flawed -- ListView doesn't inflate a different layout view hierarchy for each row, it keeps only enough needed to fill the screen. As one scrolls off the edge, it is re-used to show the data for the next one scrolling in.
The data inside of each row in the list must come from the data in the cursor. If it isn't, it will be lost when it scrolls off the screen.
You would have to loop over the Cursor.
myCursor.moveToFirst();
/* Loop over all items */
while (myCursor.isAfterLast() == false) {
//get radio button
RadioButton myRadioButton = (RadioButton) findViewById(R.id.radiobutton);
/*
Edit or Check myRadioButton
*/
myCursor.moveToNext();
}
This is a quick example showing just one RadioButton but you get the picture.
You can't. The views are recycled in a list view so if you have say 20 items in list you may only actually have 5 instances of RadioButton. Once the view is off the Screen it gets recycled.
What you need to do, is whenever a RadioButton is clicked, change the data behind the list that determines whether the radio needs to be checked.