Chapter 5
IN THIS CHAPTER
Using classes with finesse
Working with Kotlin’s classes and interfaces
Being part of Kotlin’s inner circle
Putting your eggs (your file, that is) in a basket
Kotlin can work in the object-oriented programming (OOP) paradigm, much as Java does. It’s not limited to OOP, though. If your heart so desires, you can use it as a functional programming language, too. Chapter 3 in this minibook covers the highlights of both functional and OOP in Kotlin. That chapter focuses on the object-oriented nature of Android development, though, because that’s the topic of this book.
There are significant differences between Java and Kotlin in OOP implementation, so even if you’re familiar with Java OOP, read this chapter to prepare for future chapters on Android programming using Kotlin. This chapter covers some of object-oriented programming's finer points.
Listing 5-1 reproduces a small portion of the source code of Android's Toast
class from Toast.java
. (To open the file in Android Studio, right-click any Toast
entry in your code and choose Go To⇒ Declaration from the context menu.) This class relies on Java even when you're using it from Kotlin, so the underlying class uses Java principles. Using the class differs in Kotlin when compared to Java. (For comparison purposes, you can read about the Kotlin version of this class at https://developer.android.com/reference/kotlin/android/widget/Toast.html
and the Java version of this class at https://developer.android.com/reference/android/widget/Toast.html
.)
LISTING 5-1: An Unrepresentative Sample of Android's Toast Class Code
public class Toast {
public static final int LENGTH_LONG = 1;
public static Toast makeText(Context context,
CharSequence text,
int duration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater) context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate
(com.android.internal.
R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById
(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
public void show() {
if (mNextView == null) {
throw new RuntimeException
("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getPackageName();
TN tn = mTN;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
}
According to the code in Listing 5-1, the Toast
class has a static field named LENGTH_LONG
and a static method named makeText
. Anything that's declared to be static belongs to the whole class, not to any particular instance of the class. When you create the static field, LENGTH_LONG
, you create only one copy of the field. This copy stays with the entire Toast
class. No matter how many instances of the Toast
class you create — one, nine, or none — you have just one LENGTH_LONG
field.
companion object {
const val LENGTH_LONG = 1
}
Contrast this situation with the one in Chapter 3 of this minibook. In that chapter, the Account
class has fields called name
, address
, and balance
. The fields aren't static, so every instance of the Account
class has its own name
, its own address
, and its own balance
. One instance has name Barry Burd
and balance 24.02
, and another instance has name John Q. Public
with balance –471.03
. To refer to Burd's balance, you may write something like
val myAccount = Account()
myAccount.name = "Burd"
myAccount.address = "222 Cyberspace Lane";
myAccount.balance = 24.02
To refer to a non-static member of a class, you write the name of an object (such as myAccount
), followed by a dot and then the name of the member (such as balance
).
But the Toast
class's LENGTH_LONG
field is static. When you create a Toast
instance, you don't create a new LENGTH_LONG
field. The Toast
class has one LENGTH_LONG
field, and that's that. Accordingly, you refer to LENGTH_LONG
by prefacing the field name with the Toast
class name, followed by a dot:
Toast.LENGTH_LONG
In fact, a typical use of Toast
in an Android app refers to the static field LENGTH_LONG
and the static method makeText
:
Toast.makeText
(getApplication(), "Whoa!", Toast.LENGTH_LONG).show();
A call to the Toast
class's makeText
method returns an actual object — an instance of the Toast
class. (You can verify this by referring to the first line of the makeText
method in Listing 5-1, earlier in the chapter.) So in an Android app, an expression such as
Toast.makeText
(getApplication(), "Whoa!", Toast.LENGTH_LONG)
stands for an object. And (again, according to Listing 5-1) each object created from the Toast
class has its own non-static show()
method. That's why you normally follow a Toast.makeText
call with .show()
.
Here's one final word about Listing 5-1: In addition to being static
, the LENGTH_LONG
field is also final
. A final
field is one whose value cannot be changed. In other words, when you declare LENGTH_LONG
, you can initialize its value to 1
(as in Listing 5-1). But elsewhere in the code, you can't write LENGTH_LONG = 2
. (For that matter, you can't even write LENGTH_LONG = 1
elsewhere in the code.)
Because you'll be looking at both Kotlin and Java interfaces when working through your Android code, this section presents both. Listing 5-2 contains a snippet from Android's pre-declared Java code. The listing contains a Java interface.
LISTING 5-2: Android's OnClickListener
Interface
public interface OnClickListener {
void onClick(View v);
}
The Kotlin version of this same interface (which you won’t find in the underlying libraries) looks like this:
interface OnClickListener {
fun onClick(View v)
}
An interface is like a class, but it’s different. (So, what else is new? A cow is like a planet, but it’s quite a bit different. Cows moo; planets hang in space.) Anyway, when you hear the word interface, you can start by thinking of a class. Then, in your head, note the following things:
class MyListener implements OnClickListener
To implement the same interface using Kotlin, you use
class MyListener: OnClickListener
MyListener
objects to listen for long clicks as well as regular clicks, you can write
class MyListener implements OnClickListener,
OnLongClickListener {
A long click is what nondevelopers would probably call a touch-and-hold motion.
Kotlin doesn’t allow true multiple inheritance, so you can still inherit from just one parent class. However, by using a fancy feature called class delegation, in which you delegate handling of inherited features to another class, you can create something that looks a lot like multiple inheritance but doesn’t come with the problems that multiple inheritance can create. Covering class delegation is outside the scope of this book, but you can read about it at https://sites.google.com/a/athaydes.com/renato-athaydes/posts/solutionstomultipleinheritanceinkotlin
if you really want to know how to do something fancy.
As with Java, you can implement multiple interfaces in Kotlin with ease. Here is the Kotlin version of the Java example that appeared earlier in this bullet:
class MyListener: OnClickListener, OnLongClickListener {
SomeListener
extends Android's built-in OnClickListener
interface:
public interface SomeListener extends OnClickListener {
The Kotlin version of the same code looks like this:
interface SomeListener: OnClickListener {
An interface's methods have no bodies of their own. In Listing 5-2, the onClick
method has no body — no curly braces and no statements to execute. In place of a body, there’s just a semicolon (just the function declaration in Kotlin).
A method with no body, like the method defined in Listing 5-2, is an abstract method.
Starting with Java 8, a method declared inside an interface can have a body. A method of this kind is called a default method. As an Android developer, you can use these features, but only when you configure your project correctly. To do this, you update your build.gradle (Module:app)
by adding a compileOptions
block as well as JavaVersion.VERSION_1_8
, as shown here:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
When working with Kotlin, you don't have to worry about any changes to support default methods.
MyListener
class in Listing 5-3 has an onClick()
method. By announcing that it will implement the OnClickListener
interface, the MyListener
class agrees that it will give meaning to the interface's onClick()
method. In this situation, giving meaning means declaring an onClick()
method with curly braces, a body, and maybe some statements to execute.LISTING 5-3: Implementing Android's OnClickListener
Interface in Java
package com.allmycode.samples;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MyActivity extends Activity {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = ((Button) findViewById(R.id.button1));
button.setOnClickListener(new MyListener(this));
}
}
class MyListener implements OnClickListener {
Activity activity;
MyListener (Activity activity) {
this.activity = activity;
}
@Override
public void onClick(View arg0) {
((MyActivity) activity).button.setBackgroundColor
(android.graphics.Color.GRAY);
}
}
Listing 5-3 doesn't illustrate the most popular way to implement the OnClickListener
interface, but the listing presents a straightforward use of interfaces and their implementations. The Kotlin version of the same MyListener
class looks like this:
package com.example.myfirstapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener(MyListener())
}
}
class MyListener : View.OnClickListener {
override fun onClick(v: View?) {
v!!.setBackgroundColor(android.graphics.Color.GRAY)
}
}
When you announce that you’re going to implement an interface (as in class MyListener implements OnClickListener
or class MyListener : View.OnClickListener
), the compiler takes this announcement seriously. In the body of the class, if you fail to give meaning to any of the interface's methods, the compiler yells at you.
Fortunately, Android Studio tells you about any missing implementations. It displays messages telling you which methods to implement. As an alternative, you can choose Code⇒ Implement Methods to tell the IDE to implement the methods for you automatically.
You can think of an interface as a kind of contract. When you write
class MyListener implements OnClickListener
or (for Kotlin):
class MyListener : View.OnClickListener
you're binding MyListener
to the contract described in Listing 5-2. That contract states, “You, the implementing class, hereby agree to provide a body for each of the abstract methods declared in the interface and to indemnify and hold harmless this interface for any damages, mishaps, or embarrassments from wearing pocket protectors.”
As a member of society, you have exactly two biological parents, but you can enter into agreements with any number of companies. In the same way, a Java class has only one parent class, but a class can implement many interfaces. Likewise, even though Kotlin has some tricky ways to get around implementing just one parent class, the reality is that you can implement only one parent class, in the strictest sense, and implement as many interfaces as you want.
The interface-implementing hierarchy (if you can call it a “hierarchy”) cuts across the class-extension hierarchy. Figure 5-1 illustrates this idea, showing class extensions vertically and displaying interface implementations horizontally. (Android's KeyboardView
class lives in the android.inputmethodservice
package. Both KeyboardView
and the homegrown MyListener
class in Listing 5-3 implement Android's OnClickListener
interface.)
The big news in Listing 5-3, shown in the preceding section, is the handling of the user's button click. Anything the user does (such as pressing a key, touching the screen, or whatever) is an event. The code that responds to the user’s press or touch is the event-handling code.
Listing 5-3 deals with the click event with three parts of its code:
MyListener
class declaration says that this class implements OnClickListener
.onCreate()
method sets the button's click handler to a new MyListener
object.MyListener
class has an onClick()
method.Taken together, all three of these tricks make the MyListener
class handle button clicks. Figure 5-2 illustrates the process. (The details are for Java, but the Kotlin details are similar.)
When the user clicks the button, Android says, “Okay, the button was clicked. So, what should I do about that?” And the answer is, “Call an onClick()
method.” It's as if Android has code that looks like this:
OnClickListener object1;
if (buttonJustGotClicked()) {
object1.onClick(infoAboutTheClick);
}
Of course, behind every answer is yet another question. In this situation, the follow-up question is, “Where does Android find onClick()
methods to call?” And there’s another question: “What if you don’t want Android to call certain onClick()
methods that are lurking in your code?”
Well, that's why you call the setOnClickListener()
method. In Listing 5-3, the call
button.setOnClickListener(new MyListener(this));
or (for Kotlin)
button.setOnClickListener(MyListener())
creates a new MyListener
object. You tell Android, “Put the new object's onClick
method on your list of methods to be called. Call this object’s onClick()
method whenever the button is clicked.”
And in response to this request, Android asks, “Oh, yeah? How do I know that your MyListener
object has an onClick()
method that I can call?” And before you can answer the question, Android notices that your MyListener
class implements the OnClickListener
interface. So (because of the code in Listing 5-2), your MyListener
object has an onClick()
method.
So here's the sequence of events (follow along in Figure 5-2): Your app registers a listener with Android. Then your app goes about its business. When a relevant event takes place (such as the clicking of a button), Android calls back to your app's code. Android calls the onClick()
method inside whatever object you registered.
Android calls back to your app's code, so the term callback describes the mechanism that Android uses to handle events.
For more information on handling events without implementing OnClickListener
, see Book 1, Chapter 5.
This section is Java-specific. If you're not interested in how Java works because you don’t plan to go into the library code, you can skip it. Kotlin still does the things that this section discusses, but it does them in the background (hence the shortness of the Kotlin version of the example code found in Listing 5-3).
When you’re working with Java code, the preceding section raises several questions about the interaction between your app and Android's callback. But that section misses one of the questions, which is, “In the onClick()
method of Listing 5-3, how does the code know what button
means?” Listing 5-3 contains two classes — MyActivity
and MyListener
. Without jumping through some hoops, one class doesn't know anything about another class's fields.
In Listing 5-3, the keyword this
sits inside the code that defines the MyActivity
class:
button.setOnClickListener(new MyListener(this));
In Java, this
refers to “the object that contains the current line of code.” So, in Listing 5-3, the word this
refers to an instance of MyActivity
— the activity that's being displayed on the device's screen. The current MyActivity
instance has a button. So far, so good.
Later in Listing 5-3, the MyListener
constructor tucks a reference to the current activity into one of its fields. (See Figure 5-3.)
Looking again at Figure 5-3, MyListener
refers to an activity
, and that activity contains a button. When Android calls the onClick()
method, the method executes an instruction that's very much like this one:
activity.button.setBackgroundColor(android.graphics.Color.GRAY);
The instruction takes the referenced activity's button and sets the button's background color to gray. To make things work properly, you have to do some casting in the onClick()
method of Listing 5-3. For example, when you see the statement:
button = ((Button) findViewById(R.id.button1));
what you're telling the compiler is that the output of findViewById()
needs to be turned into a Button
object, so that it matches the type of button
. Otherwise, the assignment could result in an error.
If you read the preceding section and then you read this section, you'll probably want to send us a nasty email message. The preceding section describes an admittedly convoluted way to make a listener remember which activity's button to tweak. You need to know how Listing 5-3 works, but if you modify Listing 5-3 so that the activity is its own listener, things become much simpler. Listing 5-4 shows you how to do it.
LISTING 5-4: An Activity Eats Its Own Dog Food
package com.allmycode.samples;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MyActivity extends Activity
implements OnClickListener {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = ((Button) findViewById(R.id.button1));
button.setOnClickListener(this);
}
@Override
public void onClick(View arg0) {
button.setBackgroundColor
(android.graphics.Color.GRAY);
}
}
The combination of automation in Android Studio and the concise nature of Kotlin make things even easier. Here is the Kotlin version of the same upgrade:
package com.example.myfirstapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun onClick(v: View) {
button.setBackgroundColor(android.graphics.Color.GRAY)
}
}
The earlier section starts with a question: “In the onClick()
method, how does the code know what button
means?” In this section, that question goes away just as your lap goes away when you stand up.
In Listing 5-4, both the button
and the onClick()
method are members inside the activity. So the onClick()
method has free-and-easy access to the button
. You don't need an Activity
field as in Listing 5-3, and you don't need any fancy casting from Activity
to MyActivity
.
When working with Java, you have to remind Android that MyActivity
contains an onClick()
method, and you do that by adding implements OnClickListener
to the declaration of MyActivity
. You must also remind Android to notify the current MyActivity
object whenever the button gets clicked. You do this reminding by writing
button.setOnClickListener(this);
which, roughly speaking, translates to “Hey, Android! When someone clicks the button, call the onClick()
method that's inside the this
object (a MyActivity
object, which fortunately implements OnClickListener
).” All this connectivity takes place in the background when working with Kotlin, but it still takes place.
The pattern in Listing 5-4 (having an Activity
implement whatever interface it requires) is a very common application-programming idiom.
Java and Kotlin take very different views on extensions. For example, in Kotlin, all classes are final by default. To inherit a class in Kotlin, you must open the class. Having all classes final by default adds a level of security and makes some tasks faster. However, it also means that you have to perform additional work just to override behaviors in an existing class. The following sections compare Java and Kotlin with regard to extensions. You need to know both when working with Android because many of the underlying library classes take the Java approach (given that they're written in Java).
Suppose you want to add functionality to an existing Java class. You like Android's Activity
class, but the pre-declared Activity
class displays nothing on the screen. Do you rewrite Android's Activity
class? No.
Instead of rewriting an existing class, you extend the class. Even in a do-nothing Android “Hello” application, you write
public class MyActivity extends Activity
Then, in the MyActivity
class's declaration, you write
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Your MyActivity
class creates new functionality by extending most of Android's Activity
functionality while overriding the Activity
class's brain-dead onCreate()
method.
In object-oriented programming, extending a class is the noblest thing you can do.
But some classes aren't meant to be extended. Take, for example, Java's String
class. A String
is a String
is a String
. You don't want somebody's MyString.length
method to return the length of time it takes to scramble a string's characters. To prevent someone from doing something unexpected, unconventional, or unusual with a string's methods, the creators of Java made the String
class final:
public final class String
Some of Android's pre-declared classes are also final, including the Telephony
and MediaStore
classes.
As previously mentioned, all classes in Kotlin are final by default. To create a class that you can override, you must declare the class as being open. In addition, you must declare all functions, fields, and other elements that you want to override as being open. Here is an example of the Kotlin open classes:
fun main(args: Array<String>) {
val primary: MySaying = MySaying()
val secondary: YourSaying = YourSaying()
primary.printSaying()
secondary.printSaying()
}
open class MySaying {
open fun printSaying() {
println("This is my saying.")
}
}
class YourSaying: MySaying() {
override fun printSaying() {
println("This is your saying.")
}
}
You still have to initialize MySaying
when extending YourSaying
, which is why it appears as MySaying()
. To override printSaying()
, you must also include the override
keyword.
Unlike with Java, you can extend classes in Kotlin by using a special kind of function, the extension function. An extension function adds something new to a class without changing anything about the existing class. For example, you might want a new way to work with a List
that Kotlin doesn't currently provide. Instead of creating an entirely new List
class, you simply add the required functionality. Here’s an example of working with an extension in Kotlin:
fun main(args: Array<String>) {
val doSaying: MySaying = MySaying()
doSaying.printSaying()
doSaying.printSaying("My custom saying.")
}
class MySaying {
fun printSaying() {
println("This is my saying.")
}
}
fun MySaying.printSaying(saying: String){
println(saying)
}
The extension function uses the form class_name.function_name. This extension adds a version of printSaying()
to the original class that accepts a String argument. This extension method works with final classes (the default), so you can make a kind of modification to Kotlin final classes without having to write everything from scratch.
Both Kotlin and Java provide abstract classes, and they work just about the same for both languages. Although a final class hates to be extended, an abstract class insists on being extended. Android's ViewGroup
is an example of an abstract class. (See Listing 5-5.)
LISTING 5-5: A Small Part of Android's ViewGroup
Class
public abstract class ViewGroup {
public void bringChildToFront(View child) {
int index = indexOfChild(child);
if (index >= 0) {
removeFromArray(index);
addInArray(child, mChildrenCount);
child.mParent = this;
}
}
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
}
Android's ViewGroup.java
file is more than 3,700 lines long. So Listing 5-5 has only a tiny fraction of the file's code. But you can see from Listing 5-5 how a class becomes abstract. To no one's surprise, the word abstract
precedes the word class
. But the word abstract
also starts the declaration of some methods belonging to the class. (Kotlin abstract classes and functions also begin with the word abstract
.)
The founders of Android decided that the idea of a ViewGroup
is useful. They were correct because your favorite Android layouts (LinearLayout
, RelativeLayout
, and so on) are subclasses of ViewGroup
. They also understood that from one kind of ViewGroup
to another, some functionality doesn't change. For example, Listing 5-5 defines a bringChildToFront()
method, and subclasses of ViewGroup
inherit this method.
But the founders also realized that some aspects of a ViewGroup
make no sense unless you work with a particular kind of group. For example, a LinearLayout
positions things one after another, and a RelativeLayout
positions things above, below, and to the side of one another. So Listing 5-5 doesn't have a full-blown onLayout()
method. The onLayout()
declaration in Listing 5-5 has no method body. But Android requires each subclass of the ViewGroup
class to declare its own onLayout()
method. Both Kotlin and Java enforce this requirement when (as in Listing 5-5) you declare method onLayout()
to be abstract
.
As a developer, you can't create an object from an abstract class. If you write
ViewGroup group = new ViewGroup();
or (for Kotlin)
val group: ViewGroup = ViewGroup()
the compiler tells you that you're behaving badly. To do something useful with the ViewGroup
class, you need a subclass of the ViewGroup
class. The subclass has a concrete version of each abstract method in the ViewGroup
class:
package com.allmycode.samples;
import android.content.Context;
import android.view.ViewGroup;
public class MyLayout extends ViewGroup {
public MyLayout(Context context) {
super(context);
}
@Override
protected void onLayout(boolean changed,
int l, int t, int r, int b);
}
}
Here's the Kotlin version of this code:
class MyLayout: ViewGroup(context) {
override fun onLayout(changed: Boolean, l: Int,
t: Int, r: Int, b: Int) {
}
}
Here's big news! You can define a class inside another class! Most classes don't live inside another class, and most classes don't contain other classes. But when the idea behind one class screams out to be part of another class, feel free to create a class within a class. Oddly enough, this is one area in which Kotlin and Java work alike.
For the user, Listing 5-6 behaves the same way as Listings 5-3 and 5-4. But in Listing 5-6, the MyActivity
class contains its own MyListener
class.
LISTING 5-6: A Class within a Class
package com.allmycode.samples;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MyActivity extends Activity {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = ((Button) findViewById(R.id.button1));
button.setOnClickListener(new MyListener());
}
class MyListener implements OnClickListener {
@Override
public void onClick(View arg0) {
button.setBackgroundColor
(android.graphics.Color.GRAY);
}
}
}
Here is the Kotlin version:
package com.example.myfirstapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener(MyListener())
}
class MyListener : View.OnClickListener {
override fun onClick(v: View?) {
v!!.setBackgroundColor(android.graphics.Color.GRAY)
}
}
}
The MyListener
class in Listing 5-6 is an inner class. An inner class is a lot like any other class. But within an inner class's code, you can refer to the enclosing class's fields. For example, the onClick()
method inside MyListener
uses the name button
, and button
is defined in the enclosing MyActivity
class.
Listings 5-4 and 5-6 are very similar. In both listings, you circumvent the complexities described in the section “An object remembers who created it,” earlier in this chapter. For this chapter's example, the choice of Listing 5-4 or Listing 5-6 is largely a matter of taste.
Notice that the code in Listing 5-6 uses the MyListener
class only once. (The only use is in a call to button.setOnClickListener
.) So, do you really need a name for something that's used only once? No, you don't. You can substitute the entire definition of the inner class inside the call to button.setOnClickListener
. When you do this, you have an anonymous inner class. Listing 5-7 shows you how it works.
LISTING 5-7: A Class with No Name (Inside a Class with a Name)
package com.allmycode.samples;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MyActivity extends Activity {
Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = ((Button) findViewById(R.id.button1));
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
button.setBackgroundColor
(android.graphics.Color.GRAY);
}
});
}
}
Here's the Kotlin version of the same code:
package com.example.myfirstapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
v!!.setBackgroundColor(android.graphics.Color.GRAY)
}
})
}
}
Inner classes are good for things like event handlers, such as the onClick()
method in this chapter's examples. The most difficult thing about an anonymous inner class is keeping track of the parentheses, the curly braces, and the indentation. So our humble advice is to start by writing code without any inner classes, such as the code in Listing 5-3 or Listing 5-4. Later, when you become bored with ordinary classes, experiment by changing some of your ordinary classes into inner classes.
18.226.169.94