Android:Adding views

From Juneday education
Jump to: navigation, search

Introduction

It is not always possible to define all views in the Layout using XML. Sometimes you need to add Views in your source code using Java. As an example we ask you to think about developing a Contact app. How could you possibly know how many contacts your user will have. You can't, so you need to add Views dynamically using Java. Adding views like this is easy.

In short, there's only a few things we need to do to a View (e. g. a button):

  • Get a reference to the Layout
  • Create a View
  • Add the View to the layout

And most likely if a clickable View

  • Add a method to handle "clicks"

Videos

Video channel:Adding Views

Adding a View to a layout

We will now go through the three steps we mentioned earlier.

Getting a reference to a layout

Before we can get a reference to the Layout we need to an id to our layout. Do this in the Layout's XML file. We will call it top_layout. Our Layout file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/top_layout"
    tools:context="se.juneday.throwaway.addingviews.MainActivity">
</LinearLayout>

Since the Layout now have an id (defined in the XML file) we can find the layout by using the method:

  findViewById(R.id.button);

We can use it like this:

  LinearLayout layout  = (LinearLayout) findViewById(R.id.top_layout);

Create a View

How do we create a View, such as a Button? Or let's put it another way. How do we create an instance of Button? Think about how you usually create objects in Java. Same thing with a Button. We use the constructor in Button:

  Button(Context context)

We can use it like this:

  Button b  = new Button(this); 
  b.setText("Butoni");

Add the View to the Layout

And now it's time to add the Button to the layout. To do this we're using method:

   void addView (View child)

We can use it like this:

  layout.addView(b);

Putting it all together

Here's the layout file activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/top_layout"
    tools:context="se.juneday.throwaway.addingviews.MainActivity">


</LinearLayout>

The source code is available at gihthub: activity_main.xml.

And here's the Java file MainActivity.java

package se.juneday.throwaway.addingviews;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout layout  = (LinearLayout) findViewById(R.id.top_layout);
        Button b  = new Button(this);
        b.setText("Butoni");
        layout.addView(b);
    }
}

The source code is available at gihthub: MainActivity.java.

Acting on clicks

We can associate a method with a click using the method void setOnClickListener (View.OnClickListener l), which you can read about in the API: setOnClickListener. This method requires an class implementing the interface View.OnClickListener.

So we need a class implementing the View.OnClickListener interface. Read about the interface here: View.OnClickListener

Implementing the interface can be done in, at least, four different ways.

  1. Letting your MainActivity implement the interface
  2. Creating an inner class
  3. Creating an anonymous inner class
  4. Using lambda notation

Note: The third and fourth option will be the same (more or less) in the actual byte code.

Hint: We recommend reading the following tutorial on Lambda Expression in Java.

Let's look at the possible ways one by one.

Letting your MainActivity implement the interface

We're really not fans of this approach. Let's motivate it shortly.

  • Is listening to a click a very important aspect of your your class (MainActivity)?
  • Is it a good idea to have a method with an if statement for each View?
  • .... TODO TODO TODO

Ths book's author's answers to the questions above are no, no , no...

Creating an inner class

Here's an example on how to create an inner class (implementing the interface).

package se.juneday.throwaway.addingviews;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout layout  = (LinearLayout) findViewById(R.id.top_layout);
        Button b  = new Button(this);
        b.setText("Butoni");
        b.setOnClickListener(new ButtonListener());
        layout.addView(b);
    }

    class ButtonListener implements OnClickListener {

        @Override
        public void onClick(View view) {
                ((Button) view).setText("Butoni clicked");
        }
    }

}

This is ok. But . hmmm ... . It is quite a lot of code for the simple task of associating a method with a click and the code is located in two places:

  • one piece of code defines the class
  • another piece of code associates an instance of the Listener with the Button

And do we really need to name the class implementing the interface? We see no reason to create and name an inner class. So, let's look at the next way.

Creating an anonymous inner class

Now, it's time to look at creating an inner class anonymously. We're going to create a class that implements the interface without naming the class. You've done a similar thing tons of times, take a look at this:

   System.out.println("Hello, is it me you're looking for?");

Recognise it? Well, of course you. In the code above we don't give the String object a "name" (more correctly we don't create a reference variable referring to the object). Same goes for classes, if we're only using them at one once (as we do above) we need not give the class a name. We can create an anonymous class implementing the interface like this:

new OnClickListener() {
    @Override
    public void onClick(View view) {
       ((Button) view).setText("Butoni clicked");
    }
}

We can now use this anonymous class in the method setOnClickListener:

package se.juneday.throwaway.addingviews;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout layout  = (LinearLayout) findViewById(R.id.top_layout);
        Button b  = new Button(this);
        b.setText("Butoni");
        b.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                ((Button) view).setText("Butoni clicked");
            }
        });
        layout.addView(b);


    }

}

The amount if code is reduced compared to when using an inner class. And the code is in one place. Nice. But we can use lambda notation to get even less code.

Using lambda notation

Let's instead use lambda notation. It is similar to the anonymous inner class in the section above:

        b.setOnClickListener((view)-> ((Button) view).setText("Butoni clicked using lambada"));

How does this work? Since this is an Android course, rather than a Java course, we will explain this very briefly. The Java compiler sees that you're using lambda notation and will then look for a method with a signature which it can "transform" the lambda expression to. The Java compiler finds that the method setOnClickListener requires a View.OnClickListener as argument. View.OnClickListener is an interface and the compiler then looks at the interface and checks if it can find one and only one method specified in the interface that can be used. There is one such method: void onClick (View v). So the Java compiler can transform the expression above to an anonymous inner class to the one below.

 b.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                ((Button) view).setText("Butoni clicked");
            }
        });

The amount of code is even less than in the previous example. We can now also skip saying what interface we're implementing and focus only on what we want to be done.

Note: You need to make sure you're using Java 8 and API level 25 or higher if you want to use lambda expressions.

Chapter links

Source code

Further reading

Read more about Button and LinearLayout in the Android API:

Android - the practical way

Book TOC | previous chapter | next chapter