We are learning how to develop Android apps in this series. So far, we've looked at the structure of an Android app and explored user interfaces. In this tutorial, we will learn to work with the project resources.
Introduction
In this part of the series, we will examine the resource types you will most likely use in your first projects. Project resources include the layouts, images, and data values used within the app. When you create a new project, a few folders for common resource types are included in your project directory automatically. You can add folders for additional types should you need them.
You can find the resources for the project we created earlier in the series by browsing to the res folder in the Package Explorer. Expand the folder to see its contents. You can add new folders to the resources directory and add new files to each one, as well as working with the existing files (as we did with the layout and string value files earlier in this series).
The exact directory structure that you see in the package explorer will depend on your perspective. The image below shows the directory structure for the Android and Project perspectives.
Alternative Resources
Before we start with the specifics, it's worth noting that you can place your Android resources into two categories: resources that can be used across devices and resources that are tailored to a subset of devices.
You can see an example of this in the existing project structure. In the Android Studio Package Explorer, look at the res directory. Remember that the different drawable folders are tailored to specific device screen densities. We will add one in this tutorial for drawable files that are not targeted (ones that can be used across devices).
You can add alternative directories for each resource type using qualifiers to target categories, as Android Studio has done with "drawable-hdpi", "drawable-xhdpi", and so on. The Android platform supports such qualifiers for various aspects of the user device, including screen size, density, API level, language, region, and more.
You might have noticed that the additional folders like drawable-mdpi and drawable-v24 are only visible within the Project perspective. When looking at the directories from the Android perspective, you only see a single drawable folder, with variations listed in brackets.
Any resource type folder that does not include a qualifier in the name is for resources that can be used across devices. You will not always need qualified folders for all of your resource types, but when you test your apps on different devices, you may find that some aspects need tweaking to remain usable across configurations.
In this tutorial, you will learn about three different resource types: Drawables, Strings, and Layouts.
Drawables
You can create multiple drawable folders in Android Studio, each targeted at a particular density bucket. The drawable folders contain any images you use in your app. You can include images prepared outside of Android Studio in digital formats, such as JPEG, PNG, and GIF. You can also define drawables in XML code. Let's do this and add one to the main layout.
Although you should generally try to tailor your drawables to each target density, for the purposes of this tutorial we will use a single drawable for all devices.
Expand the res directory if it isn't already expanded and then select the drawable directory. Select New from the context menu and then click on the Drawable Resource File menu option.
This will open a new pop-up window where you can set a new file name, the root element of the drawable resource, and the directory name which is set to drawable. There is also a list of available qualifiers if you want to create a resource file for a particular subset of devices. In our case, the drawable will be used on all devices, so we don't select any qualifier.
Click OK, and you should see a new file named purple_radial_gradient.xml inside the drawable folder. This is the process to follow any time you need to create a new folder in your project.
The current contents of the file should be similar to the XML code shown below:
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <shapexmlns:android="https://schemas.android.com/apk/res/android"> |
3 | |
4 | </shape> |
Defining the Shape Drawable
A shape
drawable is ideal for styling some basic UI elements in your app. In this tutorial, we will create a rectangular shape with a purple radial background.
With a shape
drawable, you can choose from a range of generic shape types, including rectangle
, oval
, line
, and ring
. Once you choose a shape type, you can then define properties of the shape including solid or gradient colors, corners, padding, dimensions, and strokes.
Edit your root shape
element to be a rectangle:
1 | <shapexmlns:android="http://schemas.android.com/apk/res/android" |
2 | android:shape="rectangle"> |
3 | |
4 | </shape> |
Choosing a rectangle as our shape creates a rectangle drawable that fills the containing view. A rectangle is also the default shape if you don't provide a value.
The shape element can contain multiple child elements to control the overall appearance of our drawable. Let's begin with the gradient
element. Place the following code inside the shape
element.
1 | <gradient |
2 | android:type="radial" |
3 | android:gradientRadius="200dp" |
4 | android:startColor="@color/purple_700" |
5 | android:endColor="@color/black"/> |
We have set the gradient type to radial
. Other possible values are linear
and sweep
. Next, we set the gradientRadius
value to 200dp. This attribute is only applicable if we use a radial gradient.
You can specify the start, center, and end color for the gradient using the startColor
, centerColor
, and endColor
attributes. We are only specifying the start and end color values here.
The corners
element can be used to create rounded corners for our rectangular shape. It won't apply to any other shapes. Let's apply a 5dp radius to our rectangle by adding the following code below the gradient
element.
1 | <corners |
2 | android:radius="5dp"/> |
Finally, we will add some padding around our view elements using the padding
element. The view element in our example would be a button, and we want the button text to be at a separation from the actual button boundary. The padding would be equal to 10dp on all sides.
1 | <padding |
2 | android:left="10dp" |
3 | android:right="10dp" |
4 | android:top="10dp" |
5 | android:bottom="10dp"/> |
The shape drawable will look like the image below:
Using the Shape Drawable
Now it's time to use the drawable resource that we created earlier to display it as a background for one of our views. We will keep things simple here and add a button element to our layout file with the following code:
1 | <Button |
2 | android:background="@drawable/purple_radial_gradient" |
3 | android:text="Reset" |
4 | android:textColor="@color/white" |
5 | //..otherattributes/> |
The background
attribute can be used to reference a drawable resource or a solid color to be used as a background for our element. In this case, we are setting it to our purple_radial_gradient drawable.
In some cases, you might notice that applying the background this way won't actually change the background of the button. This is probably due to the theme being used in your app. You will have to use the AppCompat theme instead of the MaterialComponents theme.
An alternative way to apply the background if you want to keep using the MaterialComponents theme is to use androidx.appcompat.widget.AppCompatButton instead of a regular button. Here is an example:
1 | <androidx.appcompat.widget.AppCompatButton |
2 | android:background="@drawable/purple_radial_gradient" |
3 | android:text="Reset" |
4 | android:textColor="@color/white" |
5 | //..otherattributes/> |
Strings
Android applications convey a lot of information with text. For example, the text for a button that resets something could be Reset. One way to set the value of the button text to Reset is to simply set the value of the android:text
attribute to Reset.
Hard-coding text values like this isn't recommended while developing Android apps, and we should use string resources for that. String resources can be of three different types: String, String Array, and Quantity Strings. We will limit ourselves to simple strings in this tutorial.
String resources are stored in the res/values/ directory or the res/values/strings/ directory if there are multiple qualifiers for the same file name.
Do you remember how we created a resource file for our shape drawable in the previous section? Follow the same steps to create a resource file for the Hindi language and the Indian region. You can also leave the region unselected if you want.
You should now have two strings.xml files: a default file for English and another one for Hindi. Place the following code inside the default strings.xml file:
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <resources> |
3 | <stringname="app_name">Empty Activity</string> |
4 | <stringname="reset_button">Reset</string> |
5 | <stringname="greet_button">Greet!</string> |
6 | </resources> |
Place the following code inside the strings.xml file for the Hindi language in the Indian region.
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <resources> |
3 | <stringname="app_name">रिक्त गतिविधि</string> |
4 | <stringname="reset_button">रीसेट</string> |
5 | <stringname="greet_button">अभिवादन!</string> |
6 | </resources> |
What benefit did we get by creating these separate string resource files? Let's say someone in India has set their phone language to Hindi because they don't know English. Your app will be useless to them if you use hard-coded string values for your button or other elements.
Now, if you have provided a string resource file that contains translations of the corresponding English terms, Android will automatically update the UI to display the appropriate text for different elements. Here is a screenshot that shows this in action:
Layouts
Did you notice how different UI elements or views of our empty activity are laid out on the device screen? Their position is determined by the layout resource file. These resource files are located under the res/layout/ directory.
We have an activity called MainActivity
, and its UI is defined by a file named activity_main.xml. Here is the XML code inside this file that controls the layout of the two buttons in our UI:
1 | <androidx.appcompat.widget.AppCompatButton |
2 | android:id="@+id/reset_button" |
3 | android:layout_width="wrap_content" |
4 | android:layout_height="wrap_content" |
5 | android:background="@drawable/purple_radial_gradient" |
6 | android:text="@string/reset_button" |
7 | android:textColor="@color/white" |
8 | android:layout_marginTop="30dp" |
9 | android:layout_marginStart="30dp" |
10 | app:layout_constraintTop_toBottomOf="@id/image_view" |
11 | app:layout_constraintStart_toStartOf="parent"/> |
12 | |
13 | <androidx.appcompat.widget.AppCompatButton |
14 | android:id="@+id/greet_button" |
15 | android:layout_width="wrap_content" |
16 | android:layout_height="wrap_content" |
17 | android:background="@drawable/purple_radial_gradient" |
18 | android:text="@string/greet_button" |
19 | android:layout_marginTop="30dp" |
20 | android:layout_marginEnd="30dp" |
21 | android:textColor="@color/white" |
22 | app:layout_constraintEnd_toEndOf="parent" |
23 | app:layout_constraintTop_toBottomOf="@+id/image_view"/> |
We have made use of our drawable resources as well as string resources in these two buttons. The android:background
attribute references the purple radial gradient shape that we defined in the first section. This adds a radial gradient to both our buttons.
The android:text
attribute determines the text to display inside the button elements. You might have noticed that we just refer to the strings by their corresponding name
attribute. Android will automatically take care of picking the right value for these resources.
In the previous section, you saw that the screenshots of the English and Hindi instances of the app had a greyed-out profile icon. We added that to our activity using the ImageView
element. We use image view to display Bitmap
or Drawable
resources. The profile icon is actually a VectorDrawable
resource, and using it allows us to manage only one file instead of several variations for different screen sizes. Here is the XML code for our ImageView
.
1 | <ImageView |
2 | android:id="@+id/image_view" |
3 | android:layout_width="200dp" |
4 | android:layout_height="200dp" |
5 | app:layout_constraintEnd_toEndOf="parent" |
6 | app:layout_constraintStart_toStartOf="parent" |
7 | app:layout_constraintBottom_toBottomOf="parent" |
8 | app:layout_constraintTop_toTopOf="parent" |
9 | app:srcCompat="@drawable/profile_icon"/> |
There is also a TextView
element that adds the hard-coded string "Hello Everyone!" to our activity. It has the following XML:
1 | <TextView |
2 | android:id="@+id/text_view" |
3 | android:layout_width="wrap_content" |
4 | android:layout_height="wrap_content" |
5 | android:text="Hello Everyone!" |
6 | android:textSize="20sp" |
7 | app:layout_constraintBottom_toBottomOf="parent" |
8 | app:layout_constraintEnd_toEndOf="parent" |
9 | app:layout_constraintStart_toStartOf="parent" |
10 | app:layout_constraintBottom_toTopOf="@id/image_view"/> |
The android:text
attribute value of TextView
is hard-coded into the layout file. That's why it did not change when we updated the language on the phone. You shouldn't do this if you want your app to be accessible to more people.
Other Resource Types
So far, we've used three resource types in the app: layout, drawable, and values—or, more specifically, strings. There are more you can use in your apps, using the same process we used above to reference them. As we saw earlier in the series, you can also reference resources in your Java files using the following syntax:
1 | //R.type.name |
2 | R.string.pic//example |
Let's briefly summarize some of the other resource types you may find useful in future apps. We used drawable and layout resources above and in previous tutorials, and used string values in the layout file. Expand the values folder in the Package Explorer. Alongside the strings file, Android Studio typically adds one for colors as well. You will also find some theme files in there.
As I mentioned above, you can create alternative resource type folders using qualifiers for particular device properties. You can target devices using many different qualifiers. For example, you may want to use a fixed width and height for the ImageView
we added, tailoring the displayed size to the device screen size. To achieve this, you could add value folders for each size or density bucket ("-small", "-large", "-hdpi", "-mdpi", etc.), with a dimension file in each. If you include a dimension value in each of these files, giving the value the same name but a different number, Android will automatically pick the correct one for the user's device.
Other resource types you may find useful include those for numbers, menus, animation, and color values. To define XML animations, you can add an anim folder to res, or add your animation files to the drawable folders, depending on the type you use.
If you want to use a set of colors across your app UI, you can define them in a file saved in the values directory, using the color
element. Each color
element can contain a HEX value and a name attribute, so that you can refer to the color in your other files. You might recall that the process is similar to the string resources. This time, however, we are using it to change color values based on themes instead of text values based on locales.
XML resources that do not fall under one of the defined categories on Android can be saved in an xml directory in res.
For an overview of all of the resource types on Android, see the Developer Guide sections on Resource Types and More Resource Types. Although your needs will be fairly straightforward at first, it's worth looking through the list now so that you have an idea of what's possible later on.
Conclusion
In this tutorial, we looked at the basics of app resources on Android. But as you have seen, there is much more to explore. For your first few apps, you can keep things relatively simple as you get accustomed to the practice of using resources. But as your apps develop, you should try to think about the variety of user devices your apps may run on, providing additional resources as necessary.
In the next part of the series, we will look at the project manifest file.