- Importing an Android project in Android Studio,
- Navigating the basic Android project structure,
- Understanding the AndroidManifest.xml file,
- Knowing where the Java source files are located,
- Understanding the basics of resources, such as shape drawables.
If you know all about the learning goals for this lesson move on to lesson 2.
Android Studio contains a new project wizard, which is started by choosing “File” → “New Project…”. While convenient, it also asks you with a lot of questions, settings and steps which might be a bit too overwhelming at this point. For this workshop we'll only focus on importing an existing Android project.
In this lesson we are going to import a very tiny “Hello World” app. Before we do this, please clone this “beginners-workshop” Github repository to your local machine by executing the following command:
git clone https://github.com/Pixplicity/beginners-workshop.git
Not familiar with Git? No problem, just grab the ZIP file straight from the project page:
Download ZIP from GitHub
In Android Studio, chose “File” → “Import Project…”
The File Menu
…or select the “Import Project…” option from the quick start menu
The Quick Start Menu
Then select the “lesson1” folder from the folder where you cloned the “beginners-workshop” Github repository.
Android Studio might ask you to choose the type of project you are importing. If this is the case, make sure to choose “Import project from external model” and select the **Gradle” option.
If all's well Android Studio will start to build your project right away.
After your project is build you can take your project for a spin. Simply click the green run button in the toolbar, select “Run 'helloworld'” from the Run menu or by pressing
Ctrl-R
on a Mac orShift-F10
on Windows or Linux. If all's well you will be prompted to run the app on a device, or you can select an emulator. Check the “Use same device for future launches” checkbox to skip this dialog on the next run.
If you want to run the app on your device, the first thing you need to do is enable “USB debugging” on the device itself (by starting the Settings application and selecting “Developer Options” → “USB Debugging”). If you don't see the developer options item in your settings menu and you are running Android 4.0 (Ice Cream Sandwich) or higher you can enable them by clicking on the “Settings” → “About Phone” → “Build number” item a couple of times to enable them. If you run an older Android version the “USB Debugging” checkbox might reside in the “Applications” section of your settings.
If you have imported the “lesson1” project successfully you will end up with a project structure like this
Android apps are written in the Java programming language. The Android SDK tools compile your code—along with any data and resource files and packages it into an APK-file, an Android package file, which is a ZIP archive file with an .apk
suffix. The APK file contains all the files which make up an Android app and is the file that Android-powered devices use to install your app on your user's Android device.
In the Gradle Scripts section you can see various gradle files. You can use command line tools, Ant, Maven and Gradle to build and package your Android projects into these APK files which you can install on your Android device directly or sign and upload to the Google Play Store. The Android Tools team have chosen the highly flexible Gradle build system as the default Android build system. You can read more about the new build system here.
The build.gradle, gradle.properties and settings.gradle files describe how to build the helloworld project.
- The settings.gradle file describes which sub-projects to include. This is only the helloworld project folder in our case, but you can imagine that with larger projects you end up isolating re-usable code in sub-projects.
- The build.gradle file in the root folder describes the necessary dependencies for the build itself and a common setup for all projects
- The helloworld/build.gradle describes how to build the helloworld Android app. It applies an android plugin and describes the build configuration.
The gradlew
and gradlew.bat
scripts (for unix resp. Windows environments) are gradle wrapper scripts. You can run a build on the command line using this script. It requires a certain Gradle version and if it is not present it downloads the binaries on the fly. The gradlew
scripts and gradle
directory are added automatically when you create a project with the Android Studio New Project Wizard.
lesson1> ./gradlew assembleDebug
:helloworld:compileDebugNdk UP-TO-DATE
:helloworld:preBuild
:helloworld:preDebugBuild
:helloworld:checkDebugManifest
:helloworld:prepareDebugDependencies
:helloworld:compileDebugAidl UP-TO-DATE
:helloworld:compileDebugRenderscript UP-TO-DATE
:helloworld:generateDebugBuildConfig UP-TO-DATE
:helloworld:mergeDebugAssets UP-TO-DATE
:helloworld:generateDebugResValues UP-TO-DATE
:helloworld:generateDebugResources UP-TO-DATE
:helloworld:mergeDebugResources UP-TO-DATE
:helloworld:processDebugManifest UP-TO-DATE
:helloworld:processDebugResources UP-TO-DATE
:helloworld:generateDebugSources UP-TO-DATE
:helloworld:compileDebugJava UP-TO-DATE
:helloworld:preDexDebug UP-TO-DATE
:helloworld:dexDebug UP-TO-DATE
:helloworld:processDebugJavaRes UP-TO-DATE
:helloworld:validateDebugSigning
:helloworld:packageDebug UP-TO-DATE
:helloworld:assembleDebug UP-TO-DATE
BUILD SUCCESSFUL
Total time: 5.702 secs
The build.gradle
file in the “helloworld” directory is very simple.
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
}
The first line says to apply the Android application plugin for this build, whereafter (with the code block preceded by the word 'android') we immediately configure this plugin with the Android Domain Specific Language (DSL). More info
The minSdkVersion
version states which Android OS level you minimally support with your app. We have chosen to support version 14 (Android 4.0 - Ice Cream Sandwich) and up in this workshop.
The targetSdkVersion
informs the system that you have tested against the target version and the system should not enable any compatibility behaviors to maintain your app's forward-compatibility with the target version. The application is still able to run on older versions (down to minSdkVersion
). To maintain your application along with each Android release, you should increase the value of this attribute to match the latest API level, then thoroughly test your application on the corresponding platform version.
The versionCode
is a value which is used mainly for distribution on the Google Play Store. Every update should have a higher version code than the previous package. Don't worry about forgetting this. When you upload your app to the Play Store you will be notified if this is not the case.
The versionName
value is a user-friendly name for the app version and can be any string. This value is visible to the end-users on Google Play.
Furthermore the Gradle build system (like the Maven build system) depends on a certain folder structure.
The basic project starts with two components called “source sets”. The main source code and the test code. These live respectively in:
src/main/
src/androidTest/
Inside each of these folders exists folder for each source components. For both the Java and Android plugin, the location of the Java source code and the Java resources:
java/
resources/
For the Android plugin, extra files and folders specific to Android:
AndroidManifest.xml
res/
assets/
aidl/
rs/
jni/
Note: Don't confuse the
src/main/resources
and thesrc/main/java/res
folders. The former is for pure Java builds and the latter is used for Android resources.
Every application must have an AndroidManifest.xml file (with precisely that name) in its root directory. Let's open up the AndroidManifest.xml
file of the helloworld app which lives in the in helloworld/src/main
folder.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pixplicity.workshop.beginners.helloworld">
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The manifest file presents essential information about your app to the Android system, information the system must have before it can run any of the app's code. Among other things, the manifest does the following:
- It names the Java package for the application. The package name serves as a unique identifier for the application.
- It describes the components of the application (in our example the activity MainActivity) and under what conditions they can be launched.
- It declares which permissions the application must have in order to access protected parts of the API and interact with other applications. We don't require any extra permissions in our app, but if you e.g. need internet access, or want to lookup a contact you need to declare those permission here.
More info - It declares the minimum level of the Android API that the application requires. In our case this information is not present in our development AndroidManifest.xml file, but it will be added in the Gradle build, using the information in the
defaultConfig
section in the ./helloworld/build.gradle file.
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View)
. There are two methods almost all subclasses of Activity will implement:
onCreate(Bundle)
is where you initialize your activity. Most importantly, here you will usually callsetContentView(int)
with a layout resource defining your UI, and usingfindViewById(int)
to retrieve the widgets in that UI that you need to interact with programmatically. More on that in lesson 2.onPause()
is where you deal with the user leaving your activity. Most importantly, any changes made by the user should at this point be saved.
To be of any use all activity classes must have a corresponding <activity>
declaration in their package's AndroidManifest.xml
.
We only have one activity in our Hello World project, the MainActivity. Let's open up MainActivity.java and see what's inside.
You can quickly open a class-file by pressing
Cmd-O
on a Mac andCtrl-N
on a Windows or Linux machine.
Tip: Type
Cmd-Shift-A
/Ctrl-Shift-A
to browse through the various keyboard shortcuts, or download a PDF file with the most common keyboard shortcuts for Windows or Linux or Mac OS X.
package com.pixplicity.workshop.beginners.helloworld;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
The onCreate()
method is one of the Activity Life Cycle methods (more on that in lesson 2). In plain English, when our activity is created (by the Android system) we don't do anything with the saved instance state (whatever that is, we simply propagate it up to our super class) and set the content view layout to R.layout.activity_main.
R.layout.activity_main
refers to an integer in a generated class with name R. Go ahead, open R.java
and see what's inside. See anything familiar? Browse around the res
folder. See something familiar? Android keeps references of all the resources in the res
directory and saves references to these files as public static final integers in the R.java file. Again, this file is generated, so don't change anything in this file directly, change the file name (or its contents) in the res
directory instead.
The Android project resources
A resource is a localized text string, bitmap, video, soundbite or other small piece of non-code information that your program needs. At build time all your resources get compiled into your application. This is useful for internationalization and for supporting multiple device types.
You store your resources in the res directory inside your project. The Android resource compiler (aapt) processes resources according to which subfolder they are in and the format of the file. For example, PNG and JPG format bitmaps should go in a directory starting with res/drawable, and XML files that describe screen layouts should go in a directory starting with res/layout. You can add suffixes for particular languages, screen orientations, pixel densities, and more.
The resource compiler compresses and packs your resources and then generates a class named R that contains identifiers you use to reference those resources in your program. This is a little different from standard Java resources, which are referenced by key strings. Doing it this way allows Android to make sure all your references are valid and saves space by not having to store all those resource keys.
You might have noticed there are several drawable
directories with different suffixes. These suffixes are called qualifiers and narrow down for which devices these resources should be used. All the default drawables go into the drawable
directory directory. Any optimized images for e.g. a high density (hdpi), medium density (mdpi) or extra high density (xhdpi) screen go into the drawable-hdpi
, drawable-mdpi
and drawable-xhdpi
directories respectively. Drawables in these directories override any drawables in less specific / qualified directories.
In the res/values
directory we see a strings.xml
file. This file contains one string-resource with name app_name
.
Suppose that your application's default language is English. Suppose also that you want to localize all the text in your application to Dutch. In this case, you could create an alternative strings.xml files, stored in a locale-specific resource directory:
res/values/strings.xml
Contains English text for all the strings that the application uses, including text for a string namedapp_name
.res/values-nl/strings.xml
Contains Dutch text for all the strings you want to override. In other words, you don't need to override all the strings. If, for example, theapp_name
is the same in any language you only have to define it once inres/values/strings.xml
.
Localization is not only for strings. You can localize any resource type by adding the
-<locale>
suffix, e.g.res/drawable-nl
could contain images with Dutch words.
We already saw a reference to R.layout.activity_main
in the onCreate()
method in MainActivity.java. This integer points to the file res/activity_main.xml
. Let's open that file.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/app_name"
android:gravity="center"/>
</LinearLayout>
The most used method to define User Interfaces in Android is XML. There's nothing holding you back in creating UIs in code directly, but there are two main reasons why XML is preferred in most cases:
- Separation Of Concerns (SOC) - Defining your UI in xml helps you separate the UI from the code that controls its behavior.
- Resource qualifiers - Storing different layout XML files in different (qualified) resource directories is a powerful tool to create different user experiences for different device types. The most simple case is a default UI in the
res/layout
directory and e.g. a landscape version of your UI in theres/layout-land
directory.
LinearLayout is a ViewGroup, which in turn is a View. It describes the way it layouts its children (in a linear way). RelativeLayout lays out its children relative to other views in the view group, or to the parent border (the RelativeLayout itself). GridLayout lays out its children in a grid, etc. There are a couple of other layout managers and it's pretty easy to create a layout manager yourself.
Some parameters are common to all layouts:
xmlns:android="http://schemas.android.com/apk/res/android"
Defines the XML namespace for Android. You should define this once, on the first XML tag in the file.android:layout_width="match_parent"
,android:layout_height="wrap_content"
Defines the dimensions of the view. In this case, it takes up the entire width of the parent (indeed, the entire window) and has a height which perfectly fits its contents. Possible values are exact measurements (such as48dp
),match_parent
andwrap_content
.
For the following exercises you can use the “Android preview” tool window which opens up when you open the activity_main.xml
file. There's no need to compile and deploy the app on your phone yet.
Open the activity_main.xml
file and change the android:layout_width
and android:layout_height
attributes of both the LinearLayout and the TextView to either match_parent
or wrap_content
.
Play around with different combinations. Can you explain what happens?
Add another TextView to the res/activity_main.xml
layout file and see what happens. Can you predict how the other TextView will be layed out?
Add an attribute android:orientation="vertical"
to the LinearLayout tag. What happens?
Add an attribute android:textColor="#B20"
to the TextView tag. Try to find other text attributes you can change.
Create a new resource file with name res/drawable/background.xml
and add the xml below to that file. Now add an attribute android:background="@drawable/background"
to the LinearLayout or one of the TextView tags in the activity_main.xml
file.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="45"
android:centerColor="#ffbaff8c"
android:centerX="25%"
android:endColor="#ff94adff"
android:startColor="#ffffb48c"
android:type="linear"/>
</shape>
Creating shape drawables (as they are called) in XML is a very powerful tool in Android. This online tool can create gradient XML files for you. Simply click the “Android” tab at the bottom to get the generated XML.
##Conclusion This simple hello world app is only scratching the surface of what Android can do, but it gives you great insight in how Android Studio works. On to lesson 2 where you will learn more about Activities and the Activity Life Cycle, more Views and how to interact with Views in your code.