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.
Related
I have a layout xml file I am trying to append to a TableLayout when the user clicks a button. Here is my onClick listener method:
addHazardButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
TableLayout table = (TableLayout) view.findViewById(R.id.safety_question_table);
View row = getLayoutInflater(null).inflate(R.layout.fragment_safety_question_table_row, table);
table.addView(row);
}
});
I have also tried replacing the line View row = getLayoutInflater.... with the following:
View row = getLayoutInflater(null).inflate(R.layout.fragment_safety_question_table_row, null);,
&
TableRow row = getLayoutInflater(null).inflate(R.layout.fragment_safety_question_table_row, table);,
&
TableRow row = getLayoutInflater(null).inflate(R.layout.fragment_safety_question_table_row, null);
I have also tried passing the LayoutInflater from the onCreateView method to the method my onClickListener is set in and using it, but I don't think that is the problem, as the stack trace is
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.TableLayout.addView(android.view.View)' on a null
object reference
How do I properly add my xml layout file to the table onClick?
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/task_step_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#drawable/border_outline"
android:inputType="text"
android:maxLines="1" />
<EditText
android:id="#+id/hazards_not_covered_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#drawable/border_outline"
android:inputType="text"
android:maxLines="1" />
<EditText
android:id="#+id/reduce_risk_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#drawable/border_outline"
android:inputType="text"
android:maxLines="1" />
</TableRow>
Use
TableLayout table = (TableLayout) findViewById(R.id.safety_question_table);
instead of
TableLayout table = (TableLayout) view.findViewById(R.id.safety_question_table);
Because TableLayout is probably in Activity layout instead of in Button View.
OR
if TableLayout is in parent View of Button then we can also access it as:
View parent = (View)view.getParent();
if (parent != null) {
TableLayout table =
(TableLayout)parent.findViewById(R.id.safety_question_table);
// add your code here
}
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()).
So I have an XML layout1 which is just a LinearLayout with three text views. I also have another XML layout2 with ScrollView and a LinearLayout inside it. I'm using this for loop to create several of the layout2 inside the LinearLayout of the ScrollView. It's working fine but I want to be able to set the text of each of the TextViews within the for loop. I'm not sure how to access these TextViews as I can only set one id within the XML file, will that not cause problems if I tried to access their id inside the for loop?
private void setUpResults() {
for (int i = 1; i < totalQuestions; i++) {
parent.addView(LayoutInflater.from(getBaseContext()).inflate(
R.layout.result_block, null));
}
}
Here is the result_block xml file (layout1) :
<?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="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/layoutSelectedAnswer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/border"
android:orientation="horizontal"
android:paddingBottom="#dimen/option_padding_bottom"
android:paddingTop="#dimen/option_padding_top" >
<TextView
android:id="#+id/tvOptionALabel2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:text="#string/option_a"
android:textColor="#color/white"
android:textSize="#dimen/option_text_size" />
<TextView
android:id="#+id/tvSelectedAnswer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:text="#string/option"
android:textColor="#color/white"
android:textSize="#dimen/option_text_size" />
</LinearLayout>
<LinearLayout
android:id="#+id/layoutCorrectAnswer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/border"
android:orientation="horizontal"
android:paddingBottom="#dimen/option_padding_bottom"
android:paddingTop="#dimen/option_padding_top" >
<TextView
android:id="#+id/tvOptionBLabel2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:text="#string/option_b"
android:textColor="#color/white"
android:textSize="#dimen/option_text_size" />
<TextView
android:id="#+id/tvCorrectAnswer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:text="#string/option"
android:textColor="#color/white"
android:textSize="#dimen/option_text_size" />
</LinearLayout>
</LinearLayout>
Let's say I wanted to set the TextView with the id as tvCorrectAnswer to a different String value in each loop, how should I access it?
Sure, you can do it like this:
private void setUpResults () {
LayoutInflater i = LayoutInflater.from(getBaseContext());
for (int i = 1 /* should be zero? */; i < totalQuestions; i++) {
View view = i.inflate(R.layout.result_block, parent, false);
TextView correctAnswer = (TextView) view.findViewById(R.id.tvSelectedAnswer);
correctAnswer.setText("My Answer Text");
parent.addView(view);
}
}
The key is, inflate using the parent as the container, but don't attach it (the false parameter). Then you'll get a reference to the inflated view, which you can then directly reference to do your findViewById() calls (which will limit the search to that particular ViewGroup). Then add it to the parent and continue to the next item.
Once you have added all your views in your ViewGroup you can use ViewGroup.getChildAt(int) to get a one of the views you have inserted. Once you get one of the views you can access any of its inner views. Something like this.
for(int i = 0; i < parentView.getChildCount();++i) {
View v = parentView.getChildAt(i);
Textview tv = (TextView) v.findViewById(R.id.tvOptionALabel2);
//And so on...
}
I'm pretty close to go for a LinearLayout alternative, but its kind of irritating not getting this right. To get most flexibility out of things I've defined a TableLayout xml with only a row header defined. Next I've generated a seperate TableRow xml defining the "row template". In Javacode I've subclassed TableRow and in the constructor i inflate the tablerow template to attach to the root (the subclassed class).
Well, good so far. When the table is populated the headerrow is ok, but the other rows are NOT. It seems like they are laid out in a different way, the two columns is not filling the whole width as expected and the the coluomns is therefore not aligned correctly to each other.
Anyone who can shed some light on this one? I've tried a lot of solutions, but nothing makes it work.
The tablelayout with header row
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<HorizontalScrollView
android:id="#+id/horizontalScrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true" >
<TableLayout
android:id="#+id/zone_table"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="*" >
<TableRow
android:id="#+id/tableRow1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal"
android:clipToPadding="false" >
<TextView
android:layout_width="0dip"
android:layout_weight="0.8"
android:background="#ffcccccc"
android:text="Zonename"
android:textColor="#android:color/black" />
<TextView
android:layout_width="0dip"
android:layout_weight="0.2"
android:background="#ffcccc00"
android:gravity="right"
android:text="Antall"
android:textColor="#android:color/black" />
</TableRow>
</TableLayout>
</HorizontalScrollView>
</ScrollView>
The "other" inflated row
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/zonetablerow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="#+id/zonerow_name"
android:layout_width="0dip"
android:layout_weight="0.8"
android:background="#ffcccccc"
android:textSize="18dp" />
<TextView
android:id="#+id/zonerow_invcount"
android:layout_width="0dip"
android:layout_gravity="right"
android:layout_weight="0.2"
android:background="#ffcccc00"
android:textSize="18dp" />
</TableRow>
Class extending TableRow
public class ZoneRow extends TableRow {
private ZoneInventoryDAO dao = null;
private int inventoryCount = 0;
public ZoneRow(Context ctx, ZoneInventoryDAO dao) {
this(ctx, dao, 0);
}
public ZoneRow(Context ctx, ZoneInventoryDAO dao, int inventoryCount) {
super(ctx);
setWeightSum(1.0f);
this.dao = dao;
this.inventoryCount = inventoryCount;
doLayout();
}
private void doLayout() {
// XML layouten settes med zonerow som parent (se:
// http://developer.android.com/resources/articles/layout-tricks-merge.html)
View v = LayoutInflater.from(getContext()).inflate(R.layout.zonerow,
this, true);
TextView t = (TextView) findViewById(R.id.zonerow_name);
TextView cnt = (TextView) findViewById(R.id.zonerow_invcount);
t.setText(dao.getZoneAlias());
cnt.setText(String.valueOf(inventoryCount));
}
public void incInventory() {
inventoryCount++;
}
public ZoneInventoryDAO getDAO() {
return dao;
}
}
What it looks like is that you are extending tablerow and creating an instance of that object. This will cause a table row to be created. Then you are inflating another tablerow which is for some reason interacting with the first. For a quick fix try adding something like table.setColumnStretchable(0, true);
I'm having the same problem. I fixed it by not doing that - specifically by not subclassing TableRow, but just instantiating them in code and applying whatever I needed to them by hand. You can do this by encapsulating instead. Have some record that has ZoneInventoryDAO, inventoryCount, and tableRow, which points to the corresponding TableRow instance. Just instantiate TableRow with an inflation:
TableLayout table = (TableLayout) inflater.inflate(R.layout.my_table, null); // etc
...
for (//each data item//) {
TableRow tr = (TableRow) inflater.inflate(R.layout.my_table_row, null);
TextView tv1 = (TextView) findViewById(R.id.table_entry_1);
tv1.setText("Whatever goes here");
...
// and so on for each column
table.addView(tr);
}
The above worked for me when moving that same code into a class which extends TableRow did not.
I know this is old, but I had same issue, and figured it out, so maybe this will help future users.
The problem is, that when you inflate the TableRow from XML, and you add it ino your extended TableRow, it is seen by the Table as a single column, with multiple children, as the TableRow in your Layout is in essence a ViewGroup. Therefore it lays out the table, as a single column table.
The solution is that in the TableRow XML layout, use a <merge> type, rather than a <TableRow> type. This way when inflated, the items within your xml, will be merged into your extended TableRow, and your extended TableRow will have multiple colums.
Here is the example code
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/zonetablerow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="#+id/zonerow_name"
android:layout_width="0dip"
android:layout_weight="0.8"
android:background="#ffcccccc"
android:textSize="18dp" />
<TextView
android:id="#+id/zonerow_invcount"
android:layout_width="0dip"
android:layout_gravity="right"
android:layout_weight="0.2"
android:background="#ffcccc00"
android:textSize="18dp" />
</merge>
I am trying to add table row dynamically in my activity. The table row is in the relative layout. It looks fine but don't know where I am going wrong. Below is my code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
RelativeLayout RLayout = (RelativeLayout)findViewById(R.id.RelativeLayout);
TableRow tableRow = (TableRow)findViewById(R.id.TableRow);
for(int i = 1; i <3; i++)
RLayout.addView(tableRow); //My code is crashing here
}
And the main.xml as follows
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/RelativeLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<TableRow
android:id="#+id/TableRow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
>
<TextView
android:id="#+id/Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
>
</TextView>
</TableRow>
</RelativeLayout>
Please Help.
It's crashing because that TableRow is already in the layout. If you want to add some dynamically you have to create it programmatically, that is:
// PSEUDOCODE
TableRow newRow = new TableRow(this);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(/*....*/);
newRow.setLayoutParams(lp);
relLayout.add(newRow);
Actually TableRow should be used inside TableLayout.
If you want to use something more than once, you can use the inflate technique. You need to create an xml layout that includes the only part that you want to repeat (so your TableRow and its children), and then:
LayoutInflater inflater =
(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View inflated = inflater.inflate(R.layout.your_layout, null);
Now inflated contains the layout you specified. Instead of null, you might want to put there the layout to which attach the inflated one. Every time you need a new element like that, you would have to inflate it the same way.
(You should always report the error you get when it crashes)
------ EDIT -----
ok now i see, this is your code:
RelativeLayout RLayout = (RelativeLayout)findViewById(R.id.RelativeLayout);
TableRow tableRow = (TableRow)findViewById(R.id.TableRow);
for(int i = 1; i <4; i++) {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View inflated = inflater.inflate(R.layout.main, tableRow);
}
this way you're inflating your whole layout inside the original TableRow.
You should have a row.xml layout like this, together with the main.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="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
>
<TextView
android:id="#+id/Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
/>
</TableRow>
and then inflate it like this:
RelativeLayout RLayout = (RelativeLayout)findViewById(R.id.RelativeLayout);
for(int i = 1; i <4; i++) {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.row, RLayout);
}
see if it works.