Chapter 3: Java - Part 7
Right now, the user can change the names of the diners at the top of the app, using the EditTexts. But that doesn't affect the names in the TextViews at the bottom. That needs to be taken care of. Let's think about how. We want to update a TextView for a name each time the corresponding EditText is edited. But when do we check for that? We probably don't want to check each time the user types a character. That would be a waste of processing power. Let's update the TextView only when we know the user is done editing the EditText. And when is the user done? Probably when he clicks out of the EditText and gives focus to another View. In other words, we should update the TextView that represents the name of a Diner every time the corresponding EditText loses focus. An EditText that has focus implies a user is editing it. When it loses focus, the implication is that the user is done editing it, and wants to give focus to something else.
So, how do we know if an EditText loses focus. Guess what? There's an interface for that. It's called the OnFocusChangeListener. Import.
import android.view.View.OnFocusChangeListener;
Now have MainActivity implement OnFocusChangeListener in order to use its methods.
public class MainActivity extends Activity implements OnClickListener, OnFocusChangeListener {
Again, we are able to implement an unlimited number of interfaces, separated by commas. We just have to implement all of their methods. OnFocusChangeListener only has one, the onFocusChange method. See below:
@Override
public void onFocusChange(View v, boolean hasFocus) {
}
The onFocusChange method is similar to the onClick method. It is called whenever a View that something that implements OnFocusChangeListener is listening to has a change in its state of focus (that is, whenever it gets focus or loses focus). The arguments it takes is the View that got or lost focus, called v, and a Boolean called hasFocus. A Boolean is a data type that evaluates to (i.e. resolves to, or becomes) one of two things: true, or false. In this case, if v got focus, hasFocus will be true, and if v lost focus, hasFocus will be false. Just like onClick, these arguments are passed automatically by Android based on what has gained or lost focus.
Of course, right now, onFocusChange will never be called. That's because MainActivity isn't listening for changes in focus in any View. Let's change that. Add the following code to the onCreate code block:
firstCustomer.setOnFocusChangeListener(this);
Now MainActivity is listening to changes in focus in firstCustomer (the EditText that has the name of the first Diner). That's what the setOnFocusChangeListener method does. We also need to listen to all future EditTexts that represent Diner names. To do that, go to addDinerButton's onClick code block, and add this:
et1.setOnFocusChangeListener(this);
This way, every future Diner that will be created will also have its name EditText listened to by MainActivity. Now that we're listening to all of them, we need to write the code that says what to do when an EditText loses focus. And the thing to do is to set the corresponding TextView to the new name. This is where our Diner class comes in again. Remember how it ties all the Views of a particular customer together into one tidy organizational unit? Well, because of that, we can now easily change the right TextView when given any particular EditText. Let's go back to the Diner class. Add this block of code underneath the constructor block.
public void setName()
{
tvName.setText(etName.getText().toString());
}
Here, we're making a new method that every Diner object will have called setText. This method will make that Diner's tvName TextView have the same Text property as its etName EditText. We can now use this method back in our MainActivity. Go back to MainActivity. Make the onFocusChange method look like this:
public void onFocusChange(View v, boolean hasFocus) {
for (int i = 0; i < dinerList.size(); i++) {
if (v == dinerList.get(i).etName && hasFocus == false)
{
dinerList.get(i).setName();
}
}
}
Let's start with the first new line, which has the keyword for. This line is the start of a for loop. A loop in programming is a block of code that runs over and over again until something tells it to stop (or nothing tells it to stop and you have an infinite loop of doom). A for loop is one kind of loop. Let's pick it apart. There are three parts inside the argument, divided by semi-colons. The first part is int i = 0. All this does is create an integer called i (you can use any variable name, but i, for index, is standard), and set it equal to 0. An integer in programming is the same thing as an integer in math: a whole number. It can be positive or negative, and is a very basic programming object/data type. The second part is i < dinerList.size(). This is a condition for the loop to run. We already dealt with conditionals earlier, with the if block. What this part says is to keep running the for loop until the condition is no longer true. Here, it says to run the loop as long as i is less than the size of dinerList. Once i is larger than the size of dinerList, exit out of the loop code block and move on to the next line of code after that. The last part is i++. This part says to increase i by 1 after every loop, or iteration. The ++ means to increase by 1, or to increment. An iteration in programming can refer to one run of a loop code block. Right then, let's re-cap the whole line. We're creating a for loop. We want the content of the loop to repeat over and over (that is, once the computer gets to the last line of the code block, it should go back to the first line) until an integer variable called i is no longer less than the size of dinerList. After each iteration of the loop, increase i by one so that, eventually, i will no longer be less than the size of dinerList, and we can break out of the loop.
Now let's look at the code that represents the content of the for loop, as marked by the fancy brackets. First we have a conditional. It says: if v is equal to dinerList.get(i).etName and hasFocus is false, do some stuff. We have a new method here, the get method. It returns the element of the ArrayList (here, that's dinerList) at index number i. So, if i is 5, it would return the 6th Diner object in dinerList (the first item has an index value of 0, not 1). Then, after the get method, we have .etName. That's not a method (note the lack of round brackets). Instead, it's saying: get the etName of dinerList.get(i). Again, dinerList.get(i) returns a particular Diner object. The etName part is referring to the etName of that particular Diner. Remember that v is a View that recently lost or gained focus. The first part of the if conditional here checks whether or not that View is the same View as the etName of dinerList.get(i). After that, we have &&. In plain English, && means 'and'. In if conditionals, it links two conditionals together such that they both must be true if the conditional is to evaluate to true. Finally, the last part of the if statement is the second conditional: hasFocus == false. This is just checking to see if hasFocus is false. That is, make sure v lost focus, not gained focus (since onFocusChange will run in both cases).
Let's review. We have a for loop. The content of the for loop essentially runs/iterates once for each diner in dinerList. Each time, we check if the View that just gained or lost focus is equal to a particular Diner's etName. The idea is that if we run this code block once for each Diner, we'll eventually get the correct Diner associated with that etName (assuming it was one of the etNames that gained or lost focus, which, right now, are the only Views being listened to for focus changes). We also check to see if the View lost focus, instead of gained focus. If both these tests evaluate to true, then we know that a particular Diner's etName has just lost focus. Now that we know that, we can change the corresponding TextView to make sure it matches the text in the etName the user just edited.
Glossary
++: The ++ operator increases the thing to the left of it by 1. The thing on the left is assigned the new, larger value. The -- operator does the same thing, except it decreases something by 1.
&&: The && operator links two conditionals together in an if statement. For the entire if statement to evaluate to true, both conditionals have to be true.
Boolean: A Boolean is a data type that can only evaluate to two values, either true, or false.
Data Type: Data types are the various kinds of basic information in programming, like integers, strings, and booleans.
Evaluate: To evaluate to something means to resolve to some value. For example, 1+1 evaluates to 2.
For Loop: A for loop is a kind of loop. It's fairly common, and is very useful.
get(int index): The get method returns the element of the ArrayList it's called on that is at the position that's passed as an argument.
Integer: An integer is a whole number. It is one of the basic building blocks of most programs.
Iteration: Among other things, an iteration refers to one single run of the code inside a loop.
Loop: A loop in programming is a code block that is run multiple times.
onFocusChange(View v, boolean hasFocus): The onFocusChange method is called whenever something that onFocusChangeListener is listening to gains or loses focus.
OnFocusChangeListener: The OnFocusChangeListener interface allows classes that implement it to listen to changes in focus of Views.