I'm trying to create a RadioGroup within an Android layout where the child RadioButtons are stretched to evenly fill the entire width of the RadioGroup. However, I've encountered some unexpected behaviour when trying to do this with RadioButtons which have been added programmatically from code. First some background...
What does work
I started with a simple layout based on a RelativeLayout which contains a large TextView and a RadioGroup at the bottom.
The main.xml layout file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView android:text="Some text"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentTop="true"
android:layout_above="#+id/radio_group"
android:gravity="center"
android:background="#android:color/holo_green_dark"
/>
<RadioGroup android:id="#+id/radio_group"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:background="#android:color/holo_blue_dark">
<RadioButton android:text="Option 1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:background="#android:color/darker_gray"
android:button="#android:color/transparent"
android:padding="10dp"
android:gravity="center"
android:layout_margin="2dp"/>
<RadioButton android:text="Option 2"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"
android:background="#android:color/darker_gray"
android:button="#android:color/transparent"
android:padding="10dp"
android:gravity="center"
android:layout_margin="2dp"/>
</RadioGroup>
</RelativeLayout>
which produces the following layout at runtime:
You can see that the use of android:layout_width="wrap_content" and android:layout_weight="1" in both RadioButtons stretches them to evenly fill half of the enclosing RadioGroup each. So far so good.
What doesn't work
However, the requirement I have is to dynamically create RadioButtons within this layout at runtime based on business logic rather than always using the the two statically included in the layout - sometimes I might need two buttons, sometimes four etc.
To implement this I removed the RadioButtons from my main.xml layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView android:text="Some text"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentTop="true"
android:layout_above="#+id/radio_group"
android:gravity="center"
android:background="#android:color/holo_green_dark"
/>
<RadioGroup android:id="#+id/radio_group"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:background="#android:color/holo_blue_dark"/>
</RelativeLayout>
...and created a separate _radio_button.xml_ layout for my RadioButton:
<?xml version="1.0" encoding="utf-8"?>
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:layout_weight="1"
android:background="#android:color/darker_gray"
android:button="#android:color/transparent"
android:gravity="center"
android:padding="10dp" />
In my activity I now add the RadioButtons programmatically:
public class TestRadioActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create an inflater to inflate our buttons
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Create the layout params for our buttons
LinearLayout.LayoutParams layoutParams = new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1f);
RadioGroup group = (RadioGroup) findViewById(R.id.radio_group);
// Add button one
RadioButton button = (RadioButton) inflater.inflate(R.layout.radio_button, null);
button.setText("Option 1");
group.addView(button, layoutParams);
// Add button two
button = (RadioButton) inflater.inflate(R.layout.radio_button, null);
button.setText("Option 2");
group.addView(button, layoutParams);
}
}
Note how both the _radio_button.xml_ file and the activity specify a layout width of WRAP_CONTENT and a layout weight of 1 to evenly distribute the buttons as in the original main.xml.
However, the layout seems to get rendered ignoring the layout weight with the buttons butted up on the left of the radio group:
As has been suggested elsewhere, I also tried setting the width of the RadioButtons to 0 in the LayoutParams (apparently this can cause the layout weight to be interpreted slightly differently), but this causes the RadioButtons not even to be rendered:
Can any advise how to get RadioButtons to evenly fill the entire width of the containing RadioGroup when added programmatically? Is there anything obvious I'm missing?
When you set a layout weight, you should use fill_parent as layout width. Then you shouldn't use LinearLayout.LayoutParams but RadioGroup.LayoutParams, as you're adding radio buttons to a RadioGroup, not to a simple LinearLayout.
Finally, as you use the inflater to "build" the radio button, the XML file of the radio button already has the layout params picked from the XML file, so I think you should just call the addView method that takes only the view to add as parameter (that is addView(View v)) and change the layout_width to fill_parent.
Note that, if you'll need to reference the variable "button" in the code, i.e. add a click listener, you'll add the listener only to the last created button. You'll have to create a RadioButton object for each RadioButton you will add to the RadioGroup (button, button1, button2, etc).
FYI, to do it without any xml at all
RadioGroup rgrp = new RadioGroup(context);
rgrp.setLayoutParams(new RadioGroup.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
rgrp.setOrientation(LinearLayout.HORIZONTAL);
mAccent = new RadioButton(context);
mAccent.setText("Accent");
mAccent.setLayoutParams(new RadioGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1f));
rgrp.addView(mAccent);
mGhost = new RadioButton(context);
mGhost.setText( "Ghost");
mGhost.setLayoutParams(new RadioGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1f));
rgrp.addView(mGhost);
mFlam = new RadioButton(context);
mFlam.setText( "Flam");
mFlam.setLayoutParams(new RadioGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1f));
rgrp.addView(mFlam);
layout.addView(rgrp);
I ran into this issue too, I used the RadioGroup.LayoutParams with weight defined. However I also found once I'd created programatically the buttons weren't responding to touch so set clickable and enabled to true and that fixed things.
private RadioButton createMyTypeRadioButton(MyType type){
//create using this constructor to use some of the style definitions
RadioButton radio = new RadioButton(this, null, R.style.MyRadioStyle);
RadioGroup.LayoutParams layoutParams = new RadioGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
radio.setLayoutParams(layoutParams);
radio.setGravity(Gravity.CENTER);
//tag used by the setOnCheckedChangeListener to link the radio button with mytype object
radio.setTag(type.getId());
//enforce enabled and clickable status otherwise they ignore clicks
radio.setClickable(true);
radio.setEnabled(true);
radio.setText(type.getTitle());
return radio;
}
private void updateMyTypesUi() {
//populate RadioGroup with permitted my types
for (int i = 0; i < myTypes.size(); i++) {
MyType type = myTypes.get(i);
RadioButton radioButton = createSwapTypeRadioButton(type);
myRadioGrp.addView(radioButton);
}
myRadioGrp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton checkedType = (RadioButton) group.findViewById(checkedId);
String idOfMyTypeChecked = (String) checkedType.getTag();
//do something with idOfMyTypeChecked
}
});
}
Related
I have this XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/button_bar"
style="?android:buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#color/md_green_400" />
<Button
android:id="#+id/action_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#color/md_green_400" />
</LinearLayout>
It´s just a buttonBar with two borderless buttons. It works. However, I don't want that, I need to inflate these buttons from a JSONArray. So I did this:
for (int b = 0; b < buttons.length(); b++) {
final JSONObject button = buttons.getJSONObject(b);
LinearLayout buttonBar = (LinearLayout) child.findViewById(R.id.button_bar);
View buttonChild = getLayoutInflater().inflate(R.layout.flat_button, null);
Button action = (Button) buttonChild.findViewById(R.id.action_button);
action.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {}
});
action.setText(button.getString("descricao"));
action.setTextColor(Color.parseColor(button.getString("text_color")));
buttonBar.addView(buttonChild);
}
It works too, but the buttons get a left alignment. I want they justified.
Why it works when I let them fixed but not when I inflate them?
OBS: The "button_bar" is A XML just with a LinearLayout and the "ActionButton" is just a XML with a Button.
This is the root of your problem:
View buttonChild = getLayoutInflater().inflate(R.layout.flat_button, null);
If you don't provide the parent view to the inflate() method, any LayoutParams attributes (e.g. layout_gravity) will be discarded since the parent is the one to interpret those attributes.
You can fix this by changing it to:
View buttonChild = getLayoutInflater().inflate(
R.layout.flat_button, buttonBar, false);
Which will give it the parent you're attaching it to, but not attach it to the hierarchy yet (you do that below with addView()).
I want to align vertically the CheckBox and their text/labels.
I add checkbox programmatically like this:
private LinearLayout ProposalLayout;
ProposalLayout = (LinearLayout) findViewById(R.id.ll_proposals);
this.ProposalLayout = ProposalLayout;
Iterator<Proposal> iter = q.getListProposals().iterator();
while (iter.hasNext()) {
Proposal p = iter.next();
CheckBox cb = new CheckBox(activity);
cb.setText(p.getProposal());
cb.setId(p.getId());
ProposalLayout.addView(cb);
}
Here is my layout:
<LinearLayout
android:id="#+id/ll_proposals"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<CheckBox
android:layout_width="wrap_content"
android:layout_height="60dp"
android:text="New CheckBox"
android:id="#+id/checkBox" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="70dp"
android:text="New CheckBox"
android:id="#+id/checkBox2" />
</LinearLayout>
The CheckBox added in the layout are good. The first one added programmatically is great but the others are not vertically aligned (their labels).
EDIT:
It's perfectly aligned when I replace:
cb.setText(p.getProposal());
by:
cb.setText("test test test");
The problem was in the XML file, there was a line break, I just didn't see it in the logs.
Define layout params for your checkbox and then set them as parameter. First the layout params:
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
Then add the view:
ProposalLayout.addView(cb, params);
Try this..
set the height of the both CheckBoxs to wrap_content in XML
android:layout_height="70dp"
to
android:layout_height="wrap_content"
You can use solution suggested by Thomas R. It should work. Or you can try mine:
...
while (iter.hasNext()) {
...
cb.setParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
...
}
And add
<LinearLayout
...
android:gravity="left">
so I'm pretty new to Android Development, and I have the following situation:
<LinearLayout
android:id="#id/child_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="">
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="">
</LinearLayout>
The problem is that I have to repeat this Horizontal LinearLayout an unknown amount of times, dynamically adding the text fields of the TextView and Button for each new instance. Each new instance of the Horizontal LinearLayout has to be added to an existing Vertical LinearLayout.
So the initial Vertical LinearLayout:
<LinearLayout
android:id="#id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
Then based on the dynamic implementation:
<LinearLayout
android:id="#id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#id/child_1">
</LinearLayout>
<LinearLayout
android:id="#id/child_2">
</LinearLayout>
</LinearLayout>
Is there any way that this can be done as I described? The XML elements that I used in the example are base case. The ones I am actually using have styling attributes associated to them, so I would just like to create the first Horizontal LinearLayout in XML then programmatically access it, add the required fields, then set it as a child of the Vertical LinearLayout.
You can create the childs dynamically and add them to the parent vertical LinearLayout.
Please check the following code: I used a TextView and a Spinner for each created row:
//--- Row Layout ---
LinearLayout rowLayout = null;
LinearLayout.LayoutParams rowLayoutparams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
/** Create the TextViews and Spinners */
for (int j=0; j<gameArray.length; j++) {
//Create new row
rowLayout = new LinearLayout(getActivity().getApplicationContext());
rowLayout.setLayoutParams(rowLayoutparams);
rowLayout.setGravity(Gravity.CENTER_HORIZONTAL);
//--- TEXT VIEW ---
TextView myTextView = new TextView(getActivity().getApplicationContext());
myTextView.setId(j);
myTextView.setText("CONTENT");
myTextView.setGravity(Gravity.CENTER);
//--- SPINNER ---
spinnersArray[j] = new Spinner(getActivity().getApplicationContext());
spinnersArray[j].setId(j+100); //set a different ID
//Populate the spinner here ...
//--- LAYOUT PARAMS ---
LinearLayout.LayoutParams itemParams = new LinearLayout.LayoutParams(100, 100); //first and last item
//edit the item params here ...
//Add the params to the Views
myTextView.setLayoutParams(itemParams);
spinnersArray[j].setLayoutParams(itemParams);
//--- Add the view to the Layout ---
rowLayout.addView(myTextView);
rowLayout.addView(spinnersArray[j]);
//--- Add the row to the layout only if (NOT even) OR the chars are finished ---
((LinearLayout)getView().findViewById(R.id.parent)).addView(rowLayout);
}
I want to add table rows dynamically to my layout, the rows will be added to a RelativeLayout called main_ScrollView_Container
The problems I have are that:
The added rows are stacked on top each other and not below each other in order added.
How can I retrieve the added rows so I can read/write to the EditText input and TextView output of each row that I have added?
My oncreate:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launcher);
// the inflater
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// the item to inflate
RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.main_ScrollView_Container);
// the item to inflate with
View tableRow = inflater.inflate(R.xml.my_row, relativeLayout, false);
relativeLayout.addView(tableRow, 0);
tableRow = inflater.inflate(R.xml.my_row, relativeLayout, false);
relativeLayout.addView(tableRow, 1);
tableRow = inflater.inflate(R.xml.my_row, relativeLayout, false);
relativeLayout.addView(tableRow, 2);
tableRow = inflater.inflate(R.xml.my_row, relativeLayout, false);
relativeLayout.addView(tableRow, 3);
// retrieve/set values to the EditText input
// retrieve/set values to the TextView output
}
I got this my_row.xml
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tableRow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp" >
<EditText
android:id="#+id/input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:ems="10"
android:gravity="center"
android:hint="#string/input"
android:inputType="numberDecimal" />
<TextView
android:id="#+id/output"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:hint="#string/output"
android:textAppearance="?android:attr/textAppearanceLarge" />
</TableRow>
and my layout
<ScrollView
android:id="#+id/main_scroll_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<RelativeLayout
android:id="#+id/main_ScrollView_Container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</RelativeLayout>
</ScrollView>
Firstly, you should probably be using R.layout rather than R.xml to organize and reference your layout files. There are many types of xml resources in your project alone - it's a good idea to subcategorize them.
Secondly, when you call relativeLayout.addView(tableRow, 0); you are in fact adding tableRow at the 0th position of the layout (the top). Also, since you are adding the rows into a RelativeLayout, it's no surprise that they are stacking on top of each other. You might want to use a vertically oriented LinearLayout instead, which will take care of vertically arranging the rows from top to bottom.
Thirdly, once you have inflated your row view, you can access its subviews like this:
View tableRow = inflater.inflate(R.xml.my_row, relativeLayout, false);
EditText inputBox = (EditText) tableRow.findViewById(R.id.input);
TextView outputBox = (TextView) tableRow.findViewById(R.id.output);
Remember, you can call findViewById on any View to access its subviews - providing they have IDs.
In an Activity I have added a horizontal Scrollview. This contains an "Add new Set" button and all previously added Sets as buttons. These Sets a saved in a SQLLite Database.
In the beginning of my App I load all Sets from the database. For each Set I add an own Button into the scrollview.
All buttons are shown, but the dynamically added buttons don’t have the right size. They should have the same height and width as the “Add new Set” button.
How I can copy the dimensions of the first button to the others?
Here my XML:
<HorizontalScrollView
android:id="#+id/horizontalScrollView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
<LinearLayout
android:id="#+id/innerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<Button
android:id="#+id/btn_NewSet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:height="100dp"
android:onClick="OnExitClick"
android:text="#string/New_Set"
android:width="100dp" />
</LinearLayout>
</HorizontalScrollView>
Here my Java-Code:
db.open();
Cursor allSets = db.getAllSets();
if (allSets.moveToFirst())
{
Button bDummy = (Button) findViewById(R.id.btn_NewSet);
LinearLayout innerLayout = (LinearLayout) findViewById(R.id.innerLayout);
do
{
Button b1 = new Button(this);
b1.setHeight(bDummy.getHeight());
b1.setWidth(bDummy.getWidth());
b1.setText(allSets.getString(1));
b1.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
));
innerLayout.addView(b1);
}while (allSets.moveToNext());
}
db.close();
They are not the same size because the dynamic buttons also use wrap_content. If you want them the same size you can use the width and height property of the button "id/btn_NewSet" in the layoutparams of the new buttons
You should try using dynamic view inflation:
1) make a dedicated xml (mybutton.xml, for instance):
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/btn_NewSet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:height="100dp"
android:text="#string/New_Set"
android:width="100dp" />
2) Inflate and attach it to innerLayout dynamically:
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout innerLayout = (LinearLayout) findViewById(R.id.innerLayout);
do
{
Button b1 = (Button)inflater.inflate(R.layout.mybutton,null);
b1.setText(allSets.getString(1));
innerLayout.addView(b1);
}while (allSets.moveToNext());