glaukon
Chapter 3: Java - Part 10
Okay, let's run the app and see what happens. Try out the add order ImageButtons. We seem to have a slight problem.
It looks like we need a new algorithm to find the correct row number to insert row3 at. An algorithm is just the method a computer uses to solve a problem. Here, we try just adding 1 to the size of dinerList. Clearly, that doesn't work quite right. But that makes sense. The current algorithm simply makes row3 the last row in the list of customers in the pre-tax bill part of our app. That is, it assumes each Diner only has one order. Moreover, if you add a few orders to some Diners, then hit addDinerButton again, you'll notice that that's no longer working properly either. That's because we aren't accounting for the fact that each Diner in dinerList can now take up multiple rows. Let's fix the addDinerButton code first, then fix the newOrder buttons. Find the line where we add row1 to mainTable in addDinerButton's onClick code block, and replace it with this:

int rowIndex = 1;

for (int i = 0; i < dinerList.size(); i++) {

rowIndex += dinerList.get(i).orderList.size();

}

mainTable.addView(row1, rowIndex);

Here, we create an integer called rowIndex. We then create another for loop that iterates once for each Diner in dinerList. Inside the for loop, we make use of +=. The += operator increases whatever is to the left of it by whatever is to the right of it. Here, we are increasing rowIndex by dinerList.get(i).orderList.size(). Note that the two example lines below are functionally equivalent:

x = x + 5

x += 5

+= is basically a shortcut for assigning a new, increased value to a variable based on its old variable. Note also that the following two example lines are also functionally equivalent:

x += 1

x ++

The ++ operator is a shortcut for the += operator, when you only want to increase by 1. The -- and -= operators do the same thing, except they're for decreasing. Oh, and an operator in programming is something that designates operations, like +, -, *, /, ++, --, +=, -=, <, >, etc. Probably should have went over that a couple hours ago...
Anyway, sorry for that tangent. Both += and ++ are pretty popular shortcuts that many programming languages have, so I thought it would be nice to quickly go over them. Let's go back to the code. So what are we incrementing rowIndex by? Well, first we get the Diner at position i in dinerList. Then we get its orderList. Then we call the size method on that Diner's orderList. The result should be how many orders that Diner has, which should also be the same as how many rows that Diner takes up in mainTable. That's how dinerList.get(i).orderList.size() works.
Once we go through that process for every single Diner in dinerList, rowIndex should then be equal to 1 + the total number of orders created by the user so far. That number should also be equal to the row number after the last row currently in the pre-tax bill portion of our app (which is where we want row1 to go). So let's add row1 to mainTable, and pass rowIndex as the row number.
Next, let's fix the part where we insert row2. Find the line that adds row2 to mainTable, and replace it with this:
mainTable.addView(row2, rowIndex + dinerList.size() + 11);
At this point, rowIndex has already taken into account all the Diner rows in the pre-tax bill portion of the app. We then just add to it the 11 rows under that, which are constant, and the size of dinerList, since that's how many rows have been created already in the bill splitting part of the app. The resulting number should be the correct row to insert row2 into. Think about it some more if you haven't gotten how it works. It'll come to you.
Okay, done? Cool, let's use this technique for adding the new order rows. Replace the part where we add row3 to mainTable with this:

int rowIndex2 = 1;

for (int j = 0; j <= i; j++) {

rowIndex2 += dinerList.get(j).orderList.size();

}

mainTable.addView(row3, rowIndex2);

Essentially the same idea here. We create another integer, this time called rowIndex2, and have it start at 1. Then we have a for loop. Notice that we the j variable here, since earlier in this else block, we already used the i variable. We can get away with using i twice if one is in the if block, and another is in the else block, since that means only one of them will ever be used in any given iteration. But we can't have two i variables in one if block or one else block. Anyway, the for loop iterates until j is less than or equal to (not just less than) i. Remember that this for loop is in another for loop. The computer only ever gets to this for loop if the current iteration of the outer for loop has given us an i that corresponds to the particular ibAddOrder the user has clicked. Therefore, i must be the position of the Diner in dinerList that the user wants to add a new order to. This new for loop runs once for each Diner starting with the first Diner in dinerList, and ending in the Diner that the user wants to add an order to. For each iteration, we add dinerList.get(j).orderList.size() to rowIndex2. In other words, we make rowIndex2 equal to the number of rows before the row under the last row of the Diner the user wants to add an order to. Perhaps an infographic is in order.
Hopefully the algorithm makes sense now. If not, think about it some more, maybe take a look at the YouTube version of this tutorial, or just wait a day or two and come back. Nested for loops can be tricky for beginners. Oh, and nested means code blocks within code blocks, like a conditional inside of another conditional, or a loop inside of another loop, etc.
Test the app. Success!
At this point, I'd like to have a checkpoint, to make sure we're all on the same page. Here is the code for MainActivity so far:

package glaukon.tutorials.tipcalc;


//Import statements let us use code that's not in our project's package.

import java.util.ArrayList;

import android.app.Activity;

import android.os.Bundle;

import android.view.Menu;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.View.OnFocusChangeListener;

import android.widget.EditText;

import android.widget.ImageButton;

import android.widget.TableLayout;

import android.widget.TableRow;

import android.widget.TextView;


//MainActivity inherits from the Activity class, but also from the OnClickListener and OnFocusChangeListener interfaces.

public class MainActivity extends Activity implements OnClickListener, OnFocusChangeListener {

//Variable declarations go here.

private TableLayout mainTable;

private ImageButton addDinerButton;

private EditText firstCustomer;

private EditText amount1of1;

private ImageButton addButton1;

private TextView textSplit1;

private TextView textSplit1Dollar;

private ArrayList<Diner> dinerList;


//Replace the onCreate method we inherited from Activity with our own code using @Override.

@Override

protected void onCreate(Bundle savedInstanceState) {

//First, run Activity's version of onCreate.

super.onCreate(savedInstanceState);

//Inflate the UI using activity_mail.xml in the layout folder.

setContentView(R.layout.activity_main);

//Retrieve all the Views we need from our UI using findViewById and assign them to variables we declared earlier.

mainTable = (TableLayout) findViewById(R.id.mainTable);

addDinerButton = (ImageButton) findViewById(R.id.addDinerButton);

firstCustomer = (EditText) findViewById(R.id.firstCustomer);

amount1of1 = (EditText) findViewById(R.id.amount1of1);

addButton1 = (ImageButton) findViewById(R.id.addButton1);

textSplit1 = (TextView) findViewById(R.id.textSplit1);

textSplit1Dollar = (TextView) findViewById(R.id.textSplit1Dollar);

//Make a new ArrayList called dinerList, then make the first Diner using the Views from our UI, then add that Diner to dinerList.

dinerList = new ArrayList<Diner>();

Diner diner = new Diner(firstCustomer, amount1of1, addButton1,

textSplit1, textSplit1Dollar);

dinerList.add(diner);

//Set up listeners.

firstCustomer.setOnFocusChangeListener(this);

addDinerButton.setOnClickListener(this);

addButton1.setOnClickListener(this);


}

//Replace the onCreateOptiosnMenu method we inherited from Activity with our own code using @Override.

@Override

public boolean onCreateOptionsMenu(Menu menu) {

//Inflate the options menu using activity_main.xml in the menu folder.

getMenuInflater().inflate(R.menu.activity_main, menu);

return true;

}


//Implement the onClick method of OnClickListener.

@Override

public void onClick(View v) {

//If it's addDinerButton that was clicked, do this.

if (v == addDinerButton) {

//Create a 2 TableRows, 2 EditTexts, 1 ImageButton, and 2 TextViews. Set their attributes based on inflated Views from the UI.

TableRow row1 = new TableRow(this);

EditText et1 = new EditText(this);

et1.setText("Customer");

et1.setSelectAllOnFocus(true);

et1.setInputType(firstCustomer.getInputType());

et1.setGravity(firstCustomer.getGravity());

et1.setLayoutParams(firstCustomer.getLayoutParams());

et1.setWidth(firstCustomer.getWidth());

et1.setOnFocusChangeListener(this);

EditText et2 = new EditText(this);

et2.setText("$0.00");

et2.setSelectAllOnFocus(true);

et2.setInputType(amount1of1.getInputType());

et2.setGravity(amount1of1.getGravity());

et2.setLayoutParams(amount1of1.getLayoutParams());

et2.setWidth(amount1of1.getWidth());

//Make sure the order EditText gets selected automatically.

et2.requestFocus();

ImageButton ib = new ImageButton(this);

ib.setImageResource(R.drawable.additem);

//Remember to set the listener.

ib.setOnClickListener(this);

//Add Views to rows, then add rows to mainTable. Use a for loop to get the right row numbers to insert rows at. Make sure to account for every order of every Diner.

row1.addView(et1);

row1.addView(et2);

row1.addView(ib);

int rowIndex = 1;

for (int i = 0; i < dinerList.size(); i++) {

rowIndex += dinerList.get(i).orderList.size();

}

mainTable.addView(row1, rowIndex);

TableRow row2 = new TableRow(this);

TextView tv1 = new TextView(this);

tv1.setText(et1.getText().toString());

tv1.setGravity(textSplit1.getGravity());

TextView tv2 = new TextView(this);

tv2.setText(et2.getText().toString());

tv2.setGravity(textSplit1Dollar.getGravity());

row2.addView(tv1);

row2.addView(tv2);

//Add 11 static rows to rowIndex, as well as one row for each Diner in the bill splitting portion of app.

mainTable.addView(row2, rowIndex + 11 + dinerList.size());

//Make a new Diner and add it to dinerList.

Diner diner = new Diner(et1, et2, ib, tv1, tv2);

dinerList.add(diner);

//If it's not addDinerButton, do this.

} else {

//Use a for loop to run though all of dinerList, and if any one of their ibAddOrder ImageButtons were clicked, do this.

for (int i = 0; i < dinerList.size(); i++) {

if (v == dinerList.get(i).ibAddOrder) {

//Create a row, then 2 EditTexts. The first is a dummy one used to push the second one into the second column. Again, grab attributes from Views inflated by XML.

TableRow row3 = new TableRow(this);

EditText emptyEditText = new EditText(this);

emptyEditText.setVisibility(4);

EditText newOrder = new EditText(this);

newOrder.setText("$0.00");

newOrder.setSelectAllOnFocus(true);

newOrder.setInputType(amount1of1.getInputType());

newOrder.setGravity(amount1of1.getGravity());

newOrder.setLayoutParams(amount1of1.getLayoutParams());

newOrder.setWidth(amount1of1.getWidth());

newOrder.requestFocus();

row3.addView(emptyEditText);

row3.addView(newOrder);

//For loop again to get the right row number to insert the row at.

int rowIndex2 = 1;

for (int j = 0; j <= i; j++) {

rowIndex2 += dinerList.get(j).orderList.size();

}

mainTable.addView(row3, rowIndex2);

//Add the order to the correct Diner's orderList.

dinerList.get(i).newOrder(newOrder);

}

}

}

}


//Implement the onFocusChange method of OnFocusChangeListener.

@Override

public void onFocusChange(View v, boolean hasFocus) {

//Use a for loop to run though all of dinerList, and if any one of their etName EditTexts lost focus, do this.

for (int i = 0; i < dinerList.size(); i++) {

if (v == dinerList.get(i).etName && hasFocus == false) {

//Set the tvName TextView using the Diner's setName method.

dinerList.get(i).setName();

}

}

}

}

And here is the Diner class:

package glaukon.tutorials.tipcalc;


//Import statements let us use code that's not in our project's package.

import java.util.ArrayList;

import android.widget.EditText;

import android.widget.ImageButton;

import android.widget.TextView;


public class Diner {

//Declare the variables we'll need.

public EditText etName;

public EditText etFirstOrder;

public ImageButton ibAddOrder;

public TextView tvName;

public TextView tvSplitBill;

public ArrayList<EditText> orderList;


//This is the constructor. It runs when you instantiate a Diner. It brings together all the Views that are associated with a new Diner object.

public Diner(EditText et1, EditText et2, ImageButton ib, TextView tv1, tv2) {

//We also need an ArrayList to keep track of each Diner's orders.

orderList = new ArrayList<EditText>();

etName = et1;

etFirstOrder = et2;

ibAddOrder = ib;

tvName = tv1;

tvSplitBill = tv2;

orderList.add(etFirstOrder);

}


//This method sets the Text property of tvName to whatever the user changed the Text property of etName to.

public void setName() {

//We need to use toString because getText does not return a string.

tvName.setText(etName.getText().toString());

}


//This method adds a new order to a Diner's orderList.

public void newOrder(EditText newOrder) {

orderList.add(newOrder);

}

}

As always, copious use of the auto-formatter is suggested. Now that we're all on the same page, let's continue.
Glossary
+=: The += operator increases the thing to the left of it by the thing to the right of it. The thing on the left is assigned the new, larger value. The -= operator does the same thing, except it decreases.
Algorithm: An algorithm is just a set of rules, or a method, to solve a problem. It can refer to programming problems, or other problems.
Nesting: Nesting refers to having one block of code inside another block of code. For instance, you can nest one for loop inside of another for loop.
Operator: An operator denotes an operation. For instance, the + operator means addition.