android app development for human beings · android app development for human beings mohit...
TRANSCRIPT
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 1
Android App
Development for
Human Beings
Mohit Deshpande
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 2
Table of Contents
Hola World – First Steps with Android ................................................................................................3
Basic UI Widgets – TextViews, EditTexts, and Buttons ....................................................................... 12
Creating a Unit Converter App – Part 1 ............................................................................................. 18
Creating a Unit Converter App – Part 2 ............................................................................................. 27
How to Create Simple Lists using ListViews ....................................................................................... 36
How to Create Lists using RecyclerView ............................................................................................ 52
Introduction to SQLite Databases on Android Devices ....................................................................... 63
How to Create Toolbars and Menus in Android ................................................................................. 71
How to Use Contextual Toolbar and ActionBar in Android ................................................................. 87
How to get the User Location in Android Tutorial .............................................................................. 95
Beginners Guide to Material Design ................................................................................................ 102
How to Use Loaders in Android ...................................................................................................... 108
How to Accessing the Camera and Take Pictures on Android ........................................................... 114
How to Use Android Sensors in Games ........................................................................................... 121
How to Use the Android Notification System .................................................................................. 129
How to Pass Data between Activities using Intents ......................................................................... 136
Android Networking Tutorial with AsyncTask ................................................................................. 156
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 3
Hola World – First Steps with Android Hello World! In this post we’re going to create our very first Android application! We’re going to
download Android Studio and other prerequisites. Then we’re going to create a new Android Studio
project and write a little app such that the user can type in his or her name, press a button, and the app
will display “Hello ” and the name of the user in the middle of the screen.
Prerequisites Before we start, we’re going to need to install some software that will allow us to develop Android apps.
First, go to this link and download and install Android Studio. With that installation, we have everything
we need to write Android apps! To verify, open up Android Studio and click Configure → SDK Manager.
In the dialog that pops up, on the bottom left, click Show Package Contents, and, under Android
6.0, you’ll want to make sure the following things are checked:
Android 6.0 Platform
Intel x86 Atom_64 System Image
Under the SDK Tools tab, Intel x86 Emulator Accelerator (HAXM installer). Follow this guide to
install HAXM to boost emulator performance. (For Windows, replace <sdk> with
C:\Users\<username>\AppData\Local\Android\sdk. For Mac, replace <sdk>
with /Users/<username>/Library/Android/sdk where <username> is your login name.
Creating a New Project Now that we have everything we need for Android Studio, we’re going to create our very first Android
app! Opening Android Studio, click “Start a new Android Studio project” and type in something like
MyFirstAndroidApp for the project name. We can type in anything we want for the company domain,
but we can use lastname.com where lastname is your last name (i.e. deshpande.com). This company
domain is just used to uniquely identify your app in the Google Play Store. The project location can just
be any path in your filesystem where you want to store the project.
After clicking next, change the minimum SDK to Android 6.0 Marshmallow. Choose Empty Activity and
we’ll just hit next past the following screen since we’re ok with just a single MainActivity.
Then click Finish and Android Studio should create our project!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 4
Creating an Android Virtual Device Now that we’ve created our project, we want to run it on the emulator so we can see it on our
computer. The Android emulator is essentially a barebones version of the Android operating system
running in a window on your computer. If we had an Android phone running Android 6.0, we could
configure it to enable USB debugging and run it directly on the device. However, this post won’t assume
you have one so we’re going to use the emulator instead.
On the top toolbar, find the app icon that corresponds to the AVD Manager (looks like a phone with an
Android peeking up from the bottom) and a dialog should pop up with a button: Create Virtual Device.
Afterwards, we can choose any device to emulate, such as the Nexus 5X or Galaxy Nexus or Nexus 6P.
We can even customize our device’s screen size and density, but we’ll go with the Nexus 5X for now.
Feel free to choose whatever device you’d like! On the next page, we need to select the Intel x86
Atom_64 System Image. After naming the device anything we want, we can click Finish. After some
time, we should have a new virtual device we can run our apps on! We can click the green arrow in the
dialog and Android Studio will launch the device. Note that it will take some time for Android Studio to
launch the emulator!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 5
If the emulator is too large, we can always decrease the size of it by clicking the pencil icon in the AVD
dialog and changing the size, as well as any other specs we want to change, like screen resolution. We
can then exit out of the AVD window and then click the green Run icon on the top toolbar of
Android Studio to compile our app to run on an emulator or other connected device. When the “Choose
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 6
A Device” dialog box pops up, then select the emulator and click OK. In a few seconds, our app should be
running on the emulator!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 7
Personalized Hello World Now that we’ve created a very simple Hello World app, we can start to make it a bit more complex by
adding UI elements. Let’s open up the activity_main.xml file in the Project view under app → res →
layout. If you don’t see the Project view, there’s a small tab on the left titled Project, or you can
press Ctrl + 1 or Cmd + 1. In the design view that comes up, we can click on the text (called a
TextView ) and delete it. Then, on the palette on the left, we can drag-and-drop an Person Name
EditText to the top left of the screen under the ToolBar that says “MyFirstAndroidApp.” When
dragging it to that position, make sure the semi-transparent overlay says alignParentLeft and
alignParentTop before you drop it in the view. Drag the right handle until the
EditText reaches the far right of the screen and the overlay says alignParentRight.
These overlays help to anchor our view to it’s parent’s view, the screen in this case. To summarize,
alignParentLeft will align the current view’s left side flush with its parent’s left side. This works
similarly for the other properties.
Similarly, drag out a Button and position in below the EditText and centered on the screen
(overlay should say centerHorizontal and below=editText ). We can change the text of
the button to “Submit” by double-clicking on the Button and changing the text in the popup. Finally,
we can drag-and-drop a Large Text TextView to the dead center of the screen (popup should say
centerHorizontal and centerVertical ). To get rid of the text in the TextView , we
can double-click the TextView and clear it out.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 8
Wire Up the Connections Now that we have these UI elements, we actually want to do something with them. We want our user to
enter their name, click the button, and the name will appear in the center of the screen. From this, it
seems like all of our work will be done when the button is clicked. We need to wire our Button so
that when it’s clicked, we want to execute code that extracts the text from the EditText and puts it
in the TextView . Let’s go back to our layout file and click on the Button. In our MainActivity.java
(located in app → java → com.lastname.myfirstandroidapp), after onCreate(Bundle
savedInstanceState) , add the following method definition:
public void buttonClicked(View button) {
}
1
2
3
public void buttonClicked(View button) {
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 9
When typing it in, you’ll notice that Android Studio will help by providing autocomplete suggestions. You
should press enter when you find the one you need so that Android Studio can automatically add import
statements (like pressing enter when typing View ). Now we can go back to our layout file and click on
the Button. In the properties pane on the bottom right, find the onClick property and click in the box.
A dropdown should be available and have our method signature in there. We can select our method
signature, and that’s all it takes to wire our button to call the code in our buttonClicked method
when the button is pressed.
Coding the Button Click Now we can go back to the method in our source code and fill it in with the following lines:
1
2
3
4
EditText editText = (EditText) findViewById(R.id.editText);
TextView textView = (TextView) findViewById(R.id.textView);
String name = editText.getText().toString();
textView.setText("Hello " + name + "!");
The first line retrieves a reference to the EditText we created. The call to
findViewById(int) takes an integer key. There is a class that Android Studio manages and
creates simply called R (stands for resources) and allows us to access all of the XML data like layouts, UI
elements, and images that are in the res (also stands for resources) folder. This is the mechanism that
Android Studio uses to access XML elements in Java. In our case, we didn’t change the ids of the
EditText and TextView so they are editText and textView by default, and, since
they’re ids, they’re in the id inner class of R . It should be noted to NEVER modify this class! It’s auto-
generated and managed by Android Studio’s unique id references so any changes will break all of our
references!
The findViewById(int) method returns an object of class View and we can typecast it to what
we want it to be. It’s helpful to give useful ids to our UI elements so that we can be sure what subclass
of View it is, whether it’s an EditText , TextView , Button , or other UI elements. Similarly,
the second line of the code snippet grabs a reference to our TextView .
In the third line, we grab the text out of the EditText using it’s getText() method. However,
the getText() method returns a CharSequence , which is an interface. We can convert this to a
String (which actually implements CharSequence ) by calling toString() on that variable.
Finally, we can set the text of the TextView to be “Hello ” plus the name plus an exclamation point
using the setText(CharSequence) method. This is all it takes to create our app! Now we can
run our app using the same green run icon and view the results!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 10
Bonus: You might notice that when clicking on the EditText, we have to manually clear it out first. A
way to prevent this is to clear it like we did for the TextView , and changing the hint property of the
EditText to say something like “Enter your name” for example. The hint property shows up as a
transparent overlay on top of the EditText that disappears when you click on the EditText .
Conclusion In this post, we created our very first Android app! We first started by installing Android Studio and
the necessary tools we needed to code our Android app. Then we created a virtual device so that we
could run our application on it. After creating a blank Android Studio project, we first ran it on the
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 11
emulator to test the emulator and the default project. Then we removed the UI elements and added our
own view. Then we coded the button so that when it was pressed, it ran code in our MainActivity class.
We finally ran it and saw the results of our first Android app! For the complete source code, click here.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 12
Basic UI Widgets – TextViews, EditTexts, and Buttons Hello World! In this post, we’re going to be going in-depth into several of the most common UI elements
that we’ll encounter in Android: TextViews, EditTexts, and Buttons. We’ll be looking into their properties
and some of Java’s language features like anonymous inner classes.
TextView The simplest way to show the user text is through the TextView element. Since this is the simplest
element, we’ll also be covering some topics that are common to all Views.
Using a TextView is a really simple thing to do. All it requires is a height and width (required by all UI
elements) and some text to go inside. The TextView also allows for all sorts of formatting for text as
well. In XML, this is what a simple hello world TextView would look like.
1
2
3
4
5
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Hello World!"
/>
As a side note, the android: prefix corresponds to the Android XML namespace, where Android will
look for of our XML properties. layout_width and layout_height are required for all views
and view containers and can only have two possible values: wrap_content and match_parent.
Using the former means to make the View as big as the content of that View, whether it be a Button or
EditText. However, the latter means that the width or height will be the same as its parent’s width or
height.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 13
Another important property and distinction we should make is between padding and margin. The above
figure illustrates the different between the two. Padding is internal to the border of the View. Margin,
on the other hand, is external to the border to the View. We use a combination of padding and margin
to prevent the content of our views from being flush with the edge of the phone. It makes our content
easier to read for the user’s sake and that’s always important! In fact, Google’s Material Design
guidelines provide keylines in units of dp’s, which stand for density-independent pixels. For layouts, it’s
more common to use padding since the layout contains other views; for single UI elements, it’s more
common to use a combination of both.
The most important property, however, is the id property. This is the id that allows us to grab a
reference to that view via its id. We also need to tell Android that we’re creating an id for a new UI
element and not referring to an existing one. This is why we use “@+id/someView” when creating an id
for a particular UI element and “@id/someView” is referring to another View. On a side note,
referring to Views is something that we need to do when strategically placing UI elements. Creating ids
will let us access them using the findViewById(int) method. When we save any XML file,
Android Studio automatically updates the R.id file.
EditText If TextViews are the simplest way for users to view text, EditTexts are the simplest way for users to
enter text. They are actually a subclass of TextView that is editable. The XML example for an EditText is
similar to a TextView:
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 14
1
2
3
4
5
<EditText
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:hint="Hello World!"
/>
A useful property of EditText for text input is the hint property. It provides us with a transparent overlay
that sits on top of the textbox and disappears when the user taps on it to input text. If we had used the
text property for this purpose, the user would have to clear out that text before they could input their
own. The hint property allows us to tell the user what data is supposed to go into this EditText.
One of the greatest properties of an EditText is that it can bring up a keyboard that’s most relevant to
the kind of input we want our user to enter. Take the Contacts app for example. When entering a phone
number, you’ll notice that the entire QWERTY keyboard won’t show up, but we’ll just get the numpad
instead. This is the inputType property on EditTexts. Below is a list that describes the possible values for
the inputType property:
1. “text” – Brings up the plain text QWERTY keyboard
2. “textEmailAddress” – Like “text”, except with the @ (at) character
3. “textUri” – Like “text”, except with the / (forward slash) character
4. “number” – Brings up standard number input keypad
5. “phone” – Brings up the dialer keypad
In addition to those values, we can even specify other characters of plain text input. For example,
suppose we were entering someone’s name. It would be nice of the keyboard could automatically
capitalize the first and last name. Or suppose we were typing an email; then we’d want the keyboard to
capitalize each sentence. Luckily, these inputType constants are also very explicitly named:
“textCapSentences”, “textCapWords”, “textAutoCorrect”, “textPassword”, “textMultiline”. To be even
more explicit, “textPassword” changes all of the characters to • (dots), and “textMultiline” allows the
user to use line breaks.
Speaking of line breaks, it may be the case that you’d want multiple lines for text entry for things like
emails, for example. To force the EditText to span multiple lines, we can set the minLines property on it
to some positive integer. This will for the EditText to be at least that number of lines tall, prompting your
user to enter text that’s longer than a single line.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 15
Button The Button is the most common way for users to begin or end some activity. For example, we might use
buttons to allow the user to submit a form, download an in-app purchase, or end the level of a
game. Therefore, the most common action associated with buttons is tapping on them! There are
several ways for us to detect a button press and to run code on a button click. The first way is the
simplest way and that uses the onClick property that Buttons have. We can declare a Button whose
onClick property is the name of a method with a very particular method signature in any Activity that
sets that XML layout to be its content view:
1
2
3
4
5
6
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Submit"
android:onClick="buttonClicked"
/>
This button is a small UI element defined in a bigger layout view, which in turn is defined in a layout file.
Activities can present a view to the user through the setContentView(int) method and pass in
the id of a layout file using the R class. Now, in any Activity that uses the layout file as the main content
view will have to implement a method that looks EXACTLY like this:
1
2
3
public void buttonClicked(View view) {
...
}
The method must be public void and take in a single View parameter. It might be easier to define all
of your button click actions first, then associate them with a Button in the layout since the property’s
autocomplete will help you find a particular method. As a rule of thumb, if autocomplete can’t find your
method, that’s a sign the method signature is incorrect in some way. The reason the method is public
void is so Android Studio can see it and because button event listeners don’t generally have return
values. The View method parameter corresponds to which View was clicked. This can be helpful because
instead of declaring multiple methods for different Buttons, we could wire all of the Buttons to one
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 16
method and use view.getId() to differentiate between the Buttons using their unique id that we
would set.
There is another way to do event handling for Button clicks and that involves a language feature of Java
called anonymous inner classes. Instead of setting the listener in the XML layout, we could do it
in Java source code. Suppose we have some Button whose id is submitButton. The follow code snippet
will grab a reference to that and setup an event handler for the Button’s onClick event:
1
2
3
4
5
6
7
Button submitButton = (Button) findViewById(R.id.submitButton);
submitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
...
}
});
The more interesting portion of the code snippet is the next line that sets the listener. All listeners in
Android are interfaces, meaning we can’t instantiate them. However, we can set them to a new object
whose’s class implements that particular interface. Anonymous inner classes is a shorthand way of doing
this. Instead of creating an inner class that implements the View.OnClickListener interface
and passing in new SomeClassImplementingTheListener, we can just do that in-place by
“instantiating” a new nameless class that automatically implements that interface and provide method
bodies on the spot. This works equally well. In fact, there’s a third way we could do this even. We could
pass in this and have the activity in which this code is in implement
the View.OnClickListener interface and force it to have a @Override public void
onClick(View) method. The rule of thumb you can use is “If the method contents are going
to have many lines of code, then it looks cleaner to pass in this , make the Activity implement the
interface, and give it a method body as a regular method on the same level of the class, rather than to
add a lengthy method body to an existing method. It’s just more lines of indentation you have to
manage.”
Conclusion In this post, we looked at TextViews, EditTexts, and Buttons. When looking at TextViews, we also
glanced at some of the key properties that are central to all Views. We learned that we can configure
the keyboard that EditTexts pull up to suite our data input needs, as well as make them span multiple
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 17
lines high for longer text input. Buttons are useful for starting or stopping something and we primarily
learned about event handling when it came to Buttons. As a bonus, we learned about Java’s anonymous
inner classes and how we can use them for event handling.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 18
Creating a Unit Converter App – Part 1 In this post (split into two), we’re going to build a unit converter app. We’ll be learning many things
along the way like the Model-View-Controller (MVC) pattern, Java Enums, and Spinners. By the end
of this post, we’ll have a working app that will allow us to convert to and from various units of distance
like miles, centimeters, kilometers, and other units. Feel free to append more types of units as well!
As a side note, it’ll be helpful if you’ve read over how to setup Android Studio, create projects, and some
of the basic Android UI elements. You can get up to speed very quickly by following the link here.
You can pull the source code for this project here.
Model-View-Controller (MVC) Pattern When dealing with a system that has many moving parts it’s helpful to have some form of system design
that helps us keep everything distinct. Imagine the mess our app would be if we had XML views, Java
source code, and other components wildly about in our Android project! To prevent this disaster from
occurring, we have design patterns we follow to try to keep our code as segmented as possible. Similar
to the concept of encapsulation in Java, we want to make sure our components are separated from each
other. For this, we use the Model-View-Controller (MVC) pattern. The MVC Pattern is now ubiquitously
used in both mobile and desktop development.
The above figure is an illustration of the components of the MVC pattern. There’s the model, view, and
controller. The model can be any kind of backend such as a database or web server. It can also be any
kind of structure that holds or stores data somewhere, like a local key-value pairing system. The view is
pretty self-explanatory: it’s the UI that’s presented to the user. The controller is the most crucial
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 19
component that allows for communication between the model and the view. However, the view and the
model don’t even know the other exist! This is so we can make changes to the model or the view and we
only need to update the controller instead of all three components! (The view in most case is
the component that is updated the most frequently.) The MVC Pattern is now ubiquitously used in both
mobile and desktop development.
Specifically, Android supports the MVC pattern and we’re going to be using it throughout this app and all
other apps. In fact, Android mandates the view and the model are all separate because the view is in
written in an XML layout file (although it is possible to manually instantiate views in Java source code).
In Java, Activities act as the controller and mediate between the XML views whatever model backend we
decide to use. In our case, our model will just be a utility class that we’ll store the constants needed for
converting from one kind of unit to another.
Creating the UnitConverter Project Let’s get started! First of all, we’ll want to create an Android project called UnitConverter with any
package name we want. We’ll want to keep this to Android 6.0 and creating a blank activity named
MainActivity will suffice for us. The first component we’ll create is the model. We’ll need to right-click on
the main package (not the one ending with (androidTest)!) and create a new class called Converter. This
will be used to convert the different units. However, we’ll need to keep some way to represent
units. There are several ways we can do this: String constants with the name of the units, distinct integer
constants that represent those values, or we can use a language feature of Java called enums.
Java Enums Enums in Java were introduced to the language after it was created and was not a part of it’s
original language features. There’re really usefully however! Essentially, we can use them to create
groups of related constants. An enum is also a kind of class in the sense that we can declare variables
and methods in an enum. One of the greatest benefits of using enums is the fact that they’re type-safe!
Meaning that we can’t assign an enum variable to just any value, it must be to a valid constant in the
variable’s enum. It also prevents us from performing any arithmetic operations. It doesn’t make any
sense to add constants representing something like units! If we used String or integer constants, we
could do something KILOMETER + MILE and that wouldn’t make any sense! Once we declare an enum,
we can use it as a type like we would with a class or interface. However, as I mentioned before, the
value of the enum variable must be in the enum class that defines that variable. Under the hood, Java
essentially creates objects with static memory addresses so when we refer to an enum constant, there’s
only one! This is great because we can do equality checks with == since the only way the two enum
pointers are the same is if they point to the same object in memory, of which there is only
one! Similarly, we can use the assignment operator since that will also allow us to set the pointers to the
same static object in memory. To sum up, enums are a kind of class that allow us to group related
constants in a type-safe way much better than integer or String constants.
We’re going to nest our enum inside our Converter class, but to do that, we need to create our
Converter class! Right-click on our main package (not the green highlighted one that ends with
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 20
“(androidTest)” since that’s the test suite) and select New Java Class. We’ll call it Converter. Inside it,
let’s declare the following enum:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum Unit {
INCH,
CENTIMETER,
FOOT,
YARD,
METER,
MILE,
KILOMETER;
// Helper method to convert text to one of the above constants
public static Unit fromString(String text) {
if (text != null) {
for (Unit unit : Unit.values()) {
if (text.equalsIgnoreCase(unit.toString())) {
return unit;
}
}
}
throw new IllegalArgumentException("Cannot find a value for " + text);
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 21
We can see that an enum can be declared in a similar fashion to a class. The first thing in an enum is the
comma-delimited list of constants. If we wanted to write methods, we have to terminate our list of
constants with a semicolon. Then we’re free to write any methods. In this case, we’re writing a small
utility method that will return us a enum value given a String. We’re doing our null check as the first
step. Then we go through each value of the enum and if our input equals the name of the enum
constant, then return that enum constant. At the end of the method, we’re going to throw an exception
saying that the user didn’t input a valid unit String. Enum checking is exhaustive, meaning that if we get
an enum of type Unit, we know that its value MUST be one of the constants. We can access those
constants by Unit.MILE for example. You’ll see in the next post that Spinners only allow the user to input
a finite set of options. This, along with the fact that enums are exhaustive, means that we’ll never reach
that exception and it’s just there for our debugging purposes. Now we can start using our enums in our
model.
Model: Converter class In the same Converter class, we’re going to need to design a way to convert from one unit to another.
Essentially, a user would input a starting unit, an ending unit, and a value to convert. There are several
design approaches we could take. For example, we could have a static method that takes in two Unit
objects and a double and converts between the two. However, to demonstrate OOP even further, we’ll
have the Converter class take the to and from Units as part of the constructor and have an instance
method called convert(double) that takes in a double and returns an output. We can convert
from any unit to another valid unit by multiplying it by some constant. Since we’re following the
constructor approach, we can set that constant in the constructor to make our method very simple. To
set the constant, we need to check the value of the the to and from units and set the constant equal to
some value that will properly convert from one to the other. We can initially set the constant to 1 in the
case that the to and from Units are equal to help reduce a case. Here’s what this would look like: (put
this code after the enum declaration)
1
2
3
4
5
6
7
8
9
// What can I multiply by to get me from my fromUnit to my toUnit?
private final double multiplier;
public Converter(Unit from, Unit to) {
double constant = 1;
// Set the multiplier, else if fromUnit = toUnit, then it is 1
switch (from) {
case INCH:
if (to == Unit.CENTIMETER) {
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 22
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
constant = 2.54;
} else if (to == Unit.FOOT) {
constant = 0.0833333;
} else if (to == Unit.YARD) {
constant = 0.0277778;
} else if (to == Unit.METER) {
constant = 0.0254;
} else if (to == Unit.MILE) {
constant = 1.5783e-5;
} else if (to == Unit.KILOMETER) {
constant = 2.54e-5;
}
break;
case CENTIMETER:
if (to == Unit.INCH) {
constant = 0.393701;
} else if (to == Unit.FOOT) {
constant = 0.0328084;
} else if (to == Unit.YARD) {
constant = 0.0109361;
} else if (to == Unit.METER) {
constant = 0.01;
} else if (to == Unit.MILE) {
constant = 6.2137e-6;
} else if (to == Unit.KILOMETER) {
constant = 1e-5;
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 23
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
break;
case FOOT:
if (to == Unit.INCH) {
constant = 12;
} else if (to == Unit.CENTIMETER) {
constant = 30.48;
} else if (to == Unit.YARD) {
constant = 0.333333;
} else if (to == Unit.METER) {
constant = 0.3048;
} else if (to == Unit.MILE) {
constant = 0.000189394;
} else if (to == Unit.KILOMETER) {
constant = 0.0003048;
}
break;
case YARD:
if (to == Unit.INCH) {
constant = 36;
} else if (to == Unit.CENTIMETER) {
constant = 91.44;
} else if (to == Unit.FOOT) {
constant = 3;
} else if (to == Unit.METER) {
constant = 0.9144;
} else if (to == Unit.MILE) {
constant = 0.000568182;
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 24
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
} else if (to == Unit.KILOMETER) {
constant = 0.0009144;
}
break;
case METER:
if (to == Unit.INCH) {
constant = 39.3701;
} else if (to == Unit.CENTIMETER) {
constant = 100;
} else if (to == Unit.FOOT) {
constant = 3.28084;
} else if (to == Unit.YARD) {
constant = 1.09361;
} else if (to == Unit.MILE) {
constant = 0.000621371;
} else if (to == Unit.KILOMETER) {
constant = 0.001;
}
break;
case MILE:
if (to == Unit.INCH) {
constant = 63360;
} else if (to == Unit.CENTIMETER) {
constant = 160934;
} else if (to == Unit.FOOT) {
constant = 5280;
} else if (to == Unit.YARD) {
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 25
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
constant = 1760;
} else if (to == Unit.METER) {
constant = 1609.34;
} else if (to == Unit.KILOMETER) {
constant = 1.60934;
}
break;
case KILOMETER:
if (to == Unit.INCH) {
constant = 39370.1;
} else if (to == Unit.CENTIMETER) {
constant = 100000;
} else if (to == Unit.FOOT) {
constant = 3280.84;
} else if (to == Unit.YARD) {
constant = 1093.61;
} else if (to == Unit.METER) {
constant = 1000;
} else if (to == Unit.MILE) {
constant = 0.621371;
}
break;
}
multiplier = constant;
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 26
118
119
120
121
// Convert the unit!
public double convert(double input) {
return input * multiplier;
}
Now the convert method is really simple. We just return the constant times the input. One downside to
this approach is if we wanted to convert another set of units, we’d have to create a new Converter
object. However, in our case, we’ll only initialize the Converter object after the user has clicked the
“Convert” Button. But now our model is ready and we can use it to convert units!
In this post, we covered the MVC pattern and how we’ll be using it in our apps. We also completely
created the model of our Unit Converter app and learned about Java enums and how we can use them
to label constants in a type-safe manner. In the follow post, we’ll be building the View
and implementing the Controller to interact between the Model and View.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 27
Creating a Unit Converter App – Part 2 This is Part 2 of the series on creating a Unit Converter app (you can view part 1 here). In this final
section, we’re going to work on the View and Controller of our Unit Converter and learn about how we
can design a UI with Spinners to allow the user the convert units.
View
Since we have a relatively simple app, we’re going to have a relatively simple UI. We’ll limit our
application to just a single screen for simplicity. We need an EditText for the user to enter values in and
another, immutable one to show the result of the conversion. Instead of an EditText, we could have
used a TextView since they’re meant to be immutable, but for the sake of consistency in our app, we can
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 28
easily force an EditText to be immutable and use it to only display values, not enter them. A Button that
will allow the user to start the conversion process. Most of our work will be done when the Button is
pressed since it’s the primary action for our app. However, we need constructs to allow the user to
choose a single option from a finite list of possibilities. If you’ve used a web browser, you’re probably
familiar with selection boxes. Android’s method of doing this is called a Spinner.
Spinner For our view however, we’ll need some kind of selection method to give the user a chance to
choose the unit they want to start at and the one they want to end with. For this purpose, we have
Spinners in Android. Given an array of elements, we can populate a Spinner with elements and, at any
point in time, get the option that the user selected.
We’re going to add some Spinners and other UI components to our layout. Our final layout file (called
activity_main.xml in res/layout should look something like the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/spinner_from"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 29
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
android:ems="10"
android:id="@+id/editText_from"
android:layout_below="@+id/spinner_from"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true" />
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/spinner_to"
android:layout_below="@+id/editText_from"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:cursorVisible="false"
android:ems="10"
android:id="@+id/editText_to"
android:layout_below="@+id/spinner_to"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true" />
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 30
45
46
47
48
49
50
51
52
53
54
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Convert"
android:id="@+id/button_convert"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:onClick="convert" />
</RelativeLayout>
There are a few properties that we might not have seen before. For example,
layout_alignParentStart and layout_alignParentEnd are similar to
layout_alignParentLeft and layout_alignParentRight except they work with
right-to-left layout styles of text (such as Arabic) as well. The ems property on EditTexts allows us to
change the font size essentially. To prevent users from entering text in the EditText, we have a slew of
properties that will only allow the EditText’s text to be changed programatically. This is done in the
lines starting with android:clickable="false" and ending
with android:cursorVisible="false". The Spinners are the more interesting part. As we
can see, they’re set up like any view, with any number of layout properties. However, they need to be
populated with values.
There are several ways to populate a Spinner. The first, and preferred, method is to store the contents
of the Spinner entries inside of the strings.xml file in a string array element. Another way we could do
this is to hard-code an array, but there are downsides to this approach. For example, we won’t be able
to localize the strings to a particular language. If we have it in the XML file, then we can have it
translated to another language and create another strings.xml file to store the strings of that particular
language and the Android system will decide at runtime which one to use, depending on the locale of
the device. This is partly how the Android resource system works, but we’ll save that topic for another
time.
To add a string array to the strings.xml file, we need to navigate to that file located in our res/values
directory. There should be a file called strings.xml in that directory. There should already be some
content inside it, but we can add a string array by typing in the following code as the last XML element in
the file:
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 31
1
2
3
4
5
6
7
8
9
<string-array name="units">
<item>Inch</item>
<item>Centimeter</item>
<item>Foot</item>
<item>Yard</item>
<item>Meter</item>
<item>Mile</item>
<item>Kilometer</item>
</string-array>
This will declare all of our valid units. Now that this is done, we can grab a reference to our Spinner and
create an ArrayAdapter to get the elements from the XML array and format in a way that the Spinner
can understand it. The purpose of adapters is to take text or other data and format it in a way that it can
be displayed in UI elements. We use adapters for Spinners, Lists, Grids, and many other Views. This
whole process only takes a few quick lines of code to do and we can use the same adapter for both of
our Spinners since they both will have the same information displayed in the same fashion. We need to
do this in the onCreate(Bundle) method so that right after our view is associated with
this Activity, the layout’s Spinners will be populated with text. In our MainActivity’s
onCreate(Bundle) method, add the following code snippet.
1
2
3
4
5
6
7
8
9
// An adapter to convert the String[] into something that can go in the Spinner
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.units, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
fromSpinner = (Spinner) findViewById(R.id.spinner_from);
toSpinner = (Spinner) findViewById(R.id.spinner_to);
fromSpinner.setAdapter(adapter);
toSpinner.setAdapter(adapter);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 32
In the above code, we have an ArrayAdapter of CharSequence, but since String implements
CharSequence, we can use it. The ArrayAdapter.createFromResource(Context,
int, int) method takes a Context object, which is, in almost all cases, the current Activity. The two
integers are the reference to the array we created in the strings.xml file and the second is the layout we
want to use. We could create our own custom layout in the layout folder, but we can
use Android’s spinner layout instead since it’s just a single TextView displaying our items. The next line
sets the view that will be shown when a user taps on a Spinner and the dropdown appears with all of
the options. We can use Android’s default spinner dropdown layout as well. The next two lines grab a
reference to the Spinners. The final two lines set the Spinner’s adapter to be the adapter we created.
This is all it takes to configure our adapter! When we run the app, the Spinner will be populated with the
values of our adapter and will display them in the layout we specified.
Wiring Up the Button Now that we’ve set up our View, we can configure the Button to do something when it is clicked. If you
remember from the previous part, we used the onClick property of our Button and the name of our
method is convert(View) . Let’s break down what we need to do. We first need a reference to the
Spinners and the EditTexts. Then we need to extract the text from the Spinner item and map it to a Unit.
This will be easy since it’s one of the core functionality of our model. Then we can instantiate a new
Converter object, given the to and from units, and perform the conversion. The final step is setting the
output EditText’s text to be the converted value. In Java source code, this looks like the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
public void convert(View view) {
Spinner fromSpinner, toSpinner;
EditText fromEditText, toEditText;
fromSpinner = (Spinner) findViewById(R.id.spinner_from);
toSpinner = (Spinner) findViewById(R.id.spinner_to);
fromEditText = (EditText) findViewById(R.id.editText_from);
toEditText = (EditText) findViewById(R.id.editText_to);
// Get the string from the Spinners and number from the EditText
String fromString = (String) fromSpinner.getSelectedItem();
String toString = (String) toSpinner.getSelectedItem();
double input = Double.valueOf(fromEditText.getText().toString());
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 33
14
15
16
17
18
19
20
21
22
23
// Convert the strings to something in our Unit enu,
Converter.Unit fromUnit = Converter.Unit.fromString(fromString);
Converter.Unit toUnit = Converter.Unit.fromString(toString);
// Create a converter object and convert!
Converter converter = new Converter(fromUnit, toUnit);
double result = converter.convert(input);
toEditText.setText(String.valueOf(result));
}
There may be points of improvement that we can make for performance or efficiency. For example,
instead of reinitializing the views, we could just declare them as private member variable and only call
findViewById(int) once in onCreate(Bundle) . This is because calls to
findViewById(int) are expensive operations so calling them the least amount of times possible
is definitely a good thing! Now we can click the green button and run our app! We should get something
like the following screenshot in our emulator!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 34
Conclusion Throughout the course of the previous two posts, we’ve successfully created a unit converter
application. Along the way, we learned about the Model-View-Controller (MVC) pattern, which allows
the Controller to interact as the mediator between the Model and View. We also learned about Java
enums, which group related constants together. We created our Converter class model and moved on to
the View. We learned about Spinners and adapters as well as how to use them effectively in Android.
Finally, we implemented the Controller to convert the input value from one unit to another.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 35
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 36
How to Create Simple Lists using ListViews Hello World! In this post, we’re going to learn how to use one of the most useful views in Android: the
ListView. We can use ListViews to present data in the form of a list. We can choose any kind of data as
input and display it however we want. That’s the power of a ListView. In this post, we’ll be creating a
simple app that presents a list showing all of the countries of the world.
Note: While Android now has a much more generic RecyclerView, since our task is going to be fairly
simple, we’ll stick to the ListView so we don’t have to deal with the additional complexities of the
RecyclerView. To summarize, a RecyclerView is more generic version of a GridView and ListView. It
allows developers to have greater power and control over the presentation of data and makes things
like animations easier to do. However, with this greater control, we have to implement many things
ourselves, such as a custom adapter, item layout, and view holder. For our purposes, a plain old ListView
will suffice. With great power comes great responsibility!
All of source code for this project is in a ZIP file here.
Creating the Project Let’s start up Android Studio and create a new project titled ListViewDemo. We can give any package
name we want and select Android 6.0 Marshmallow as our minimum SDK version. We’ll just need an
empty activity and we’ll leave the default names as is. We won’t need too much code to get our ListView
working. First, we’ll need open up the Activity’s layout file in res/layout. We can replace
the entire contents of the file with just the following code snippet:
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:id="@+id/listView" />
The above code will create a ListView for us and give it some properties. By setting the height and width
of a top-level UI element to be match_parent , we are telling Android to make this UI element take up
the entire screen, which is exactly what we want our ListView to do. We have some padding to the top
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 37
and bottom so that the text in the list items aren’t right up against the top of the screen, making it
difficult to read. Finally, we have the id property so that we can refer to this ListView when we get to
our source code.
For our model, we’ll be displaying all of the countries in the world, but we need a place to store that
information. We’ll keep it in our strings.xml file in res/values. Open up that file and paste the
following between the resources tag:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<string-array name="countries_array">
<item>Afghanistan</item>
<item>Albania</item>
<item>Algeria</item>
<item>American Samoa</item>
<item>Andorra</item>
<item>Angola</item>
<item>Anguilla</item>
<item>Antarctica</item>
<item>Antigua and Barbuda</item>
<item>Argentina</item>
<item>Armenia</item>
<item>Aruba</item>
<item>Australia</item>
<item>Austria</item>
<item>Azerbaijan</item>
<item>Bahrain</item>
<item>Bangladesh</item>
<item>Barbados</item>
<item>Belarus</item>
<item>Belgium</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 38
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<item>Belize</item>
<item>Benin</item>
<item>Bermuda</item>
<item>Bhutan</item>
<item>Bolivia</item>
<item>Bosnia and Herzegovina</item>
<item>Botswana</item>
<item>Bouvet Island</item>
<item>Brazil</item>
<item>British Indian Ocean Territory</item>
<item>British Virgin Islands</item>
<item>Brunei</item>
<item>Bulgaria</item>
<item>Burkina Faso</item>
<item>Burundi</item>
<item>Cambodia</item>
<item>Cameroon</item>
<item>Canada</item>
<item>Cape Verde</item>
<item>Cayman Islands</item>
<item>Central African Republic</item>
<item>Chad</item>
<item>Chile</item>
<item>China</item>
<item>Christmas Island</item>
<item>Cocos (Keeling) Islands</item>
<item>Colombia</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 39
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<item>Comoros</item>
<item>Congo</item>
<item>Cook Islands</item>
<item>Costa Rica</item>
<item>Cote d\'Ivoire</item>
<item>Croatia</item>
<item>Cuba</item>
<item>Cyprus</item>
<item>Czech Republic</item>
<item>Democratic Republic of the Congo</item>
<item>Denmark</item>
<item>Djibouti</item>
<item>Dominica</item>
<item>Dominican Republic</item>
<item>East Timor</item>
<item>Ecuador</item>
<item>Egypt</item>
<item>El Salvador</item>
<item>Equatorial Guinea</item>
<item>Eritrea</item>
<item>Estonia</item>
<item>Ethiopia</item>
<item>Faeroe Islands</item>
<item>Falkland Islands</item>
<item>Fiji</item>
<item>Finland</item>
<item>Former Yugoslav Republic of Macedonia</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 40
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<item>France</item>
<item>French Guiana</item>
<item>French Polynesia</item>
<item>French Southern Territories</item>
<item>Gabon</item>
<item>Georgia</item>
<item>Germany</item>
<item>Ghana</item>
<item>Gibraltar</item>
<item>Greece</item>
<item>Greenland</item>
<item>Grenada</item>
<item>Guadeloupe</item>
<item>Guam</item>
<item>Guatemala</item>
<item>Guinea</item>
<item>Guinea-Bissau</item>
<item>Guyana</item>
<item>Haiti</item>
<item>Heard Island and McDonald Islands</item>
<item>Honduras</item>
<item>Hong Kong</item>
<item>Hungary</item>
<item>Iceland</item>
<item>India</item>
<item>Indonesia</item>
<item>Iran</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 41
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<item>Iraq</item>
<item>Ireland</item>
<item>Israel</item>
<item>Italy</item>
<item>Jamaica</item>
<item>Japan</item>
<item>Jordan</item>
<item>Kazakhstan</item>
<item>Kenya</item>
<item>Kiribati</item>
<item>Kuwait</item>
<item>Kyrgyzstan</item>
<item>Laos</item>
<item>Latvia</item>
<item>Lebanon</item>
<item>Lesotho</item>
<item>Liberia</item>
<item>Libya</item>
<item>Liechtenstein</item>
<item>Lithuania</item>
<item>Luxembourg</item>
<item>Macau</item>
<item>Madagascar</item>
<item>Malawi</item>
<item>Malaysia</item>
<item>Maldives</item>
<item>Mali</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 42
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<item>Malta</item>
<item>Marshall Islands</item>
<item>Martinique</item>
<item>Mauritania</item>
<item>Mauritius</item>
<item>Mayotte</item>
<item>Mexico</item>
<item>Micronesia</item>
<item>Moldova</item>
<item>Monaco</item>
<item>Mongolia</item>
<item>Montenegro</item>
<item>Montserrat</item>
<item>Morocco</item>
<item>Mozambique</item>
<item>Myanmar</item>
<item>Namibia</item>
<item>Nauru</item>
<item>Nepal</item>
<item>Netherlands</item>
<item>Netherlands Antilles</item>
<item>New Caledonia</item>
<item>New Zealand</item>
<item>Nicaragua</item>
<item>Niger</item>
<item>Nigeria</item>
<item>Niue</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 43
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<item>Norfolk Island</item>
<item>North Korea</item>
<item>Northern Marianas</item>
<item>Norway</item>
<item>Oman</item>
<item>Pakistan</item>
<item>Palau</item>
<item>Panama</item>
<item>Papua New Guinea</item>
<item>Paraguay</item>
<item>Peru</item>
<item>Philippines</item>
<item>Pitcairn Islands</item>
<item>Poland</item>
<item>Portugal</item>
<item>Puerto Rico</item>
<item>Qatar</item>
<item>Reunion</item>
<item>Romania</item>
<item>Russia</item>
<item>Rwanda</item>
<item>Sqo Tome and Principe</item>
<item>Saint Helena</item>
<item>Saint Kitts and Nevis</item>
<item>Saint Lucia</item>
<item>Saint Pierre and Miquelon</item>
<item>Saint Vincent and the Grenadines</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 44
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<item>Samoa</item>
<item>San Marino</item>
<item>Saudi Arabia</item>
<item>Senegal</item>
<item>Serbia</item>
<item>Seychelles</item>
<item>Sierra Leone</item>
<item>Singapore</item>
<item>Slovakia</item>
<item>Slovenia</item>
<item>Solomon Islands</item>
<item>Somalia</item>
<item>South Africa</item>
<item>South Georgia and the South Sandwich Islands</item>
<item>South Korea</item>
<item>South Sudan</item>
<item>Spain</item>
<item>Sri Lanka</item>
<item>Sudan</item>
<item>Suriname</item>
<item>Svalbard and Jan Mayen</item>
<item>Swaziland</item>
<item>Sweden</item>
<item>Switzerland</item>
<item>Syria</item>
<item>Taiwan</item>
<item>Tajikistan</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 45
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<item>Tanzania</item>
<item>Thailand</item>
<item>The Bahamas</item>
<item>The Gambia</item>
<item>Togo</item>
<item>Tokelau</item>
<item>Tonga</item>
<item>Trinidad and Tobago</item>
<item>Tunisia</item>
<item>Turkey</item>
<item>Turkmenistan</item>
<item>Turks and Caicos Islands</item>
<item>Tuvalu</item>
<item>Virgin Islands</item>
<item>Uganda</item>
<item>Ukraine</item>
<item>United Arab Emirates</item>
<item>United Kingdom</item>
<item>United States</item>
<item>United States Minor Outlying Islands</item>
<item>Uruguay</item>
<item>Uzbekistan</item>
<item>Vanuatu</item>
<item>Vatican City</item>
<item>Venezuela</item>
<item>Vietnam</item>
<item>Wallis and Futuna</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 46
238
239
240
241
242
243
<item>Western Sahara</item>
<item>Yemen</item>
<item>Yugoslavia</item>
<item>Zambia</item>
<item>Zimbabwe</item>
</string-array>
The above XML just creates a string array that holds all of the countries of the world. One of the benefits
of putting this in the res folder is that we can localize the strings to any particular language. This is just
one of the many benefits of the Android resource system. To summarize the Android resource
system, we can provide string files in different languages, layouts in different orientations, and other
specifications and Android will choose the one that pertains to the user’s current situation at runtime.
We just have to be thorough in providing Android with all of these resources. We can do this
by appending a very specific sequence of characters after the “main” name of the folder (i.e. values,
layout, drawable). For example, suppose we want to have a different dimensions file for tablet screens
or screens with a width of 820 density-independent pixels (dp). We can create another folder called
values and append “-w820dp” to the folder name. Then we can add any files to this folder and Android
will only use these values if the device which runs our app has a width of 820dp.
Now that we have our data, we can actually link up our model and our view. Open up
the MainActivity.java file and we’ll create a few member variables to hold data. We’ll create one for the
ListView and another for the ArrayAdapter:
1
2
private ListView listView;
private ArrayAdapter<CharSequence> adapter;
The reason that the ArrayAdapter is a generic of CharSequence instead of String is because we’ll be
grabbing string array from the strings.xml file. We can have a String array since CharSequence is an
interface and String implements CharSequence, hence we can use String. Now that we have our
member variables set up, we actually need to configure our adapter and ListView. Doing this setup in
onCreate(Bundle) is ideal because that method is going to be called before the screen is actually visible
to the user, so data will be ready as soon as the view is presented. In our onCreate(Bundle) method,
let’s add the following code:
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 47
1
2
3
listView = (ListView) findViewById(R.id.listView);
adapter = ArrayAdapter.createFromResource(this, R.array.countries_array, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
The first line grabs a reference to the ListView. Then, we need to create a new adapter using a static
utility method on ArrayAdapter called ArrayAdapter.createFromResource(Context, int, int) .
The purpose of the method is to take the contents of the given string array and put it in the layout. The
first parameter is the Context. In Android, the Context is the current state of the application. We use it
to initialize views or to get something from our app resources or the system’s resources. Since Activity is
a subclass of Context, we can pass in this as a valid object. The second parameter is the string array of
countries. Since this is in our resources system and an array, we need to use the R class to access it via
R.array.countries_array . The final parameter is the layout that we want to populate with the string
array. In our case, we’re just using Android’s simplest ListView layout that consists of just a single
TextView. Finally, we set the ListView’s adapter to be the one we just created. Now we should be able to
run our application and see the results!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 48
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 49
Setting up a ListView to display data is great, but we want some user interaction in our ListView. For
example, when a user taps on a list item, we want to do something about it. To do this, we need to set
up an event listener to notify us when the user taps on a list item. Event listeners work by allowing us to
register and run a callback method when the event occurs. That callback method is in the form of an
interface we pass in to the listener. There are several ways we can choose to implement an event
listener: create a separate class that implements that interface and pass in a new instance of that
class; use Java’s anonymous inner classes; or pass in this and force our Activity to implement the
interface and the callback method. For our case, it’ll look cleaner if we do the latter. Note that there are
many different listeners so we have to choose the appropriate one for the event we want to receive
information about. We can set the listener in the onCreate(Bundle) method
by executing listView.setOnItemClickListener(this); to set up our listener. This will make Android
Studio throw an error and so we have to implement the interface in our Activity. Our class declaration
changes to public class MainActivity extends AppCompatActivity implements
AdapterView.OnItemClickListener. We also have to implement the callback method for in our class on
the class level like the following.
1
2
3
4
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(this, adapter.getItem(position), Toast.LENGTH_SHORT).show();
}
You’ll notice we use Toasts in the above code. A Toast just shows a transparent overlay. We need to
pass in a Context, which is just this again. The second parameter is the text we want to show.
We’re essentially grabbing the text at that position in the ListView. This means that when the user taps
on a ListView item, we’ll just have a Toast that displays what’s on the ListView. The final parameter is
just a constant corresponding to how long we want to show the Toast. However, the makeText(Context,
CharSequence, int) method just creates a Toast, we have to show it by calling show() on the newly
created Toast message. This is all it takes to configure our ListView! We can see the final result below.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 50
Conclusion
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 51
In this post, we covered the basics of ListView and how to populate it with items from an adapter. For
our purposes, we just used a string array in our strings.xml file. As a side note, we learned more about
the Android resource system as well. In addition to populating a ListView, we also learned how to
configure event listeners to be notified when some event happens regarding the ListView. We used that
to display a Toast message to the user.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 52
How to Create Lists using RecyclerView In this post, we’re going to look at the newer RecyclerView to build more complicated lists and list items.
RecyclerView was a recently added UI item that gave developers fine-grain control and power over their
lists or grids. However, as we’ll find out, that great power comes with great responsibility. RecyclerView
doesn’t provide much of the basic functionality that ListView does, so we’ll have to implement it
ourselves. In this post, we’ll be implementing a layout with an icon on the left and descriptive text on
the right.
Note: Before we create the project, we’ll need to make sure we have the support library downloaded.
The support library was released to allow for greater backwards-compatibility among the range of
physical devices and their different Android versions in the ecosystem. Instead of waiting for a software
update to the latest version of Android, a backwards-compatible version would be released in the
support library that developers can use right away. Open up the SDK manager and click on the SDK Tools
tab. Make sure Android Support Library and Android Support Repository are checked. If not, check
them, wait for the packages to download, and restart Android Studio. Now we can begin!
All of the source code for this project can be found here.
Creating the Project Let’s open up Android Studio and create a new project called RecyclerViewDemo (give it any package
name you want!) The minimum SDK version should be Android 6.0 Marshmallow and we can created an
empty activity with the default values. Before we can use RecyclerView, we need to add that component
to our build files since it’s in the support library. In the Project view on the left, open up the Gradle
Scripts node and the build.gradle (Module: app) file. Note that there is a build.gradle (Project:
RecyclerViewDemo), but that serves a different purpose! Towards the bottom, there should be a section
labeled dependencies. This tells Android what our app needs to function properly. Between the curly
braces, add the following line of code: compile 'com.android.support:recyclerview-v7:23.1.+' .
This line will tell Android to include RecyclerView in our project. The first number MUST correspond to
the compileSdkVersion number we see at the top of the file. When you type that line, it might be
highlighted in yellow and we can see the suggestion if we hover our cursor over it. This warning can
generally be discarded since it just provides us with the warning that we should be using the
absolute latest version of RecyclerView. We can change the plus (+) symbol to the same version as the
dependency on the appcompat-v7 library in the same dependencies block. After we make changes to
this file, towards the top of the main coding area, we should have an option to “Sync Now” and gradle
will recompile our project with the modified dependencies.
Now we can start building the view, which turns out to be as simple as ListView. Open up the
activity_main.xml layout file in res/layout and replace the contents with the following:
1 <?xml version="1.0" encoding="utf-8"?>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 53
2
3
4
5
6
7
8
9
10
11
<android.support.v7.widget.RecyclerView
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:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:scrollbars="vertical"
android:id="@+id/recyclerView"
tools:context=".MainActivity" />
This looks strikingly similar to the ListView, however, we have to specify the scrollbars property since
RecyclerView is flexible to be used either horizontally or vertically. This is all we’ll need for our main
view.
We need to create a layout for each of our list items, but, before we do that, we need to define a border
for each of our list items. ListView did this for us, but we need to do this as well. Right-click on the
res/drawable folder and go to New → Drawable Resource File. We’ll name it border and replace its
contents with the following XML code to define a border and background.
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<stroke
android:width="1dp"
android:color="#CCCCCC" />
</shape>
Now we need to create a layout for each list item in our RecyclerView. To do this, we can right-click on
the res/layout folder and go to New → Layout Resource File. We can call it list_item and make the Root
Element a RelativeLayout. Afterwards, we’ll click OK and we should have a new file generated for us in
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 54
the the design view. In this view, we can simply drag-and-drop UI items from the palette on the left and
change any properties in the properties pane on the bottom left. This will generate the underlying XML
code for us. We’ll need an ImageView for the icon and a TextView for the description. Click on the Text
tab at the bottom of the main pane and replaced the contents with the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightLarge"
android:background="@drawable/border">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:contentDescription="Icon" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toEndOf="@id/imageView"
android:gravity="center_vertical"
android:textSize="16sp"/>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 55
24
25
</RelativeLayout>
The above code will properly place the ImageView and TextView within the RelativeLayout and set the
background of the RelativeLayout to the border we defined. Now we can work on the model. We’ll need
some object to represent an image and a text description. We can create a class to encapsulate this
data. Find the MainActivity.java file, and, in the package above it (NOT ending in (androidTest)), go
to New → Java Class and we’ll call it IconData. We’ll need two simple fields: a String text and an int
resource id. Put the following in the IconData class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class IconData {
private String description;
private int imgId;
public IconData(String description, int imgId) {
this.description = description;
this.imgId = imgId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getImgId() {
return imgId;
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 56
20
21
22
23
24
25
}
public void setImgId(int imgId) {
this.imgId = imgId;
}
}
Suppose we have member variables in a class and we want getters and setters for these fields. We can
actually have Android Studio generate them AND parameterized constructors as well! On the top menu
bar, we can go to Code → Generate, then select Getters and Setters, and select both fields (using Shift
key) and click OK. We can also generate parameterized constructors in the same way, except choosing
Constructor in the popup.
Now that we have a container for our data, we can create the custom adapter that will show our data.
Create a new class in the same package and call it IconAdapter. Inside of it, we’ll create a public static
inner class ViewHolder whose superclass is RecyclerView.ViewHolder. We’ll get an error, but we can fix
it by clicking our cursor somewhere in the error and press Alt+Enter to generate a constructor matching
super. Since we’re using a more complicated layout, we’ll need to extract and keep references to
views by adding them as member variables and calling findViewById(int) on the view passed into the
constructor.
1
2
3
4
5
6
7
8
9
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView textView;
public ViewHolder(View itemView) {
super(itemView);
this.imageView = (ImageView) itemView.findViewById(R.id.imageView);
this.textView = (TextView) itemView.findViewById(R.id.textView);
}
}
The ViewHolder pattern is a way to make lists very efficient by recycling the list items that go off the
screen. When we have a list, we create enough memory and resources for list items on the screen plus a
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 57
few more for smooth scrolling. When list items, go off the top of the screen, the idea is to recycle those
views by replacing their contents with that of a new data item and putting them at the bottom. This
allows us to use the least amount of memory while still giving the user a smooth experience. Now that
we’ve instantiated our ViewHolder, we’ll need to make our IconAdapter actually extend
RecyclerView.Adapter with the generic being our ViewHolder. We’ll have to implement the abstract
methods soon, but, currently, our entire IconAdapter class should look like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class IconAdapter extends RecyclerView.Adapter<IconAdapter.ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public TextView textView;
public ViewHolder(View itemView) {
super(itemView);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 58
23
24
25
26
27
28
this.imageView = (ImageView) itemView.findViewById(R.id.imageView);
this.textView = (TextView) itemView.findViewById(R.id.textView);
}
}
}
Now we need to start implementing the adapter and we can take full use of the ViewHolder pattern to
make this task easy. We first need some way to store data, and we’ll use an array of IconData for that
purpose. After creating a member variable and generating a parameterized constructor, we get the
following:
1
2
3
4
5
6
7
public class IconAdapter extends RecyclerView.Adapter<IconAdapter.ViewHolder> {
private IconData[] data;
public IconAdapter(IconData[] data) {
this.data = data;
}
...
Now we can start implementing the primary methods. getItemCount() can just return the length of our
data array:
1
2
3
4
5
6
...
@Override
public int getItemCount() {
return data.length;
}
...
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 59
onCreateViewHolder(ViewGroup, int) will need to “inflate” our custom list item view from our xml
layout. However, the LayoutInflater needs a Context object and we can’t provide one from just our
adapter, but we can get one from the ViewGroup object. Then we can inflate the view and get a View
object. As the name suggests, we’ll also need to create a new ViewHolder object that will be associated
with that row and we can return that object:
1
2
3
4
5
6
7
8
9
...
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View listItem= layoutInflater.inflate(R.layout.list_item, parent, false);
ViewHolder viewHolder = new ViewHolder(listItem);
return viewHolder;
}
...
onBindViewHolder(ViewHolder, int) is where we actually populate the views in the list item row with
data. Because of the ViewHolder pattern, this is as simple as getting our ImageView and TextView from
our ViewHolder and setting their image or text. Within the parentheses of setText(String) and
setImageResource(int) , we grab the single IconData object at that particular position and extract its
description or image ID:
1
2
3
4
5
6
7
...
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.textView.setText(data[position].getDescription());
holder.imageView.setImageResource(data[position].getImgId());
}
...
Now our adapter is complete and we can begin using it! Let’s go to MainActivity.java and look in the
onCreate(Bundle) method. Inside, we should just have a class to the super class and a method call to
setContentView(int) that will associate this Activity with the given layout. We need to put any data that
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 60
accesses views after the call to setContentView(int) since, before then, there is no inflated view! Let’s
add some code after that call so our method looks like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IconData[] data = new IconData[] {
new IconData("Delete", android.R.drawable.ic_delete),
new IconData("Alert", android.R.drawable.ic_dialog_alert)
};
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
IconAdapter adapter = new IconAdapter(data);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
First, we create an array of static IconData that will be displayed in the RecyclerView. You might notice
on the left margin Android Studio will show a small thumbnail icon of each of the icons that we use for
each IconData object. Then, we grab a reference to the RecyclerView and create a new IconAdapter
using that array of IconData. Now we can set properties on the RecyclerView. The first is there for
performance: if the RecyclerView isn’t going to change in size, then Android can make it even smoother!
The next is the layout of the RecyclerView. It can present a list (LinearLayoutManager), a grid
(GridLayoutManager), or a staggered grid (StaggeredGridLayoutManager). We’ll be presenting a list so
we’ll use a LinearLayoutManager. Finally, we set the adapter of our RecyclerView to be our IconAdapter.
Running our application, we should see the following.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 61
Feel free to use any text description or icons (many are in android.R.drawable class and start with
“ic_”!). Add as many or as few items you want! This will give you a chance to see what you can do with a
RecyclerView.
Conclusion
In this post, we learned about the more power and flexible RecyclerView. However, with this power, we
had to do more work ourselves to get the desired result. For instance, we had to create a custom list
item layout and corresponding ViewHolder. Then we had to create an adapter that used that
ViewHolder. then we can finally use that adapter in our RecyclerView. When debating on
using RecyclerView or a plain ListView, generally choose the RecyclerView since it’s intended to replace
ListView. On the other hand, if you’re presenting a small, simple list, a ListView might fit your needs to
get that data presented quickly.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 62
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 63
Introduction to SQLite Databases on Android Devices Hello World! In this post, we’re going to cover the basics of SQLite and databases. SQLite is the main
database backend that Android uses to manage complicated data. It provides more structure
and functionality than storing data in a file or anywhere else. Database backends are widely used to
store data locally on the device and sync with a web server, for example. But before we can do this, we
need to learn more about databases and SQLite, which defines it’s own “programming language” and
system. We’ll learn about what databases are and how to do basic CRUD (Create, Read, Update, and
Delete) operations with them in SQLite.
The database itself can be downloaded here.
Prerequisites It is highly recommended that this tutorial be followed on a UNIX-based operating system like Linux or
Mac OS X. You can download Ubuntu Linux here and a Virtual Machine here to run it on to prevent you
from going through the ordeal of dual-booting. While it’s certainly possible to do this whole tutorial on
Windows, the commands I’ll be using are UNIX bash commands so having access to a shell is ideal.
We’ll obviously need SQLite! You can download it here and find all kinds of great documentation on the
website. We just need the shell executable and not the analyzer. Unzipping the file will give the actual
executable. We’ll want to put it in a folder so we have access to it or we can copy it to a more useful
directory by running the following terminal command: sudo cp sqlite3 /usr/local/bin and we should be
able to access the executable from anywhere. We can test this by running sqlite3 --version and we
should get the current version of SQLite.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 64
What is a Database?
A database is an organized collection of information or data. More specifically, SQLite is a relational
database, meaning that each datum conforms to a highly-specific format or schema. We can visualize
databases as being giant tables, with potentially many rows and columns. However, databases are much
more powerful than just spreadsheets because we can do complicated queries to retrieve exactly what
data we want, subject to any number of constraints. More concretely, databases are just files that
hold this organized data.
Databases primarily consist of many tables; each table is defined by its columns. Each column needs a
name and a data type, much like in Java. Each table can then have many hundreds of rows, which each
row is a tuple whose elements correspond to the columns. Suppose I had a table whose columns were
simply an artist name and an album name, both as text data. Then an example of a row would be (“Led
Zeppelin”, “Led Zeppelin IV”). Note that the order that data appears in the tuple must correspond to the
order that columns were defined.
Let’s see what this looks like in action. Open up a terminal and navigate to any directory you want to
create the database file in. Then type sqlite3 my-db.sqlite and we should see the SQLite prompt like in
the following figure.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 65
Now that we have a blank database, we can create a table with a very specific schema. But before we do
that, we need more information on what the data types for SQLite columns. They’re enumerated in the
table below.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 66
In this post, we’ll only be using INTEGER, REAL and TEXT since they’re the most common, but NULL and
BLOB exist as well. These data types store exactly what you think they store based on their names:
INTEGER, REAL, TEXT. We’ll need to use them when declaring our table and columns. Let’s declare a
table called student with an ID, Name, Age, and GPA. This will illustrate using working with all of the
common data types. Type the following into SQLite:
1
2
3
4
5
6
CREATE TABLE student(
_id INTEGER,
name TEXT NOT NULL,
age INTEGER,
gpa REAL
);
The will create the table named students with the given schema. At any time, we can view all of
our tables by typing .tables and we can view the full schema for all tables by typing .schema . You’ll
notice that the name column has some additional properties. By putting NOT NULL after a column type,
we’re telling SQLite that any row inserted into this table must have a value for the name column. There
are more helpful properties that we’re going to get to later. You’ll also notice that we have this _id
column. It’s common practice to have some identification for each row so we can uniquely identify that
particular row for being deleted or updated, for example. Currently, we have to manage this uniqueness
ourselves, but when we talk about additional properties at the end of this post, we can actually have
SQLite manage this for us.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 67
Create Now that we have a table, let’s actually add some rows to it! To insert rows, we have to begin
a transaction since we’re modifying the database. We can query all we want, but if we’re adding new
rows, updating existing rows, or deleting rows, we need to use a transaction to do so. All transactions
begin with BEGIN; and end with COMMIT; and we’ll be wrapping all of our potential database changes
in these markers like so. Execute the following SQLite commands.
1
2
3
BEGIN;
INSERT INTO student(_id, name, age, gpa) VALUES(1, 'John Doe', 18, 3.81);
COMMIT;
Let’s talk about the insert statement. First we have to say what table we’re inserting rows into! Then
comes a comma-delimited list of columns in parentheses that we want to insert into and then the values
that we want to insert into those columns. After this, we should have our row inserted into the
database! We’ll learn how to read it later, but let’s add a few more rows and go over different styles of
insert statements.
The purpose of the above syntax is to specify that we’re going to be inserting exactly these values into
these columns of the table. If we’re going to provide values for each column, we can omit the comma-
separated list of columns. Execute the following statement:
1
2
3
BEGIN;
INSERT INTO student VALUES(2, 'Jane Doe', 20, 3.57);
COMMIT;
We can see that the columns are omitted and SQLite associates the first value with the first column and
so on. This is why the ordering of the columns in the table is important! It allows us to do this kind of
shorthand. In addition to this method of inserting, we can insert only values into particular rows.
Suppose we wanted to omit one’s age for privacy reasons. As long as that column isn’t marked with the
NOT NULL attribute, we can do something like the following. Execute the following statement:
1
2
3
BEGIN;
INSERT INTO student(_id, name, gpa) VALUES(3, 'John Smith', 3.38);
COMMIT;
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 68
We can see that we omitted the age column so this row will not have an age if we were to query for it.
Note that the age isn’t simply zero; the age doesn’t exist! Now we have 3 rows in our database! Let’s
learn how to access these columns next.
Read Now that we have some data, let’s see how we can query for it. Let’s first retrieve all of rows in our only
table. Note that we’re not actually modifying the database, so we won’t need to use a transaction.
Instead, we can use a select statement. Execute the following statement: SELECT * FROM student; We
should see 3 rows where the columns are delimited by a pipe character |.
The star means to get all of the columns. We can choose to only retrieve specific columns by
replacing the asterisk in the select statement with comma-delimited columns names like SELECT name
FROM student; and we can only get all of the names. Now suppose we want to retrieve rows that fit a
criterion. We can add a where clause and search for more specific data like the following query that
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 69
only retrieves student names who have a GPA of 3.5 or higher: SELECT name FROM student WHERE gpa
>= 3.5;
This only scratches the surface of the kinds of powerful queries we can do with databases!
Update Suppose we want to make a change to an existing row. We can do that by using the update statement.
For example, suppose John Smith did very well in his classes so we need to update his GPA. We can do
that using the following.
1
2
3
BEGIN;
UPDATE student SET gpa=3.52 WHERE _id=3;
COMMIT;
Note that this does change the database so we needed to use a transaction. You can see we use the
unique id of John to change his GPA. We could also have used the condition where name was John
Smith, or, better yet, we could use wildcard characters, but we won’t get into those in this post. After
the SET keyword, we could provide any number of columns to change, delimited by commas.
Any columns not in the list won’t be changed.
Delete Suppose we want to delete a row from our database. This is going to look similar to update, except we
won’t need any values. Suppose John Smith transferred to another school and we need to remove him
from our database.
1
2
3
BEGIN;
DELETE FROM student WHERE _id=3;
COMMIT;
The delete statement is fairly straightforward and will remove any row from our database that satisfies
the where clause.
Additional Properties I mentioned earlier that it’s common to have an ID column where each row can be identified uniquely.
We’ve been managing it ourselves, but we can have SQLite do this for us. This happens at database
creation though, so we’ll also learn how to safely transfer data between tables so we don’t lose all of
our users’ data! First, we’ll rename the old table and create the new one. You’ll notice that we now use
the additional attributes PRIMARY KEY and AUTOINCREMENT. The former means that this column holds
unique values for each row and the latter means to automatically increment this column if it isn’t given a
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 70
specific value in the insert statement. Then we copy over all of our data and delete the old table. All of
this looks like the following:
1
2
3
4
5
6
BEGIN;
ALTER TABLE student RENAME TO tmp_student;
CREATE TABLE student (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, gpa REAL);
INSERT INTO student SELECT * FROM tmp_student;
DROP TABLE tmp_student;
COMMIT;
Now we can use insert statements like INSERT INTO student(name, age, gpa) VALUES('Michael Smith',
20, 3.77) and there will be a new row with an ID that’s one greater than the previous row, even if that
row is deleted! We’re guaranteed unique values for rows and PRIMARY KEY helps to enforce that as
well.
This technique can be used whenever we want to change our table schema drastically. We can use
ALTER TABLE to rename our old table, create a new one with a new schema and copy over all of our old
data and remove the old table. This method is safe, secure, and prevents your users from losing all of
their data!
Conclusion In this post, we learned about relational databases (RDBs) and SQLite. We covered the data types of the
language and how to create tables. We then learned the basic create, read, update, and delete
(CRUD) operations that we can perform. Then we finished with some basic techniques and additional
modifiers. SQLite is the backend that Android’s ContentProviders use to store data and they can be
synced with data from web servers or other providers.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 71
How to Create Toolbars and Menus in Android Hello World! In this post, we’re going to talk about Toolbars from a design perspective and then we’ll
actually build an app that uses Toolbars. We’re all familiar with the top bar of an app that houses the
logo or name of the app as well as a navigation drawer perhaps. One of the most prominent items on
that top bar are the actions and the action overflow menu! We’re going to cover how we can create this
Toolbar and put actions on it!
Download the source code for this post here. You’ll need some of the resources in the project later on!
Before we get into the source code for building menus, I want to take a short bit of time to think about
our toolbar actions and do some design instead of jumping straight into developing. We’ll be focusing on
the actions that can go on the Toolbar.
When considering whether or not an action should go on the Toolbar, follow the FIT principle. If an
action is Frequent, Important, or Typical, it should go on the Toolbar. By Frequent, ask yourself, will
people use this action almost every time they come to this screen? For example, in a social media app,
the action to publish a new post would definitely be Frequent. By Important, ask yourself, does this
action align with the main purpose of my app? For example, in an email app, composing a new email is
absolutely important and the entire point of the app! By Typical, ask yourself, given the context of this
screen, would it be strange if this action were missing or hidden somewhere? For example, in a photo
editing app, users would be surprised if the ability to edit photos were hidden away in the action
overflow.
If you’ve answered yes to any of these questions, put that action in the Toolbar! Keep the number
of actions on the Toolbar to a minimum so that your app looks very clean and fresh. Nothing puts off of
your app than a million items on a small Toolbar!
Now that we’ve thought carefully about the design of our toolbar, let’s actually begin developing one!
Let’s create a project in Android Studio and call it ToolbarDemo. We’ll just need a blank Activity called
MainActivity. We’re going to create a quick ListView for the sake of having some content. If you’d like to
know more about ListViews in-depth, check out the blog post on simple ListViews. Anyways, let’s open
up res/values/strings.xml and add the following code block to serve as our main content for the
ListView:
1
2
3
4
5
<string-array name="countries_array">
<item>Afghanistan</item>
<item>Albania</item>
<item>Algeria</item>
<item>American Samoa</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 72
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<item>Andorra</item>
<item>Angola</item>
<item>Anguilla</item>
<item>Antarctica</item>
<item>Antigua and Barbuda</item>
<item>Argentina</item>
<item>Armenia</item>
<item>Aruba</item>
<item>Australia</item>
<item>Austria</item>
<item>Azerbaijan</item>
<item>Bahrain</item>
<item>Bangladesh</item>
<item>Barbados</item>
<item>Belarus</item>
<item>Belgium</item>
<item>Belize</item>
<item>Benin</item>
<item>Bermuda</item>
<item>Bhutan</item>
<item>Bolivia</item>
<item>Bosnia and Herzegovina</item>
<item>Botswana</item>
<item>Bouvet Island</item>
<item>Brazil</item>
<item>British Indian Ocean Territory</item>
<item>British Virgin Islands</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 73
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<item>Brunei</item>
<item>Bulgaria</item>
<item>Burkina Faso</item>
<item>Burundi</item>
<item>Cambodia</item>
<item>Cameroon</item>
<item>Canada</item>
<item>Cape Verde</item>
<item>Cayman Islands</item>
<item>Central African Republic</item>
<item>Chad</item>
<item>Chile</item>
<item>China</item>
<item>Christmas Island</item>
<item>Cocos (Keeling) Islands</item>
<item>Colombia</item>
<item>Comoros</item>
<item>Congo</item>
<item>Cook Islands</item>
<item>Costa Rica</item>
<item>Cote d\'Ivoire</item>
<item>Croatia</item>
<item>Cuba</item>
<item>Cyprus</item>
<item>Czech Republic</item>
<item>Democratic Republic of the Congo</item>
<item>Denmark</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 74
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<item>Djibouti</item>
<item>Dominica</item>
<item>Dominican Republic</item>
<item>East Timor</item>
<item>Ecuador</item>
<item>Egypt</item>
<item>El Salvador</item>
<item>Equatorial Guinea</item>
<item>Eritrea</item>
<item>Estonia</item>
<item>Ethiopia</item>
<item>Faeroe Islands</item>
<item>Falkland Islands</item>
<item>Fiji</item>
<item>Finland</item>
<item>Former Yugoslav Republic of Macedonia</item>
<item>France</item>
<item>French Guiana</item>
<item>French Polynesia</item>
<item>French Southern Territories</item>
<item>Gabon</item>
<item>Georgia</item>
<item>Germany</item>
<item>Ghana</item>
<item>Gibraltar</item>
<item>Greece</item>
<item>Greenland</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 75
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<item>Grenada</item>
<item>Guadeloupe</item>
<item>Guam</item>
<item>Guatemala</item>
<item>Guinea</item>
<item>Guinea-Bissau</item>
<item>Guyana</item>
<item>Haiti</item>
<item>Heard Island and McDonald Islands</item>
<item>Honduras</item>
<item>Hong Kong</item>
<item>Hungary</item>
<item>Iceland</item>
<item>India</item>
<item>Indonesia</item>
<item>Iran</item>
<item>Iraq</item>
<item>Ireland</item>
<item>Israel</item>
<item>Italy</item>
<item>Jamaica</item>
<item>Japan</item>
<item>Jordan</item>
<item>Kazakhstan</item>
<item>Kenya</item>
<item>Kiribati</item>
<item>Kuwait</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 76
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<item>Kyrgyzstan</item>
<item>Laos</item>
<item>Latvia</item>
<item>Lebanon</item>
<item>Lesotho</item>
<item>Liberia</item>
<item>Libya</item>
<item>Liechtenstein</item>
<item>Lithuania</item>
<item>Luxembourg</item>
<item>Macau</item>
<item>Madagascar</item>
<item>Malawi</item>
<item>Malaysia</item>
<item>Maldives</item>
<item>Mali</item>
<item>Malta</item>
<item>Marshall Islands</item>
<item>Martinique</item>
<item>Mauritania</item>
<item>Mauritius</item>
<item>Mayotte</item>
<item>Mexico</item>
<item>Micronesia</item>
<item>Moldova</item>
<item>Monaco</item>
<item>Mongolia</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 77
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<item>Montenegro</item>
<item>Montserrat</item>
<item>Morocco</item>
<item>Mozambique</item>
<item>Myanmar</item>
<item>Namibia</item>
<item>Nauru</item>
<item>Nepal</item>
<item>Netherlands</item>
<item>Netherlands Antilles</item>
<item>New Caledonia</item>
<item>New Zealand</item>
<item>Nicaragua</item>
<item>Niger</item>
<item>Nigeria</item>
<item>Niue</item>
<item>Norfolk Island</item>
<item>North Korea</item>
<item>Northern Marianas</item>
<item>Norway</item>
<item>Oman</item>
<item>Pakistan</item>
<item>Palau</item>
<item>Panama</item>
<item>Papua New Guinea</item>
<item>Paraguay</item>
<item>Peru</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 78
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<item>Philippines</item>
<item>Pitcairn Islands</item>
<item>Poland</item>
<item>Portugal</item>
<item>Puerto Rico</item>
<item>Qatar</item>
<item>Reunion</item>
<item>Romania</item>
<item>Russia</item>
<item>Rwanda</item>
<item>Sqo Tome and Principe</item>
<item>Saint Helena</item>
<item>Saint Kitts and Nevis</item>
<item>Saint Lucia</item>
<item>Saint Pierre and Miquelon</item>
<item>Saint Vincent and the Grenadines</item>
<item>Samoa</item>
<item>San Marino</item>
<item>Saudi Arabia</item>
<item>Senegal</item>
<item>Serbia</item>
<item>Seychelles</item>
<item>Sierra Leone</item>
<item>Singapore</item>
<item>Slovakia</item>
<item>Slovenia</item>
<item>Solomon Islands</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 79
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
<item>Somalia</item>
<item>South Africa</item>
<item>South Georgia and the South Sandwich Islands</item>
<item>South Korea</item>
<item>South Sudan</item>
<item>Spain</item>
<item>Sri Lanka</item>
<item>Sudan</item>
<item>Suriname</item>
<item>Svalbard and Jan Mayen</item>
<item>Swaziland</item>
<item>Sweden</item>
<item>Switzerland</item>
<item>Syria</item>
<item>Taiwan</item>
<item>Tajikistan</item>
<item>Tanzania</item>
<item>Thailand</item>
<item>The Bahamas</item>
<item>The Gambia</item>
<item>Togo</item>
<item>Tokelau</item>
<item>Tonga</item>
<item>Trinidad and Tobago</item>
<item>Tunisia</item>
<item>Turkey</item>
<item>Turkmenistan</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 80
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
<item>Turks and Caicos Islands</item>
<item>Tuvalu</item>
<item>Virgin Islands</item>
<item>Uganda</item>
<item>Ukraine</item>
<item>United Arab Emirates</item>
<item>United Kingdom</item>
<item>United States</item>
<item>United States Minor Outlying Islands</item>
<item>Uruguay</item>
<item>Uzbekistan</item>
<item>Vanuatu</item>
<item>Vatican City</item>
<item>Venezuela</item>
<item>Vietnam</item>
<item>Wallis and Futuna</item>
<item>Western Sahara</item>
<item>Yemen</item>
<item>Yugoslavia</item>
<item>Zambia</item>
<item>Zimbabwe</item>
</string-array>
Before we go editing the view, we need to configure some things regarding styles first. Open up
res/values/styles.xml. In this file, we can configure any special properties that we want our app to have.
In our case, we need to remove the system-provided action bar so we can use the more flexible Toolbar
widget. In order to do this, we need to add a few properties to this file. The end result should look like
the following.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>
Now that we’ve added that, let’s go into our main layout in res/layout/activity_main.xml. We need to
position our Toolbar before our ListView, and, in accordance to Material Design, it should be above our
ListView as well. The best layout to accomplish our task is the LinearLayout. We can declare one as the
top-level element and set its orientation to be vertical so we can put our Toolbar first, then our ListView
after it. Replace the contents of the layout file with the following:
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 82
9
10
11
12
13
14
15
16
17
18
19
20
21
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:elevation="4dp"
android:background="@color/colorPrimary"/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView" />
</LinearLayout>
You can see that we have a minHeight and a layout_height. By including both properties, we can let
our Toolbar grow in size, like on tablet layouts, but not be less than the Material Design guideline for
size. This helps us achieve the best of both worlds. The question mark syntax queries the system’s value
of actionBarSize and sets our Toolbar’s minHeight to be in accordance with what the system uses. This
enforces conformity among apps, which is always good for your user!
We can use the elevation property on the Toolbar so that it appears to be “above” the ListView. All of
the shadows will be drawn for us! Toolbars also need to have a color: the primary color of our
application. Each application, according to Material Design, has a primary color for the most common
elements, a dark primary color for the status bar, and an accent color for EditText underlines and other
smaller elements. The Toolbar is definitely a primary element so we should have it’s background be our
primary color. We can change these colors by editing the res/values/colors.xml file. Now that this is
configured, we should see a view similar to below if we click on the design tab.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 83
Now we can start populating our ListView, and, to make it interesting, let’s add listener so that our app
displays a Toast message when the user clicks on an ListView item! Add the following code
to the MainActivity class.
1
2
3
4
5
6
7
8
9
private ListView listView;
private ArrayAdapter<CharSequence> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 84
10
11
12
13
14
15
16
17
18
19
listView = (ListView) findViewById(R.id.listView);
adapter = ArrayAdapter.createFromResource(this, R.array.countries_array, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(this, adapter.getItem(position), Toast.LENGTH_SHORT).show();
}
Now let’s run our app we should see a very simple app with a populated ListView. But now let’s get
more into actually using the Toolbar. Let’s add a refresh menu item to the Toolbar and have it also
display a Toast message.
One of the best ways to do this is to create an XML file that describes our menu. Right-click on the res
folder and select New → Android resource directory. In the dialog that appears, select menu under
Resource type and click OK. A new folder should be in the res directory called menu. Right-click on that
and go to New → Menu resource file. We’ll call it toolbar.xml. Before we can declare this item, we need
an app icon for it! Download the source code (link is at the beginning of this post!) and navigate to
ToolbarDemo/app/src/main/res/drawable and there should be an icon called
ic_refresh_white_48dp.png. Copy this into your own drawable folder in your project (exact same path).
You should see it in Android Studio because the drawable folder will be populated now. As a side note,
Google has a ton of free material design icons that you are free to download and use! This is where I
originally got the refresh icon. You can download this library of icons here! Now that we have an icon,
let’s go back to our toolbar.xml menu and replace the contents with the following.
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_refresh" android:title="Refresh" android:icon="@drawable/ic_refresh_white_48dp" app:showAsAction="ifRoom" />
</menu>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 85
You’ll notice that we needed to declare another namespace app so that AppCompat can make our app
backwards compatible, particularly for the showAsAction attribute. This attribute tells Android in what
circumstances to put this action at different places on the Toolbar: always visible, only visible if there’s
room, or put it in the action overflow menu (three vertical dots on the far right of the Toolbar) Now
we’ve declare a menu item with an ID of action_refresh. It’s common to prefix IDs for menu actions with
action_. We’ve also given it a text title and set it’s icon to be the image we just put in our drawable
folder.
This is all it takes to define a menu! Now we can tell our Toolbar to inflate this menu. We’re also going
to wire it to an action as well: display the Toast message! It would also be nice if we could add a custom
title and color to our Toolbar as well. Let’s go back to the MainActivity class and set some properties on
our Toolbar programmatically. Inside of the onCreate(Bundle) method, let’s add the following lines of
code after we configure the ListView.
1
2
3
4
5
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextColor(Color.WHITE);
toolbar.setTitle("ListViewDemo");
toolbar.inflateMenu(R.menu.toolbar);
toolbar.setOnMenuItemClickListener(this);
In this code snippet, we grab a reference to the Toolbar and set the title and its color. We also inflate
that menu we were just editing and set the listener. Android Studio will want us to handle the listener so
Alt+Enter at the error and allow MainActivity to implement the handler interface. Inside, we need to
make sure that we only display the Toast if we clicked on the item whose ID matches the ID of the
refresh action. To make this future-proof (in case we decide to add more actions later), we can use a
switch-case statement. Add the following to the onMenuItemClick(MenuItem) method.
1
2
3
4
5
6
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
Toast.makeText(this, "Refreshing...", Toast.LENGTH_SHORT).show();
return true;
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 86
7
8
9
10
default:
return false;
}
}
Now we can run our app and see the results below!
Conclusion In this post, we learned how to use Toolbars and Menus. We covered some of the design considerations
for adding actions to the Toolbar. Actions must be either Frequent, Important, or Typical. We created
our own Toolbar and populated it with an action. We even set up an event listener for that action as
well. As a reminder, Google has a ton of free material design icons for all platforms that you can use in
your apps!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 87
How to Use Contextual Toolbar and ActionBar in Android Hello World! In this post, we’re going to be covering contextual toolbars/action bars. They are a
structure that will allow us to change the menu items on the toolbar based on the user’s context.
Suppose we were looking at our email in the Inbox or Gmail app and wanted to delete an email. We
could tap on the email in the list and then click the delete icon in the Toolbar, or we could long-press on
the email in the list and the menu items in our Toolbar change! The context is the list of selected emails.
What can we do with a list of emails? We can delete them, mark them to be unread, archive them, etc.
This the contextual toolbar at work! We’re going to be adding on from our previous app on Toolbars and
Menus.
Note: Before reading too much into this post, you should look at the other post on Toolbars and Menus.
We’re going to be building off of the same app! The source code for this post can be downloaded here.
You’ll need an icon in it so make sure you download it!
Side Note: To save text, I’m going to be abbreviating the contextual Toolbar as CAB, which actually
stands for Contextual Action Bar. However, we’ll be using Toolbars instead of the system action bar
because it gives us the flexibility we need to implement Material Design.
Let’s open up the previous app and make sure it works. We should see a list with all of the countries and
a refresh icon on the Toolbar. We’ll need to add another icon to our drawable folder so follow the same
process as before: copy over everything in CABDemo/app/src/main/res/drawable/ folder and copy it to
your project’s drawable folder. We’ll get a new icon that we can use in the toolbar’s context menu.
Speaking of context menu, let’s create another menu XML file to serve as the menu that will be inflated
when the CAB activates. Let’s right-click on the menu folder and create a new menu resource XML file
called toolbar_cab.xml. Put the following in the file:
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_delete" android:title="Delete" android:icon="@drawable/ic_delete_white_48dp" app:showAsAction="ifRoom" />
</menu>
This is similar to the previous menu with the exception of the icon and the ID. Now we can use this when
we start the Toolbar’s contextual action mode. However, before we can get to this, we need to set
another property in the styles.xml file to prevent the CAB from being stacked on top of the existing one
like in the following image!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 88
We want the CAB to be an overlay on top of our existing Toolbar. Let’s open up the styles.xml file and
append the following line after the windowNoTitle attribute: <item
name="windowActionModeOverlay">true</item> . This will give us the desired result of having the
action mode overlayed on top of the existing Toolbar.
Now that we’ve handled everything there is to do regarding resources, let’s get to the code. We’re going
to have to rethink our approach when it comes to our list because of one critical fact: ArrayAdapter’s
internal representation of a list is immutable! We’re not allowed to delete items from the ArrayAdapter
so we need to think of another way to do this. Luckily, we can use Java’s aliasing to our benefit. Instead
of using the utility method on ArrayAdapter, we’re going to grab an ArrayList and pass that reference
to ArrayAdapter. It’s important that we use an ArrayList because they are completely mutable. Keep in
mind that not all implementing classes of the List interface are mutable! In specific, we’ll use an
ArrayList. This way, we can make changes to the underlying list that the adapter displays. Then we can
call a method on the adapter called notifyDataSetChanged() that will refresh our main ListView. This
works because when we set the adapter, the ListView registers itself as a listener to the adapter for
some methods. Therefore, when the adapter calls that method, the ListView knows to refresh itself
because it’s underlying adapter changed. We can also use this to implement refresh functionality by
“resetting” our adapter by “resetting” our list by clearing it and adding back a permanent copy of all of
the countries.
Let’s begin implementing this fix by adding member variables for the list of current countries and a list of
countries that are queued up for deletion:
1
2
3
4
private ListView listView;
private ArrayAdapter<String> adapter;
private ArrayList<String> toDelete;
private ArrayList<String> countries;
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 89
The final two variables we’ll need to keep track of countries to be deleted in the action mode and a
running list of countries that may not or may not have all of the countries. Now that we’ve done this,
let’s make corresponding changes in our onCreate(Bundle) to initialize these ArrayLists and use a
constructor for our adapter instead of the utility method.
1
2
3
4
toDelete = new ArrayList<>();
countries = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.countries_array)));
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1, countries);
listView.setAdapter(adapter);
That constructor parameter is simply fetching the array from our app’s resources and converting it into a
List, then an ArrayList, more specifically. Now that we have this new approach, let’s change more code
in our onMenuItemClick(MenuItem) method so that the refresh functionality actually works!
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
Toast.makeText(this, "Refreshing...", Toast.LENGTH_SHORT).show();
countries.clear();
countries.addAll(Arrays.asList(getResources().getStringArray(R.array.countries_array)));
adapter.notifyDataSetChanged();
return true;
default:
return false;
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 90
What we’re doing in this code snippet is clearing out the current list of countries so it becomes an empty
list. Then we’re adding back all of the countries. Finally, we’re telling our adapter that our data set has
changed and it will tell our ListView to refresh.
Now that we’ve added that core functionality, let’s actually get to the part where we’re going to start
our action mode. We can do this in two different ways: register a listener with our ListView to listen for
long presses and call Activity’s startActionMode(…) and pass in MainActivity as a listener, or we can take
advantage of ListView and use it’s setMultiChoiceModeListener(…) to start the action mode
automatically for us. To do this, we first need to tell our ListView to allow the user to select multiple
items and enter the action mode at the same time. Then we can set this activity to be the listener
and implement those methods. In our onCreate(Bundle) , let’s type in the following code.
1
2
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(this);
The last line will make our activity implement the MultiChoiceModeListener interface so we’ll have to do
that. The first method we need to implement is the one that actually inflates the menu!
1
2
3
4
5
6
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.toolbar_cab, menu);
return true;
}
We can grab a MenuInflater object from our Activity class and very simply inflate the XML file of the
menu into the menu object. Note that we have to return true so the action mode knows we’re
doing work in this method. In onPrepareActionMode(...) , it’s OK for us to return false since we’re not
doing any work in that method. Next, we need to implement the method that will handle list items
being checked and unchecked.
1 @Override
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 91
2
3
4
5
6
7
8
public void onItemCheckedStateChanged(ActionMode actionMode, int position, long id, boolean checked) {
if (checked) {
toDelete.add(adapter.getItem(position));
} else {
toDelete.remove(adapter.getItem(position));
}
}
The implementation is simple: if this item is checked, then add it to the list to be deleted! We also need
the other condition of the list item being unchecking. However, we don’t need any more
contingencies since our design guarantees us that if a ListView item is passed in with the checked flag as
false, it must be true that it is in our list since it had to have been checked at some point. Next, we can
move on to the method that actually deletes the items. We also have a method that is called when the
action mode is finished.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_delete:
for (String item : toDelete) {
countries.remove(item);
}
actionMode.finish();
return true;
default:
return false;
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 92
15
16
17
18
@Override
public void onDestroyActionMode(ActionMode actionMode) {
toDelete.clear();
}
Our implementation looks strikingly similar to when we implemented the Toolbar’s menu items and it’s
intended to be that way. If the delete action was tapped, then we need to iterate through all of the
items in the queue of items to delete and remove them from our list of current countries. Then we
actually need to end the action mode! Regarding the method that’s called when the action mode is
finished, we need to clear our queue of items to delete every time the user exits the action mode.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 93
Now we’re finished with our implementation and we can run our app! When we long-press on a
ListView item, we should see the CAB overlay on top of our Toolbar and we should be able to delete
items now! If we’ve removed countries, we can always add them back again by pressing the refresh
button and our ListView should reset!
Conclusion In this post, we learned how to use the contextual Toolbar/action bar to change the menu items based
on the context of the user. We did this by declaring another menu item and inflating that when
the action mode begins. We also learned more about ArrayLists and dealing with immutable and
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 94
mutable lists as well. Overall, we created an app changes the menus when the user long-presses on a
country, allow them to delete it. Changing our view to suit the user’s context is always beneficial to your
user!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 95
How to get the User Location in Android Tutorial Hello world! In this post, we’re going to learn how we can use Google Play Services to access the user’s
location. Along with that, we’re also going to learn how to use Android Marshmallow’s new runtime
permissions to ask the user for location permission at runtime instead of when they download your app.
We’ll be building off of an existing application from a previous post here. In that post, we learned how
to access the Internet and pull weather data from OpenWeatherMap given a specific latitude and
longitude. We’re going to extend that app, and, instead of providing a latitude and longitude, the system
will grab the user’s last known location and we’ll use that as the location to send to OpenWeatherMap!
Download the source for this project here.
Let’s get started! From the previous mentioned post, run NetworkDemo to double-check that it still
works. We’re going to be adding location functionality in this app to grab the user’s location and send it
to OpenWeatherMap instead. However, before we do that, we can’t just grab the user’s location
without their permission!
The permission model took a new turn in Android 6.0 Marshmallow to an approach similar to iOS: ask
for permission only when needed! In the old Android permission model, developers would post all of
the permissions that their app used in the AndroidManifest. When they published their app, users could
see all of the permissions that a particular app required on the Play Store. The downside was that before
users could install the app, they had to agree to grant the app all of the permissions it requested.
Concern arose when simple “Flashlight” apps requested Internet, camera, or audio permissions. Users
didn’t like this as they had to debate whether or not to install a particular application based on that one
suspicious condition. Browsing through the Play Store, users had to decide whether it was worth the risk
of downloading a simple app that, for some reason, needed access to your camera.
The old permission model also promoted bad practices for developers as well. If your app tried to access
some data without the appropriate permission, Android would just crash your app. This can get
annoying if you keep forgetting to add permissions. To remedy this headache, instead of
carefully thinking what about permissions their app really needs, developers would just add all of the
permissions or much more than necessary so that they could develop more seamlessly. When they
would go to publish, their app would have many unnecessary permissions that users wouldn’t be
comfortable with so developers lose user installs and wonder why!
Starting in Android 6.0 Marshmallow, we now have runtime permissions that developers must declare
in both the manifest (partly for backwards compatibility) and request them at runtime. This forces
developers to only include the permissions they need and gives the users more freedom and security in
limiting what your app can do. For example, take the Google Keep app. We can add audio or images to a
note, but those permissions aren’t enabled at first. The first time a user clicks on the audio button, the
app asks the user whether or not they have permission to access the device’s microphone. Should the
user say yes, then the appropriate action occurs. However, if the user declines, we can just disable the
functionality that requires that permission. For example, if I didn’t want Keep to access my microphone,
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 96
I wouldn’t be able to take audio notes until I tap the audio icon again and accept the permission. Also,
we can decide when to ask again, as long as we don’t pester the user about accepting a permission.
We’ll generally want to ask the user for permission when we need it with one exception: if the
permission is central to our app, then we can request it right at the start. If the user declines, we should
show some rationale as to why this permission is central to our app. Hopefully, this won’t be the case
since our app’s purpose will be clear enough that users will understand why we need a particular
permission.
In our case, location is obviously central to our application so we can just request our permission in
onCreate(...) . Add the following code to the method.
1
2
3
4
5
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.ACCESS_COARSE_LOCATION },
PERMISSION_ACCESS_COARSE_LOCATION);
}
What we’re doing is first checking to see if the coarse permission location isn’t already granted. If it isn’t
then we need to request it. We also need to give it a constant as an identifier in the callback method.
We need a callback method as a check the result of the request. This is called
onRequestPermissionResult(...) and we need to override it as the following.
1
2
3
4
5
6
7
8
9
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case PERMISSION_ACCESS_COARSE_LOCATION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// All good!
} else {
Toast.makeText(this, "Need your location!", Toast.LENGTH_SHORT).show();
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 97
10
11
12
13
break;
}
}
We perform a switch-case on the request code and check if the resulting array is greater than zero. If so,
we check if the first (and only) permission was granted. If so, we’re good to go! If not, then we tell the
user. As I mentioned before, there are more robust ways of doing this, but this method will suffice for
now.
Now that we have permission to grab the user’s location, let’s actually do that and send it to
OpenWeatherMap. We’ll have to refactor our existing code, but we first need to learn how to get
location. The location APIs are now integrated with Google Play Services and made backwards-
compatible all the way back to Android 2.2 Froyo devices. We also have a new FusedLocationProvider
that will automatically select the best method of ascertaining the user’s location: either WiFi or GPS. It’s
also very easy to use, but we’ll need to make sure we have the Google Play Services support library and
add it to our Gradle build file. Open up the SDK manager and go to the SDK Tools tab. Download the
latest version of Google Play services. Afterwards, expand the Gradle Scripts item in Android
Studio’s Project Explorer on the left. Double-click the build.gradle file for the app module, NOT FOR THE
PROJECT! In the dependencies code block, add the following line: compile
'com.google.android.gms:play-services:8.3.0' . Replace the version number with the most recent one.
This will give us access to Google Play services and the FusedLocationProvider API.
Now let’s go back to our MainActivity.java file. Let’s declare a field for the Google Play Services API client
and instantiate it on onCreate(...) like the following.
1
2
3
4
5
6
7
8
...
private GoogleApiClient googleApiClient;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 98
9
10
11
googleApiClient = new GoogleApiClient.Builder(this, this, this).addApi(LocationServices.API).build();
}
We create a new client through the means of a builder inner class. The first parameter is the Context
and the next two parameters are callbacks for when the connection succeeded, was suspended, or
failed. Then we add the location services API and finally call build() . However, now that we have the
client, we need to connect it when we need to use location and disconnect it when we’re not using
location to conserve system resources. Ideal places to do this are the onStart() and
onStop() methods. The former is called before the Activity is visible on screen and the latter is called
after the Activity leaves the screen. These won’t be called when a dialog or other view partially blocks
the current activity, else we’d be connecting and disconnecting very frequently and wasting even more
resources! Override those lifecycle methods and add the following code inside them to connect and
disconnect the API client.
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void onStart() {
super.onStart();
if (googleApiClient != null) {
googleApiClient.connect();
}
}
@Override
protected void onStop() {
googleApiClient.disconnect();
super.onStop();
}
Now that we have the connection set up, we can look at the most important method for Google Play
services: onConnected(...) . This method is called when we’ve successfully connected to Google Play
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 99
services. Beyond this point, we can use any of the APIs we declared when we were building the
object. We should first check if the user gave the permission to use location services in the same fashion
as the onRequestPermissionsResult(...) method. Then we can get the user’s last known location and run
the code to fetch the weather. We can implement the method like the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onConnected(Bundle bundle) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
double lat = lastLocation.getLatitude(), lon = lastLocation.getLongitude();
String units = "imperial";
String url = String.format("http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&units=%s&appid=%s",
lat, lon, units, APP_ID);
new GetWeatherTask(textView).execute(url);
}
}
Notice that we can retrieve the user’s last location very simply using the FusedLocationAPI. We then
extract the latitude and longitude from the Location object and execute the task. Note: we know need a
field that store the main textView of the screen so that we can grab a reference to the textView in
onCreate(...) for efficiency reasons. We can do this using standard procedure: textView = (TextView)
findViewById(R.id.textView); .
There’s just one last thing we have to do before we can run this application. We need to make sure our
emulator supports Google Play services. We can do this by opening up the SDK manager once more and
downloading a system image that says “Google APIs” in the title. Create a new AVD from that system
image and we should be good to go! After we’ve done all of this, we should have a complete
application and it will ask for your location and then display the current weather at that latitude and
longitude! Here are some screenshots below.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 100
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 101
Conclusion In this post, we learned about Android Marshmallow’s new runtime permissions model that asks the
user for permission only when needed, not all up front. We also covered how to connect to Google Play
Services. Many of Android’s newer APIs, like location services, will require the Google Play Services
configuration. Finally, we used the FusedLocationProvider API to gain access the the user’s last known
location and sent that through to OpenWeatherMap to give the user the current weather at their
current location.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 102
Beginners Guide to Material Design In this post, we’re going to learn all about Google’s design philosophy dubbed Material Design. We’re
going to cover some of the fundamental concepts behind Material Design and will later learn how to
realized our design. We’ll be discussing just some of the basics behind the essence of Material Design,
like elevation, colors, and layouts. Note that we’ll only be covering a very small amount of what Material
Design actually is so if you want to learn more, check the link at the bottom of the post for more
information on Google’s Material Design.
In most development settings, we have both developers and designers. The designer’s purpose is to
think about how the user can get the best experience on a product or service. They do market research
on other products, mockups, and even focus groups. One of the most important thing designers
are responsible for are the wireframe mockups and workflow diagrams. They define our application’s
entire purpose and give the developers a bird’s eye view on all of the screens and elements they’ll have
to deal with. When thinking about an app idea, they come up with flows for different scenarios or use-
cases. For example, think about a note-taking app and let’s explore one such use-case. Suppose we have
our character, let’s call him Jim, talking with his boss at work. His boss mentions that Jim needs to file
reports to him by 3:00PM on Friday and that they must be filed in alphabetical order as well. Jim opens
up our note-taking app on his phone and puts all of that information in there, including the due date and
the notes. This may seem like a silly example, but it’s a perfectly valid use-case. From this, we know that
our note-taking app should have a way to quickly enter in notes so the user doesn’t spend too much
time trying to figure out how to work with the app. In other words, we also want to make our app as
intuitive as possible. Furthermore, we want our user to feel comfortable in our app, regardless of the
platform he or she is using it on, i.e. phone, tablet, desktop.
Although there are variations in intuitiveness, Google’s Material Design aims to provide a combined user
experience across all sorts of devices. If you see a particular icon on a phone app, you can be pretty
certain that it’ll mean the same thing on the desktop app and the web app. By unifying the experience
across all devices, this allows users to be more familiar with an app and spend less time trying to figure it
out. Material Design provides a clean interface that users can intuitively interact with and expect that
same level of interaction on all of their different devices. As developers, we also need to have an
appreciation for this design as well as the knowledge on how to implement or realize it. Let’s get right to
some of the key topics in Material Design!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 103
(Source: Google’s Material Design website)
One of the most important facets of Material Design is the fact that we have three axes we can work
with instead of two! We have our fundamental plane that is the phone’s screen, but Material Design
adds the z-axis to be increasing as the element moves “above” the screen. When we have material that
is “above”, we can denote this by having a shadow that gives the impression that a particular element is
at a different elevation than those around it. Take the floating action button (or FAB for short) for
example. Note that it has a noticeable drop shadow around it and it gives the impression that it is
“above” the rest of the screen.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 104
We define different elevations for different UI elements based on importance. For example, the FAB’s
resting elevation is 6dp. In addition to a resting elevation, we have a raised elevation that is activated
when a UI element is pressed. This signifies to the user that there’s interaction happening. The raised
elevation of the FAB is 12dp. It gives the impression that the FAB is “rising” to meet the user. As
mentioned before however, the system can do most of this for us, particularly rendering the drop
shadows. These numbers come in handy because in our views, we have that elevation attribute that we
will assign appropriate elevations too. Not everything in material design has an elevation!
The next aspect of Material Design is the bright and bold color scheme. Google has provided us with a
giant list of different colors and shades that we can use right here. There are several colors that we need
to pick, particularly a primary and accent color. The primary color is intended to be the 500 value of a
particular color. The status bar has to be a darker shade of the particular color, the 700 value of our
primary color. In addition, we can define an accent color. Notice that the colors have a bottom section
that starts with A###. For the accent color, we’ll use the A200 value of a complimentary color with a
good contrast from our primary color. The accent color will be used for things like EditText
underlines and other subtile places to add the bold color signature that is Material Design. This is why it
needs to have a good contrast, so that we can overlay it on top of the primary color and users will be
able to see the difference instead of the text being difficult to read.
It’s helpful to place these hexadecimal values in a file called colors.xml. The default colors.xml file when
we create a project looks like the following.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 105
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
Notice that we have a primary color (the 500 value), a dark primary color (the 700 value), and an accent
color (the A200 value of a different, complimentary color). The hex values can either be in uppercase or
lowercase. If we wanted to change our colors, we can just copy-and-paste values from the above
webpage. Overall, colors help to better define our brand and promote our product. Take Evernote for
example. When we see the icon and the green background, our mind immediately associates this with
being Evernote. As another example, take Apple. We immediately recognize the brand by the white icon
that looks like an apple with a bite taken out and a leaf. Hopefully these examples illustrate the power of
branding our product/service.
We’re going to shift our design perspective a bit towards development and talk about keylines. We can
think of all Android layouts to conform to a baseline grid of density-independent pixels which scale
based on the actual density of the device our app runs on (hence the density-independent part). Keylines
are critical margins that we have to consider when we’re building a layout. For example, take the below
app.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 106
Notice that our app has margins to the left and the right, as well as between the circular ImageView and
the TextView to its right. The magic number of the first keyline is 16dp. This means that all UI elements,
except the status bar and toolbar of course, start at 16dps from the left and right side of the screen. This
prevents text from being placed at the edge of the screen, which makes it difficult to read. We followed
this keyline in the above app. The magic number of the second keyline is 72dp. This is where, in a list or
navigation drawer, we can start the text if there’s an icon or checkbox to the left of it. This gives us
enough space between the icon/checkbox that the user can accurately touch the icon/checkbox and we
can provide a touch event. For example, in the Messager app, when we touch on an icon, we bring up
that contact’s info; however, if we tap on the list item, we bring up the text messages between us and
that contact. This also prevents us from cramming the text right up against the icon/checkbox.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 107
There are also other considerations when regarding lists and spacing, in general. For example, the main
toolbar usually has a height of 56dp (as opposed to 48dp back in Android 4.0). In some of the other
posts, you’ll notice that we set the minimum height of the toolbar to be the system’s height of the
toolbar. This is the reason why: if the height changes in the next Android release, we can be future-
proof. Also, the toolbar can change depending on the screen size or orientation. We also do the same
for ListView rows/items. Depending on their content, we can query the system for the best height using
the listPreferredItemHeight or listPreferredItemHeightLarge attributes. In our case, ListView items have
a height of 72dps. This makes them easy to tap on and the user doesn’t have to pinpoint his or her
finger to touch the right ListView item.
We’ve only covered just a little bit of Material Design. In fact, Google has a great website that covers
many more aspects about Material Design that you can look at here. If you need some inspiration, just
look at some of the apps on the Google Play Store already! Most of the major applications, by the time
of this writing, have already redesigned their apps to support Material Design. Take a look at them for
some great examples of Material Design in action.
We only scratched the surface of Material Design in this post. We hit some key points about Google’s
new Material Design approach. We talked about the purpose of Material Design and why it
was instated. We also discussed topics that we need to be mindful of, like elevation, color, and layout.
We finally learned about some great resources that we can refer to for guidance.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 108
How to Use Loaders in Android In this post, we’re going to learn about loaders as well as content providers. Loaders allow us
to load vast amounts of data asynchronously so we don’t bog down the main UI thread. We can get this
data from querying databases using content providers instead of having it stored in a resources file like
we’ve done in other posts. We’ll be building a small application that will just list the names of all of the
contacts on our phone.
If you’re not familiar with SQLite, I highly suggest that you look over this post on SQLite since content
providers deal with accessing data from the system’s SQLite databases.
The source code for this post can be downloaded here.
We’ve looked before at how to load data into a ListView using adapters, but what if we have a very large
database or data set? Most of the data sets that we’ve been using have been pretty small so it doesn’t
hurt that much to load them immediately in onCreate(...) . However, when we have databases or data
sets of thousands or hundreds of thousands of entries, instead of stalling the app, it might be better to
load them on a background thread. We’ve already looked at how we can create and manage
background threads, but since loading data from a database in a background thread is such a common
task, Android has a wonderful CursorLoader class that prevents us from having to write the boilerplate
code ourselves.
But where do we get the data for the loaders to load? Let me talk about cursors and content providers.
Let’s look at an example of one: the Contacts app. On Android phones, there’s a way to manage all of
our contacts. But where is that data stored? It’s stored in a database! In fact, the system can even let us
access into that database through a content provider. Content providers allow applications to share data
between each other. This is how Twitter or LinkedIn know about your contacts: they have access to
them if you give the permission for them to read your contacts.
The Android system has hundreds of content providers for many different pieces of information like
contacts, media, and photos. To differentiate all of these different content providers, each group of
related information has a content URI that we provide to tell the content provider that we’re
querying one database and not the other. Think of URIs as being URLs on the internet. Inputting
androidkennel.org will get you to this site, but inputting google.com will get you to another site. These
URIs are also unique so no two content providers should have the same URI. We simply provide the URI
and the query we want to run, although most of the querying part is actually abstracted away for good
reasons.
When we query a content provider, we get back a Cursor object. This allows us to step through the rows
of the result query using a similar approach as if we had an iterator: while the Cursor has a next row,
grab the current one and then move on to the next row. We can access the data stored in the
current row of the Cursor through the index of the column, but we won’t have to do this since Android
provides us with a SimpleCursorAdapter that will display cursor results for us, allowing us to skip the
step of making our own custom adapter.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 109
To summarize our approach to display contacts in a ListView, we’ll first need to ask for permission to
read contacts. Then we’ll initialize the loader with the appropriate query (that is, retrieve all of the
contacts’ names). Then, when the query is finished, we switch our adapter’s Cursor with the one we get
back from the loader.
Let’s get started and create the LoaderDemo project! We’ll need Android 6.0 Marshmallow with an
empty activity. First things first, we need a ListView! Replace the layout/activity_main.xml with the
following.
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.deshpande.loaderdemo.MainActivity" />
Now that we have a ListView, we need to deal with the runtime permissions for reading contacts. Add
the following line in the AndroidManifest.xml file before the application tag.
1 <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS" />
Now that we’ve done this, we can request the READ_CONTACTS permission right when we start the app.
Open up MainActivity and add the following to the onCreate(...) method. We’ll also need to create a
constant at the top of our class for PERMISSION_CONTACTS.
1
2
3
4
5
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.READ_CONTACTS }, PERMISSION_CONTACTS);
} else {
getLoaderManager().initLoader(LOADER_CONTACTS, null, this);
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 110
Now after we’ve been granted permission, we can initialize the loader. We’ll need another constant for
LOADER_CONTACTS since we could have initialized multiple loaders if we wanted to.
1
2
3
4
5
6
7
8
9
10
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case PERMISSION_CONTACTS:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getLoaderManager().initLoader(LOADER_CONTACTS, null, this);
}
break;
}
}
The first parameter is the ID of the loader, the next is an optional Bundle we could pass, and the final
argument is the callback listener. This will make us implement the callbacks, but change the declaration
so that instead of the parameter type being Object, it’s Cursor. We’ll have to change this in the methods
as well.
1 public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> { ... }
Before we do that, we’ll need to create a SimpleCursorAdapter and set our ListView’s adapter to be that
SimpleCursorAdapter. We’ll need to create a private field in our class for the adapter and then initialize
it in onCreate(...) since we’ll need the adapter later. The first parameter is the context, the second is the
layout, and the third is the Cursor. We’re passing in a simple ListView item layout that’s really just a
TextView whose ID is text1.
We’re initially passing null since we’ll give it a Cursor after the loader is finished. The next two fields are
the mapping between database column and the contents of a view ID. In our case, we want to map the
name to the text1 TextView. To reiterate, text1 is the ID of the single TextView in the simple list item
layout. Android is smart enough to know that the ID we’re passing is that of a TextView so we set it’s
text to be whatever is in the row’s DISPLAY_NAME column. The final parameter is just some arbitrary
flags that we won’t set since we don’t have any flags to set.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 111
1
2
3
4
5
6
String[] from = { ContactsContract.Contacts.DISPLAY_NAME };
int[] to = { android.R.id.text1 };
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, from, to, 0);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(adapter);
Now that we’ve configured our adapter, we can actually look at how we can implement the loader. First
we need to create the loader. We’ll be creating a new CursorLoader that will handling loading up the
data we need. The first parameter is the context, the next is the URI that I mentioned before. The third
parameter is called the projection and it’s the columns that we want to return. We declared this as a
constant at the top of the class. We need to make sure to always have the unique _ID value in the
projection. Then we need the DISPLAY_NAME. The next two parameters are the selection clause and the
selection arguments. We’re passing in null so we get all of the contacts (null translates to * in SQLite)
and we don’t have any selection parameters, of course. The final parameter is the sorting order and
we don’t care, but we could have our ListView sorted if we needed to.
1
2
3
4
5
6
7
8
9
10
11
12
private static final String[] PROJECTION = { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME };
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
if (id == LOADER_CONTACTS) {
return new CursorLoader(this, ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null, null);
} else {
return null;
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 112
Now the loader will go and execute the query on the background thread and call
onLoadFinished(...) when it’s done with the query. Note that we get the original loader and an object of
the parameterized type, a Cursor in our case. This part of the code is simple, we just swap the null
Cursor with the one that the loader returned.
1
2
3
4
5
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
adapter.swapCursor(c);
adapter.notifyDataSetChanged();
}
You might have noticed that we have a method called swapCursor(...) and one called changeCursor(...) .
These two are very different by one key fact: changeCursor(...) will close the old Cursor. After we’re
done getting data from a Cursor, it’s important that we call close() on it to free up resources. We
haven’t had to do it since SimpleCursorAdapter and the CursorLoader manage it for us. This is also the
reason why it’s very important that we use swapCursor(...) instead of changeCursor(...) . CursorLoader is
managing the Cursor so it will close it after the call to onLoadFinished(...) . If we close it and it’s already
closed, this will crash our app! This is why we must use swapCursor(...) instead of changeCursor(...) .
onLoaderReset(...) is just a mandatory method we have to implement so we’ll swap the cursor of our
adapter to be null since this means that our created loader is being reset, so we don’t want to be
accessing data at this time.
1
2
3
4
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
That’s all we need to do with the loaders! Now we can run the app in our emulator or on our phone and
get a list of all of our contacts! In the emulator, you can sign into your Google account to grab those
contacts. Here’s an example of the app running on my phone (obviously my contacts are censored).
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 113
Conclusion In this post, we learned how to use content providers to access our phone’s contacts and load them into
a ListView asynchronously using loaders. The great benefit of using loaders is that they’re asynchronous
so we don’t hang the UI thread while it waits for us to grab potentially thousands of entries. We also
learned more about Cursors and how they’re related to content providers as well. Use loaders whenever
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 114
you predict that you’re going to be fetching a potentially vast amount of data (maybe even showing a
spinning progress dialog while the user waits).
How to Accessing the Camera and Take Pictures on Android In this post, we’re going to learn how we can access the camera and use it to take a picture. Grabbing
data from the camera can be a bit tricky, especially when dealing with the Android filesystem so we’ll be
covering how to create an image file for the photo and extracting that image back into the
ImageView. In this post, we’re going to create an app that will take a photo and display that photo in
an ImageView.
Download the full source code here.
Let’s get started right away and create a new Android project called CameraDemo with Android 6.0.
We’ll just need to create a blank activity and leave the defaults as is. The first thing we’ll need to do is
to define the appropriate permissions in our AndroidManifest.xml. So open that up and add the
following lines before the application tag.
1
2
<uses-permission-sdk-23 android:name="android.permission.CAMERA" />
<uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
We’ll obviously need the camera permission, but we’ll also need the permission to write this image to
the device’s public storage, so even if our application is uninstalled, the photos that users took with it
will still remain on the device. Next we’ll need to define our layout so open activity_main.xml and
substitute the following text (replacing tools:context with your respective MainActivity).
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 115
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
tools:context="com.deshpande.camerademo.MainActivity">
<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_above="@+id/button_image"/>
<Button
android:id="@+id/button_image"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:onClick="takePicture"
android:text="Take a picture!"/>
</RelativeLayout>
We’re defining the ImageView that will store our resulting image. We also want to center that and make
sure it’s above the Button. Speaking of the Button, we’re giving it a height of 48dp so that it conforms
with Material Design (for more information on Material Design, read this post). We want to anchor this
view to the bottom of the screen and center it as well. Notice that we give it a method name for the
onClick event that we’ll later define. Finally, we give it some text.
Now that we’ve completed our view, we’ll need to handle the permissions. But what if the user doesn’t
want to give us permission to access the camera or external storage? Instead of closing the app, we can
actually disable the Button that allows the user to take a picture. Then after the user has given us
permission to access those systems, we can re-enable the Button. In order to do this however, we need
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 116
to declare the Button as a field and set it in onCreate(...) before we check out permissions. If we don’t
have the appropriate permissions, we disable the button until we do. We also need the ImageView to
populate so we can do that as well in a similar fashion to the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Button takePictureButton;
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
takePictureButton = (Button) findViewById(R.id.button_image);
imageView = (ImageView) findViewById(R.id.imageview);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
takePictureButton.setEnabled(false);
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
}
}
We can save some checking by only determining whether or not we have the camera permission since
the only reason we need the external storage permission is to save images from the camera. So we can
add both permissions to the String array. To save some space, we’re also simply using 0 as the request
code since we’re only calling requestPermissions(...) once in our app. If the user accepts our terms, then
we simply re-enable the button like the following.
1
2
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 117
3
4
5
6
7
8
9
if (requestCode == 0) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
takePictureButton.setEnabled(true);
}
}
}
Next, we’re going to implement the onClick attribute of the button. We’ll be using the system’s Intent
for taking pictures so we don’t have to implement our own functionality of taking pictures. Since we also
need a result, we call startActivityForResult(...) and not just startActivity(...) . We also need a file that
we’re saving the image to. This will have to be defined as a member variable at the top of our class for
reasons we’ll see in just a second. For now, we’re assuming that there’s a method called
getOutputMediaFile() that does this for us. We’ll implement it later in this post. We also need to make
this file Uri a field in our class instead of a local variable in the method since Intents fully support having
Uris as extras and for another reason we’ll see in the implementation of that method. Now we can just
call startActivityForResult(...) . As with requesting permissions, we’re just going to use some integer
constant like 100.
1
2
3
4
5
6
7
public void takePicture(View view) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
file = Uri.fromFile(getOutputMediaFile());
intent.putExtra(MediaStore.EXTRA_OUTPUT, file);
startActivityForResult(intent, 100);
}
Now that we’ve sent this Intent to the system, we have to handle the result in onActivityResult(...) like
the following.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 118
1
2
3
4
5
6
7
8
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 100) {
if (resultCode == RESULT_OK) {
imageView.setImageURI(file);
}
}
}
But wait, there’s no result? This is why we needed to declare that file Uri to be a member. For camera
events, data is written to the file and not returned in the data Intent. So the workflow here is to pass in
a reference to a file that the camera will populate with image data. In onActivityResult(...) , we’re really
just setting the ImageView’s image data to be that URI since it’s been set by the system’s camera activity
if the result code was RESULT_OK.
The only thing left to do is to implement the getOutputMediaFile() method to create a
file reference that the image data will be saved to. We first need to get access to the public directory
where images may be saved on the device. As mentioned before we don’t want to use just
our application’s inner directory because our user will loose all of the photos that were taken with our
app when it is uninstalled. To do this, we need to get a file path to that external pictures directory and
then add a subdirectory to the path for our app’s pictures! Now we need to create that subdirectory. So
we first check if the path exists, and, if it doesn’t, then we call mkdirs() to create the file path. If that
failed, we simply return null since we weren’t able to create a path for the file.
1
2
3
4
5
6
7
8
9
private static File getOutputMediaFile(){
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "CameraDemo");
if (!mediaStorageDir.exists()){
if (!mediaStorageDir.mkdirs()){
return null;
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 119
10
11
12
13
14
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
return new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
}
Now that we have the directory, we need to create the file. We’re going to have the file names
contain timestamps so that each is unique and no two files would have a chance at being overwritten.
Calling new Date() will return the timestamp exactly when that call is executed. Then we create the
filepath to be the subdirectory in the public pictures directory, plus a file separator, and plus an IMG_
prefix, then the name of the file and the file extension. (Please note that this code is adapted
from Android’s API. Original source here) Now we’re done!
Before we can run our app, we need to make sure we have a camera-enabled emulator by opening up
our AVD manager and clicking the pencil icon next to our emulator to manage it. In the dialog that
comes up, click on the button that says “Show Advanced Settings” and find the Camera section. In
here, we have the option to emulate the camera or actually use our computer’s webcam. Currently, the
default is that our device won’t have a camera, but we need it for our app. Click the dropdown for both
Front and Back cameras and change them to webcam if you have one. If not, change them to emulated.
We need them to be not “None” since our app requires a camera and the system will tell us that a
camera doesn’t exist.
Now that we’re finished with our app, we can run it! Our app will have a mostly-blank screen with a
button (initially disabled) at the bottom. After we accept all of the permissions, the button will be
enabled and we can click on it. This will bring us to the system’s Activity for handling image capture and
we can take a picture now. After we do that and accept the photo, Android will bring us back to the
Activity that called the system’s camera Activity and we will be able to see the image in the main view of
our app!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 120
Conclusion In this post, we learned how we can access the Camera using Intents so we don’t have to create our own
Activity to take pictures. We learned how we can get a result back from that camera Activity and how
we can use the Android filesystem’s public directories to save data. We learned how to create an image
file that the system will save the data to before control jumps back to our app. Ultimately, we created an
app that can take a picture using the system’s Activity for picture-taking and read the result back.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 121
How to Use Android Sensors in Games In this post, we’re going to learn how to access just one of the multitude of sensors present on an
Android device by building a very small game that uses the accelerometer on the device. This game will
simply show a red ball on the screen whose motion is tied to the device’s accelerometer. For example,
tilting the device will cause the ball to accelerate in the direction of the tilt.
Download the full source code here.
Let’s get started! Create an Android Studio project called SensorsDemo and we’ll leave the defaults as is
(but create an Empty Activity). We can go ahead and delete the activity_main.xml file in the layout
folder since we’ll be using a custom view that will fill our entire screen and setContentView(...) has an
overload where we can pass in a View object. Also, copy-and-paste the ball from the provided source
code’s res/drawable directory to our res/drawable directory. If there isn’t a directory, create one. We’re
going to create a custom view that will handle updating our ball.
However, we still have much to do before we can get to the custom View. As a placeholder, let’s just
create our BallView private inner class inside MainActivity towards the end of the file like the following.
1
2
3
4
5
private class BallView extends View {
public BallView(Context context) {
super(context);
}
}
We’ll populate it with the logic to move the ball, but we first need to declare some top-level members
representing our ball’s position, acceleration, and velocity in the x and y directions. We’ll also
need members to represent the maximum x and y so we define boundaries restricting the ball’s motion
so it doesn’t roll off the screen! Of course, we’ll need a member to be the ball itself as a Bitmap object.
Finally, we need a SensorManager object so that we can register and unregister the listener for the
accelerometer sensor.
1
2
3
4
private float xPos, xAccel, xVel = 0.0f;
private float yPos, yAccel, yVel = 0.0f;
private float xMax, yMax;
private Bitmap ball;
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 122
5 private SensorManager sensorManager;
Now that we’ve done this, let’s complete our onCreate(...) method. Since we’re using the
accelerometer, it’s a good idea to keep our app in portrait mode so that the system doesn’t think we
want to change orientations if we tilt a bit too much in one direction. We can do this to a call in
setRequestedOrientation(...) method. Then we can create our custom BallView and set the view
of our Activity to be the new BallView object. Then we need to grab the our device’s size so that the ball
doesn’t roll off of the screen. Finally, we need to instantiate our SensorManager object by grabbing a
reference to the system’s sensor manager.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
BallView ballView = new BallView(this);
setContentView(ballView);
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
xMax = (float) size.x - 100;
yMax = (float) size.y - 100;
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
In onStart() and onStop() , we need to register and unregister our SensorManager, respectively. This
is a great place to do this because these Activity lifecycle methods are called before the screen is visible
to the user and after the screen has disappeared off the screen, respectively. We need to specify
that we need the accelerometer sensor and we’ll need to specify a refresh rate, SENSOR_DELAY_GAME,
in our case. Note that we perform our method calls after the call to the superclass in onStart() , but, in
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 123
onStop() , we perform method calls before the call to the superclass. This is because we’d like to
unregister our listener before the system does it’s tasks to free up resources, for example.
1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onStart() {
super.onStart();
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
}
@Override
protected void onStop() {
sensorManager.unregisterListener(this);
super.onStop();
}
This will force us to implement the SensorEventListener2 interface, which will make us implement
onSensorChanged(...) , onFlushCompleted(...) , and onAccuracyChanged(...) . We won’t have to do
anything in the latter two methods, but the former is the most important since this is where we
actually retrieve data from the accelerometer. To gain a better understanding of why we have
positive/negative values in our code, look at the following diagram of the orientation of the sensors with
respect to the device.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 124
(Source)
Also, according to the documentation, sensorEvent.values[i] stores data on our accelerometer, where i
is a value from 0 to 2 representing the acceleration in the x, y, and z directions. From the diagram, note
that we have to negate our y value, else the ball will just move in one dimension! After we get their
values, we need to update our ball’s location. This is a common practice in game development to update
the location of all of the entities of the game per frame. In our case, our only entity is the ball, and, since
that’s tied to the accelerometer, our “frame” depends on the refresh rate of the sensors, which is still
very fast.
1
2
3
4
5
6
7
8
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
xAccel = sensorEvent.values[0];
yAccel = -sensorEvent.values[1];
updateBall();
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 125
Now in our updateBall() method, we need to change our velocities and calculate how far our ball has
moved in that time. Then we need to combine our ball’s current location with the displacement and we
now have the ball’s new position! Notice that because of the coordinate axes on the device, we have to
actually subtract the delta position from the current position! Feel free to play around with these
calculation to make your ball game more or less difficult! We also need to handle the cases where
it goes off the screen. We can set the position to be the min or the max depending on how it goes off
the screen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void updateBall() {
float frameTime = 0.666f;
xVel += (xAccel * frameTime);
yVel += (yAccel * frameTime);
float xS = (xVel / 2) * frameTime;
float yS = (yVel / 2) * frameTime;
xPos -= xS;
yPos -= yS;
if (xPos > xMax) {
xPos = xMax;
} else if (xPos < 0) {
xPos = 0;
}
if (yPos > yMax) {
yPos = yMax;
} else if (yPos < 0) {
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 126
21
22
23
yPos = 0;
}
}
Now we need to code our custom view to actually display the ball on the screen. Since our entire
Activity is comprised of the BallView, we can just position the ball on the view and we’re sure that the
ball will be on the screen. Note that we are forced to implement the constructor that calls the
superclass’s constructor. In our constructor, we need to grab our Bitmap and resize it so it’s the
appropriate size for our screen.
When we’re creating a custom view, we have to override a method called onDraw() since that’s called
to draw the view once. However, we don’t just want to draw our ball once, we want to keep refreshing
it. We can call an instance method inside View called invalidate() . This will tell the view to call
onDraw(...) again sometime in the future.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private class BallView extends View {
public BallView(Context context) {
super(context);
Bitmap ballSrc = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
final int dstWidth = 100;
final int dstHeight = 100;
ball = Bitmap.createScaledBitmap(ballSrc, dstWidth, dstHeight, true);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(ball, xPos, yPos, null);
invalidate();
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 127
16 }
This should be all we need to get our app running and using the accelerometer! Here’s what our app
looks like running on a physical device.
As we move the device, the ball accelerates in the appropriate direction! For those of you that might not
have an Android device, we can still use the accelerometer. However, instead of having a physical
device, we need to manually connect with the emulator’s console through a client called telnet. If you’re
on a Mac or Linux computer, you already have this installed. However, if you’re on Windows, you’ll need
to enable it by following this procedure. To simulate the accelerometer, we’ll need to start up our
emulator and open up a terminal window. Note the 4-digit number at the top of the emulator’s window.
That’s called the port number and we need it to log into the emulator. In the console, type telnet
localhost #### with #### as your emulator’s port number. Now we should see something like Android
Console: type 'help' for a list of commands in the console. Now we can type sensor status to show the
status of all of the emulator sensors. We know everything is good if we can see acceleration: enabled. in
the list of sensors. Now if we wanted to change the acceleration, we can run the following command to
set the acceleration: sensor set acceleration 0:0:0 or sensor set acceleration 50:50:50 . We can use this
to change the acceleration of the emulator and play our game!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 128
Conclusion In this post, we learned how to access the accelerometer on our device/emulator to build a game using
it as well. We built a game that has a ball on the screen whose motion is tied to the accelerometer.
When we move the accelerometer, we also move the ball. Even though we only covered the
accelerometer, this same approach can be done with different types of sensors as well. Make sure to
check out the documentation on how to use the other kinds of sensors!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 129
How to Use the Android Notification System In this post we’re going to learn how to use the Android notification system. Android notifications have
evolved from the standard one-liner with an app icon to having actions, showing images, and expanding
to show more information. Notifications are a critical part of the user experience. Instead of the user
having to check your app for updates, your app will save the user the time and effort by notifying the
user about important things. We’re going to learn how to use the notification system as well as
the variety of styles that notifications come in.
You can download the source code for this post here.
Let’s get started! Create a new Android Studio project called NotificationDemo with Android 6.0. We
just need an empty Activity with the default naming conventions. In addition to this primary Activity, we
also need to create a second Activity so that the user will be directed to that when they tap on the
notification. Android Studio has a shortcut that will allow us to create an Activity and corresponding XML
layout file. Right-click on the primary package and select New->Android Activity-> Empty Activity. Then
enter ResultActivity for the name of our new Activity. Android Studio will now create the corresponding
Java and XML file.
Regarding the view of this Activity, we want to keep this simple by just having a large TextView centered
on the screen so that we know our notification directed us to the appropriate Activity. Change the XML
of ResultActivity to be the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.deshpande.notificationdemo.ResultActivity">
<TextView
android:layout_width="wrap_content"
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 130
14
15
16
17
18
19
20
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Notification Worked"
android:id="@+id/textView"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Now that we’ve done that, we need to consider the view that will actually launch the notification that
will get us to this Activity. In our case, since we just need to generate a notification, let’s have a simple
button that will launch a notification when it is clicked. The XML layout file of MainActivity should now
look like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.deshpande.notificationdemo.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me!"
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 131
16
17
18
19
20
android:id="@+id/button"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:onClick="createNotification" />
</RelativeLayout>
We’ve centered the Button on the screen and already assigned it a method to be called when the button
is clicked. All of our notification content is going to go into that single method. Let’s open up
MainActivity and add the method below onCreate(...) as public void createNotification(View view). The
reason we need that one View parameter is because that’s a part of the method signature required for
our Button’s onClick attribute.
Now we can get started building the notification. First of all, the process of creating a notification
follows the Builder design pattern. With this pattern, the only way to create a Notification object is to
use the public inner class Notification.Builder. Then we can customize our notification, call build(), and
our Notification object is created and ready to hand off to the system to post. Each setter method
of the Builder also returns a Builder with that property set. This means that we can chain together the
setters like the following:
1
2
3
4
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setContentTitle("Notification!")
.setContentText("This is my first notification!");
Make sure to import the v4 NotificationCompat class, not the v7! The only argument the Builder
requires is the Context. Note that on the left gutter of Android Studio’s main text pane, we can see a
tiny thumbnail of the drawable icon. This gives us an idea of what the icon is going to look like.
After we’ve created this preliminary Builder object, we can then consider changing the style. You
might have noticed that when you have multiple unread emails, the notification looks like a miniature
ListView. This is the NotificationCompat.InboxStyle. If you get a notification with a single email, the
notification becomes expandable and we can actually read the first few lines of the email. This is the
NotificationCompat.BigTextStyle. Finally, when you take a screenshot, there’s always a follow-up
notification that can be expanded into a picture of the screenshot. This is the
NotificationCompat.BigPictureStyle. In fact, we can create our own custom styles by extending the
NotificationCompat.Style abstract class.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 132
For our purposes, we’re going to look at the BigTextStyle. Using styles is very straightforward. We just
create a BigTextStyle object and call bigText(...) on it to set the expanded content of the notification. In
our case, we’re going to use a Lorem Ipsum generator so that we can generate some phony text to see
how the notification is going to work. Finally, we need to set our builder object’s style to be the one we
just created. All of this looks like the following in code:
1
2
3
4
5
6
7
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
bigText.bigText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Etiam hendrerit risus ut congue feugiat. Donec rutrum tristique purus, at tempus ipsum pharetra eget. " +
"Ut tristique aliquet elementum. Sed hendrerit quis sapien a mattis. " +
"Pellentesque interdum neque a felis mattis finibus. ");
builder.setStyle(bigText);
Now that we’ve done this, we need to handle what happens when the user clicks on the notification. We
can use the Intent system to launch an Activity. However, we need to wrap that Intent inside another
kind of special Intent called a PendingIntent. By wrapping our Intent around a PendingIntent and
passing that to the system, it becomes the systems responsibility to call the Intent when the notification
is called. A PendingIntent is just what the name implies: it’s an Intent that has yet to be used. After we
create this PendingIntent, we then tell our builder to use it. This is represented in code by the following:
1
2
3
Intent resultIntent = new Intent(this, ResultActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
Notice that we don’t really care about the request code since this is the only PendingIntent our app
launches. In addition, the last parameter of the getActivity(...) method is any flags we want to give to
this PendingIntent. In our case, we’re telling the system not to push out a new notification if any
information changes, but to update this current one. This prevents the user from receiving a hundred
notifications from our app!
Finally, we can build this Notification. We also want to set an additional flag so that the notification
is dismissed when the user taps on it. We can do this by building our notification and setting the flags
on the built notification as follows:
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 133
1
2
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
Now that we’ve finally built our notification, we can send it off to the system! We need to get a
backwards-compatible reference to the system’s notification manager and simply call notify(...) with
our notification object.
1
2
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(0, notification);
Notice how we simply set the ID of the notification to be some arbitrary integer. However, if we wanted
to cancel our notification programmatically for some reason, then we would need to keep track of the
ID.
And that’s all it takes to create a highly-customizable notification! We can see the various stages of the
notification in the screenshots below!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 134
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 135
In this post, we learned how to create and customize a notification that can be sent
programmatically. We learned about how the notification system uses the Builder pattern to create
Notification objects. We also covered the three styles of notifications: InboxStyle, BigTextStyle, and
BigPictureStyle. In addition to that, we learned what PendingIntents are and their uses. Finally, we saw
how to formally post the notification to the Android systems. Notifications are a key part of an
app’s interact with the user. A word of warning: only make notifications if it makes sense for your app to
do so. Nothing is more annoying than receiving random notifications from a calculator app! Be smart
about your notifications!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 136
How to Pass Data between Activities using Intents Hello World! In this post, we’re going to learn how we can pass data back and forth between two
different activities. We’ll also learn how to reuse layouts and use our app’s manifest with Intents. The
Intent object is the fundamental class that we use to pass data around in Android. It acts as a container
of information to the system that we can manipulate. We’re going to see how we can transfer to
an Activity and get a result back from that Activity as well.
Note: To make this easier on us, we’re going to use source code from a previous post on Toolbars so if
you have any questions as to the specifics of ListViews or Toolbars, please read those respective articles!
We’re going to be building an app that displays a list of countries, and, when the user taps on an entry, it
brings them to another view that simply displays the name of the country. The interesting part is that
the second Activity will receive the country to display from the first Activity.
Let’s create a new Android Studio project and call it IntentDemo. We’re going to start by adding the
following to the res/values/strings.xml file to act as our database backend.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<string-array name="countries_array">
<item>Afghanistan</item>
<item>Albania</item>
<item>Algeria</item>
<item>American Samoa</item>
<item>Andorra</item>
<item>Angola</item>
<item>Anguilla</item>
<item>Antarctica</item>
<item>Antigua and Barbuda</item>
<item>Argentina</item>
<item>Armenia</item>
<item>Aruba</item>
<item>Australia</item>
<item>Austria</item>
<item>Azerbaijan</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 137
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<item>Bahrain</item>
<item>Bangladesh</item>
<item>Barbados</item>
<item>Belarus</item>
<item>Belgium</item>
<item>Belize</item>
<item>Benin</item>
<item>Bermuda</item>
<item>Bhutan</item>
<item>Bolivia</item>
<item>Bosnia and Herzegovina</item>
<item>Botswana</item>
<item>Bouvet Island</item>
<item>Brazil</item>
<item>British Indian Ocean Territory</item>
<item>British Virgin Islands</item>
<item>Brunei</item>
<item>Bulgaria</item>
<item>Burkina Faso</item>
<item>Burundi</item>
<item>Cambodia</item>
<item>Cameroon</item>
<item>Canada</item>
<item>Cape Verde</item>
<item>Cayman Islands</item>
<item>Central African Republic</item>
<item>Chad</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 138
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<item>Chile</item>
<item>China</item>
<item>Christmas Island</item>
<item>Cocos (Keeling) Islands</item>
<item>Colombia</item>
<item>Comoros</item>
<item>Congo</item>
<item>Cook Islands</item>
<item>Costa Rica</item>
<item>Cote d\'Ivoire</item>
<item>Croatia</item>
<item>Cuba</item>
<item>Cyprus</item>
<item>Czech Republic</item>
<item>Democratic Republic of the Congo</item>
<item>Denmark</item>
<item>Djibouti</item>
<item>Dominica</item>
<item>Dominican Republic</item>
<item>East Timor</item>
<item>Ecuador</item>
<item>Egypt</item>
<item>El Salvador</item>
<item>Equatorial Guinea</item>
<item>Eritrea</item>
<item>Estonia</item>
<item>Ethiopia</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 139
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<item>Faeroe Islands</item>
<item>Falkland Islands</item>
<item>Fiji</item>
<item>Finland</item>
<item>Former Yugoslav Republic of Macedonia</item>
<item>France</item>
<item>French Guiana</item>
<item>French Polynesia</item>
<item>French Southern Territories</item>
<item>Gabon</item>
<item>Georgia</item>
<item>Germany</item>
<item>Ghana</item>
<item>Gibraltar</item>
<item>Greece</item>
<item>Greenland</item>
<item>Grenada</item>
<item>Guadeloupe</item>
<item>Guam</item>
<item>Guatemala</item>
<item>Guinea</item>
<item>Guinea-Bissau</item>
<item>Guyana</item>
<item>Haiti</item>
<item>Heard Island and McDonald Islands</item>
<item>Honduras</item>
<item>Hong Kong</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 140
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<item>Hungary</item>
<item>Iceland</item>
<item>India</item>
<item>Indonesia</item>
<item>Iran</item>
<item>Iraq</item>
<item>Ireland</item>
<item>Israel</item>
<item>Italy</item>
<item>Jamaica</item>
<item>Japan</item>
<item>Jordan</item>
<item>Kazakhstan</item>
<item>Kenya</item>
<item>Kiribati</item>
<item>Kuwait</item>
<item>Kyrgyzstan</item>
<item>Laos</item>
<item>Latvia</item>
<item>Lebanon</item>
<item>Lesotho</item>
<item>Liberia</item>
<item>Libya</item>
<item>Liechtenstein</item>
<item>Lithuania</item>
<item>Luxembourg</item>
<item>Macau</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 141
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<item>Madagascar</item>
<item>Malawi</item>
<item>Malaysia</item>
<item>Maldives</item>
<item>Mali</item>
<item>Malta</item>
<item>Marshall Islands</item>
<item>Martinique</item>
<item>Mauritania</item>
<item>Mauritius</item>
<item>Mayotte</item>
<item>Mexico</item>
<item>Micronesia</item>
<item>Moldova</item>
<item>Monaco</item>
<item>Mongolia</item>
<item>Montenegro</item>
<item>Montserrat</item>
<item>Morocco</item>
<item>Mozambique</item>
<item>Myanmar</item>
<item>Namibia</item>
<item>Nauru</item>
<item>Nepal</item>
<item>Netherlands</item>
<item>Netherlands Antilles</item>
<item>New Caledonia</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 142
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<item>New Zealand</item>
<item>Nicaragua</item>
<item>Niger</item>
<item>Nigeria</item>
<item>Niue</item>
<item>Norfolk Island</item>
<item>North Korea</item>
<item>Northern Marianas</item>
<item>Norway</item>
<item>Oman</item>
<item>Pakistan</item>
<item>Palau</item>
<item>Panama</item>
<item>Papua New Guinea</item>
<item>Paraguay</item>
<item>Peru</item>
<item>Philippines</item>
<item>Pitcairn Islands</item>
<item>Poland</item>
<item>Portugal</item>
<item>Puerto Rico</item>
<item>Qatar</item>
<item>Reunion</item>
<item>Romania</item>
<item>Russia</item>
<item>Rwanda</item>
<item>Sqo Tome and Principe</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 143
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<item>Saint Helena</item>
<item>Saint Kitts and Nevis</item>
<item>Saint Lucia</item>
<item>Saint Pierre and Miquelon</item>
<item>Saint Vincent and the Grenadines</item>
<item>Samoa</item>
<item>San Marino</item>
<item>Saudi Arabia</item>
<item>Senegal</item>
<item>Serbia</item>
<item>Seychelles</item>
<item>Sierra Leone</item>
<item>Singapore</item>
<item>Slovakia</item>
<item>Slovenia</item>
<item>Solomon Islands</item>
<item>Somalia</item>
<item>South Africa</item>
<item>South Georgia and the South Sandwich Islands</item>
<item>South Korea</item>
<item>South Sudan</item>
<item>Spain</item>
<item>Sri Lanka</item>
<item>Sudan</item>
<item>Suriname</item>
<item>Svalbard and Jan Mayen</item>
<item>Swaziland</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 144
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<item>Sweden</item>
<item>Switzerland</item>
<item>Syria</item>
<item>Taiwan</item>
<item>Tajikistan</item>
<item>Tanzania</item>
<item>Thailand</item>
<item>The Bahamas</item>
<item>The Gambia</item>
<item>Togo</item>
<item>Tokelau</item>
<item>Tonga</item>
<item>Trinidad and Tobago</item>
<item>Tunisia</item>
<item>Turkey</item>
<item>Turkmenistan</item>
<item>Turks and Caicos Islands</item>
<item>Tuvalu</item>
<item>Virgin Islands</item>
<item>Uganda</item>
<item>Ukraine</item>
<item>United Arab Emirates</item>
<item>United Kingdom</item>
<item>United States</item>
<item>United States Minor Outlying Islands</item>
<item>Uruguay</item>
<item>Uzbekistan</item>
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 145
233
234
235
236
237
238
239
240
241
242
243
<item>Vanuatu</item>
<item>Vatican City</item>
<item>Venezuela</item>
<item>Vietnam</item>
<item>Wallis and Futuna</item>
<item>Western Sahara</item>
<item>Yemen</item>
<item>Yugoslavia</item>
<item>Zambia</item>
<item>Zimbabwe</item>
</string-array>
Now, before we make changes to our view, since we’re going to need a Toolbar in both Activity’s views.
Instead of copying and pasting code from one file to another, we can declare a layout file that can be
inserted in both views. This way, we only need to edit one layout file, not every single one! Let’s right-
click on the res/layout folder and create a new layout file whose name toolbar and whose root element
is android.support.v7.widget.Toolbar . Now we add properties to this toolbar like so.
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:elevation="4dp"
android:background="@color/colorPrimary" />
Since we’ve created this layout file, we can insert it into any number of other layout files as well. For
example, let’s go into the activity_main.xml file and insert the following.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 146
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar" />
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView" />
</LinearLayout>
In the above code snippet, we can insert the contents of the toolbar layout into this layout by using the
include tag and layout attribute. Let’s first have our list configured before we move on to create another
Activity. Open up the MainActivity class and add the following code snippet to set up our list.
1
2
3
4
5
6
7
private ListView listView;
private ArrayAdapter<CharSequence> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 147
8
9
10
11
12
13
14
15
16
17
18
19
listView = (ListView) findViewById(R.id.listView);
adapter = ArrayAdapter.createFromResource(this, R.array.countries_array, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setTitleTextColor(Color.WHITE);
toolbar.setTitle(R.string.app_name);
}
You’ll notice that we can access the Toolbar even when it’s actually in another file thanks to the include
tag. The call to setSupportActionBar(Toolbar) simply sets this Toolbar to act as the system Toolbar; we’ll
see more of this when we get to the other Activity. In the final line, we’re using the overload of the
setTitle(int) that accepts a string resource ID. We’ll be forced to implement the interface that will add
the onItemClick(...) method, but we can leave that empty for now. Run the code now and verify that
you get a list of countries as a start.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 148
Now we’re ready to create a new Activity. There are several steps involved in this process: creating the
Java class, creating the XML layout, and registering the Activity with the app manifest. Luckily, Android
Studio can do all of this for us with a single wizard! Right-click on the package name of our MainActivity
and go to New → Activity → Empty Activity. We’ll call ours DetailActivity and Android Studio will create
the corresponding layout file and register it with the app manifest. Let’s first look at the layout and
replace it with the following.
1
2
3
4
<?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"
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 149
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
android:orientation="vertical"
tools:context="com.deshpande.intentdemo.DetailActivity">
<include layout="@layout/toolbar" />
<RelativeLayout
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textview_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:layout_centerInParent="true"/>
</RelativeLayout>
</LinearLayout>
We’re just simply declaring a TextView that will display our country. We need a top-level LinearLayout
so that the Toolbar appears first. Then we use the include tag to insert the Toolbar into this layout. We
need a RelativeLayout after the Toolbar so that the TextView is centered on the screen. Now we can go
back to our MainActivity and start the other Activity when a list item was clicked. Let’s add the following
code to the onItemClick(...) method. We also need some constants to keep track of uniqueness so we’ll
add those as fields at the top of the Activity.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 150
1
2
public static final String EXTRA_COUNTRY = "EXTRA_COUNTRY";
private static final int REQUEST_RESPONSE = 1;
1
2
3
4
5
6
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra(EXTRA_COUNTRY, adapter.getItem(position));
startActivityForResult(intent, REQUEST_RESPONSE);
}
In this code snippet, we’re creating a new Intent object, giving it some data, and starting up the other
Activity. The first parameter to the constructor is the Context and the second is the class we want to
start. Each class in Java has a class property that uses Java’s Reflection API (where a program can know
about itself so to speak). This particular parameterized constructor uses Java Reflection to launch the
appropriate Activity. In the next line, we call a method to store the country into the Intent with a unique
String ID. We’ll retrieve this value later in the other Activity. Then, we call the method to start the other
Activity through the Intent. Since we want a response back, we’re also passing in a unique request code
that we’ll be checking later. Now let’s open up the DetailActivity and add the following code in the
onCreate(Bundle) method.
1
2
3
4
5
6
7
8
9
10
String country = getIntent().getStringExtra(MainActivity.EXTRA_COUNTRY);
TextView textView = (TextView) findViewById(R.id.textview_detail);
textView.setText(country);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setTitleTextColor(Color.WHITE);
toolbar.setTitle("Country");
toolbar.setNavigationOnClickListener(this);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 151
11 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
In the very first line, we grab the String hidden away in the Intent. We need the unique ID to grab
it however. Then we set the TextView’s text to be that country. We grab the Toolbar and set the
system’s ActionBar to be this Toolbar. The reason for this is so we can provide backwards-navigation for
the user to get back the the previous Activity. You might have noticed in other apps that it looks like a
left-facing arrow to the left of the Toolbar’s title. Instead of having to import and manage that image,
we can have the system display that for us by getting the ActionBar, in other words our Toolbar, and tell
the system to display that backwards-navigation icon. We just have to make sure we implement the
callback. We set this callback when we call setNavigationOnClickListener(...) . This will force us to
implement an interface and a callback method.
Before we do this, we need to tell Android that the parent activity of DetailActivity is MainActivity. We
can only do this in our app’s manifest file. In our Project view, let’s open up the
app/manifests/AndroidManifest.xml file and find and replace the tag relating to DetailActivity with the
following.
1
2
3
<activity android:name=".DetailActivity" >
<meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" />
</activity>
The above code snippet tells Android that the parent activity of DetailActivity is MainActivity. Now that
we’ve done this, we can go back to our DetailActivity and configure the navigation. To do this however,
we’ll need an extra constant to do the same task as EXTRA_COUNTRY did in MainActivity.
1 public static final String EXTRA_RESPONSE = "EXTRA_RESPONSE";
1
2
3
4
5
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.putExtra(EXTRA_RESPONSE, "All is well!");
setResult(RESULT_OK, intent);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 152
6
7
finish();
}
We’re creating a new Intent object and putting in a small verification message. This method
setResult(...) will be helpful when we go back to MainActivity to read the response. RESULT_OK is a
constant of Activity so we don’t need to declare it. Then we pass the Intent that we created with the
data as well. The final call will finish this current Activity and transfer control back to the Activity which
started DetailActivity, which is MainActivity. Let’s take a look at what this Activity is going to look like.
Now let’s go back to MainActivity and display the response. Let’s override onActivityResult(...) method.
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 153
1
2
3
4
5
6
7
8
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_RESPONSE) {
Toast.makeText(this, data.getStringExtra(DetailActivity.EXTRA_RESPONSE), Toast.LENGTH_SHORT).show();
}
}
}
In this code, we first check if the resultCode turned out to be RESULT_OK, which it did. Then we make
sure that the requestCode corresponds to the code we sent initially. Finally, we create a Toast message
and extract the string from the Intent using the key. This is all there is to do! Let’s run our app!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 154
You can see that when we tap on a list item, we transition to DetailActivity that has the name of the
country in the center of the screen. When we press on the backwards-navigation arrow, we have a Toast
message that says “All is well!”
Conclusion In this post, we covered how we can use Intents to start other Activities and pass data to them. We
also learned how to pass data back to the initial Activity as well. As a bonus, we also went over how to
reuse layouts through the include tag and a bit about the mysterious AndroidManifest.xml file. Intents
are very important since we can use so that we don’t have to create an Activity for everything!
For example, if we want an image, we ask the system, via an Intent, to take the user to the Camera app.
Then we can get the image back in onActivityResult(...) ! It’s really that simple! To summarize,
Intents are powerful objects that will make our Android coding go a lot smoother!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 155
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 156
Android Networking Tutorial with AsyncTask Hello World! In this post, we’re going to lean how to access and use the Internet. This might seem like a
simple task, but we’re going to learn all of what that entails. Specifically, we’ll look at how we can run
networking operations on a different thread as well as how to use Android’s networking client to grab
data. We’ll finally learn how to parse the result to find our specific value in a sea of other information. In
our example, we’re going to pull weather data from a free weather API called OpenWeatherMap
and display the current weather for a given location.
Grab the full source code here.
Before we can create the project, we first need to go to OpenWeatherMap and get an API key. For most
online API services, developers need to create an account and request an API key. We send this
uniquely-generated key whenever we need to use the API to get data from the online API. The identifies
the API call with our signature. Anyway, at the website, sign up for a new account. After that, we
should see a “My Home” page and the “Setup” tab active. Scroll down and there should be a field called
“API key”. We need to write this down or copy it into a text file. We’ll need this unique String
after we create the project. Don’t share this API key with anyone! OpenWeatherMap’s API service
can cost money, but we’ll just be using their free tier of service. Sharing API keys could cause your usage
to spill over into the paid tier!
After we’ve retrieved our OpenWeatherMap API key, let’s create the project! We’ll call it
NetworkingDemo, give it an API level of Marshmallow, and create a blank Activity. Before we do start
creating our app, we need to open up the app/manifests/AndroidManifest.xml file. Above the
application tag, we need to add the following permission for internet access: <uses-permission-sdk-23
android:name="android.permission.INTERNET" />. After we’ve done this, we’re going to set up our view
so that it simply displays the current weather. Let’s go into our activity_main.xml layout file. Replace the
TextView with the following TextView.
1
2
3
4
5
<TextView
android:id="@+id/textView"
android:text="Getting Weather..." android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_centerInParent="true"
android:textSize="28sp"/>
This new TextView has an ID since we’ll need to populate it eventually. The default text is “Getting
Weather…” in case the network is slow. For more complex cases, we would want to show a progress
spinner to tell the user that we’re waiting on the network. Our case is trivial enough that changing the
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 157
TextView will suffice. We also center this child on the screen and change the text size so that it’s easier
to see.
Now we’re ready to add the Java code and handle all of the networking and JSON parsing. Let’s open up
our MainActivity and add a private constant to the top for the API key that looks something like
this: private static final String APP_ID = "INSERT YOUR API KEY HERE!";. You’ll need to replace the value
with your the API key from OpenWeatherMap.
Before we get into the actual networking, let’s first look at threads and threading. A thread is simply a
sequence of execution handled independently of the main sequence. For example, Android has a main
thread that we’ve been working with. Most of our code runs on main thread. In fact, we can only access
UI elements on the main thread, hence it’s also called the UI thread sometimes. But we have a problem
if we want to do networking. If we try to run networking calls on the main/UI thread, our entire app
will freeze until we’re done with the networking request. Imagine you’re waiting for your news feed to
load on a news app on your phone. If we didn’t have threading, your phone would freeze until the
stories are ready to go! To prevent this, we can create another thread, grab the data from the network
from that thread, and give it to our UI thread to update the UI elements. In some cases, we might want
to update a download progress bar, for example. We can use a method to send progress updates to the
main thread for it to update the download progress bar. After the networking is done, we can publish
the result on the main/UI thread for further processing or for display. In fact, Android is now more
proactive and forces you to take this approach so an error will be shown if you try to access the network
on the UI thread!
Instead of creating and managing these threads manually, Android provides us with an AsyncTask
parameterized class to help us do exactly this! Above is a graphic of the different method calls and the
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 158
order in which they are called and the thread they run on. First, onPreExecute() is called and then
doInBackground(...) is called on the background thread immediately after. We can push progress
updates to onProgressUpdate(...) by called publishProgress(...) in doInBackground(...) . Finally, after
doInBackground(...) is finished, we run onPostExecute(...) .
Let’s create an inner subclass of AsyncTask to handle all of our networking after
the onCreate(...) method. Notice that we’re accepting a String as an input, leaving the progress
parameter unused (put in Void), and returning a String. Since AsyncTask is abstract class, we’ll need to
implement doInBackground(...) at least. But we also need to override onPostExecute(...) . We’re going
to be using aliasing to our benefit to populate the TextView. We’re going to pass in a reference to the
TextView we want to populate in the constructor. Since we’re getting a temperature, we can simply set
the TextView’s text to say that temperature in onPostExecute(...) with what we’ll soon get in
doInBackground(...) .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private class GetWeatherTask extends AsyncTask<String, Void, String> {
private TextView textView;
public GetWeatherTask(TextView textView) {
this.textView = textView;
}
@Override
protected String doInBackground(String... strings) {
return null;
}
@Override
protected void onPostExecute(String temp) {
textView.setText("Current Weather: " + temp);
}
}
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 159
Now that we’ve done this, we can look at what we need to do in the background thread. Let me
outline the approach we’re going to take in this background thread. Note that we get a varargs list, but
we only need the first argument from that. However, we’ll need to grab just the first from that list. We
need to create a valid URL from it and open up a connection. Then we’ll get some JSON back and we
need to convert that input stream into a String and convert that into a JSON object. Then we can extract
our value from the JSON. Finally, we need to close the connection. In code, this looks like the following.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protected String doInBackground(String... strings) {
String weather = "UNDEFINED";
try {
URL url = new URL(strings[0]);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream stream = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream));
StringBuilder builder = new StringBuilder();
String inputString;
while ((inputString = bufferedReader.readLine()) != null) {
builder.append(inputString);
}
JSONObject topLevel = new JSONObject(builder.toString());
JSONObject main = topLevel.getJSONObject("main");
weather = String.valueOf(main.getDouble("temp"));
urlConnection.disconnect();
} catch (IOException | JSONException e) {
e.printStackTrace();
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 160
24
25
26
}
return weather;
}
The reason we use a String is so that in case we don’t get a response back, we can account for that. We
need to surround most of this in a try-catch since many of these instance methods throw exceptions. In
the next two lines, we create a new url and connect using Android’s awesome HttpURLConnection class.
The next three lines essentially convert the input stream to a buffered format so we can read it a line at
a time. If we wanted to be more robust, we could check the status code of the URL connection to make
sure it’s an HTTP OK before grabbing an input stream. Then we have a StringBuilder that we append
each line of the response to the StringBuilder. Converting to a JSONObject is a piece of cake since one of
the parameterized constructors for JSONObject takes a String and parses it for us.
JSON stands for Javascript Object Notation and it’s a way to format and organize information in key-
value pairs. We can essentially have JSON Objects with key-value pairs. We can also have JSON Arrays of
objects, each with key-value pairs as well. Look at this link for more information about JSON and
examples of it compared to XML. Looking at the OpenWeatherMap API, we know that we’ll get a top-
level JSONObject. The temperature we’re looking for is another JSON object whose name is “main”.
Then we can extract the temperature inside that object called “temp” and get the value of it. Finally, we
close the connection.
Now that we have our AsyncTask subclass created, it’s very simple to execute it in our
onCreate(...) method.
1
2
3
4
5
6
7
8
9
10
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
double lat = 40.712774, lon = -74.006091;
String units = "imperial";
String url = String.format("http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&units=%s&appid=%s",
lat, lon, units, APP_ID);
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 161
11
12
13
TextView textView = (TextView) findViewById(R.id.textView);
new GetWeatherTask(textView).execute(url);
}
Feel free to replace the lat and lon with your location! The one provided is that of New York City, New
York, USA. The units can either be “imperial” or “metric” depending on your taste of units. If you want
to see the raw JSON, copy the url link and put it in your browser and substitute the lat, lon, API key, and
units. Your browser should show raw JSON text. You can format it by pasting into a JSON formatter here.
In this code, we grab a reference to the TextView to change and pass it to the AsyncTask to
populate after the network call. In the final line, we create a new task and call execute with the single
URL. We don’t need to store it in a variable since we won’t be do anything with it beside calling
execute(...) on it. We can see the current weather at our provided location below!
Zenva Academy – Online courses on game, web and mobile app development
©2017 Zenva Pty Ltd all rights reserved
Page 162
Conclusion In this post, we learned how to access the Internet using AsyncTask and HttpURLConnection. We also
learned that all network calls need to happen on a separate thread and AsyncTask is the best way to
create a background thread. We learned the different methods in AsyncTask and how threading works
to our benefit. Finally, in AsyncTask, we covered how to connect to the OpenWeatherMap API by using
HttpURLConnection as well as how to parse the resulting JSON.