The material design team at Google defines the functionality of dialogs in Android as follows:
Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.
Now you have understood what dialogs are used for, it's now time to learn how to display them. In this tutorial, I'll take you through the process of showing different kinds of material design dialogs in Android. We'll cover the following dialogs:
- alert
- single and multiple choice
- time and date picker
- bottom sheet dialog
- full-screen dialog
A sample project for this tutorial can be found on our GitHub repo for you to easily follow along.
1. Alert Dialog
According to the official Google material design documentation:
Alerts are urgent interruptions, requiring acknowledgement, that inform the user about a situation.
Creating an Alert Dialog
Make sure you include the latest appcompat
artifact in your build.gradle
file (app module). The minimum supported API level is Android 4.0 (API level 14).
dependencies { implementation 'com.android.support:appcompat-v7:26.1.0' }
The next thing is to create an instance of AlertDialog.Builder
.
new AlertDialog.Builder(this) .setTitle("Nuke planet Jupiter?") .setMessage("Note that nuking planet Jupiter will destroy everything in there.") .setPositiveButton("Nuke", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Log.d("MainActivity", "Sending atomic bombs to Jupiter"); } }) .setNegativeButton("Abort", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Log.d("MainActivity", "Aborting mission..."); } }) .show();
Here we created an instance of AlertDialog.Builder
and began configuring the instance by calling some setter methods on it. Note that we are using the AlertDialog
from the Android support artifact.
import android.support.v7.app.AlertDialog;
Here are the details of the setter methods we called on the AlertDialog.Builder
instance.
setTitle()
: set the text to show in the title bar of the dialog.setMessage()
: set the message to display in the dialog.setPositiveButton()
: the first argument supplied is the text to show in the positive button, while the second argument is the listener called when the positive button is clicked.setNegativeButton()
: the first argument supplied is the text to show in the negative button, while the second argument is the listener called when the negative button is clicked.
Note that AlertDialog.Builder
has a setView()
to set your custom layout view to it.
To show our dialog on the screen, we just invoke show()
.
There is another setter method called setNeutralButton()
. Calling this method will add another button on the far left side of the dialog. To call this method, we have to pass a String
that will serve as the button text, and also a listener that is called when the button is tapped.
new AlertDialog.Builder(this) // other setter methods .setNeutralButton("Neutral", null) .show()
Note that touching outside the dialog will automatically dismiss it. To prevent that from happening, you will have to call the setCanceledOnTouchOutside()
on the AlertDialog
instance and pass false
as an argument.
AlertDialog dialog = new AlertDialog.Builder(this) // after calling setter methods .create(); dialog.setCanceledOnTouchOutside(false); dialog.show();
To further prevent dismissing the dialog by pressing the BACK button, you then have to call setCancelable()
on the AlertDialog
instance and pass false
to it as an argument.
Styling an Alert Dialog
It's quite easy to style our dialog. We just create a custom style in the styles.xml resource. Observe that this style parent is Theme.AppCompat.Light.Dialog.Alert
. In other words, this style inherits some style attributes from its parent.
<style name="CustomDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert"><!--buttons color--><item name="colorAccent">@android:color/holo_orange_dark</item><!--title and message color--><item name="android:textColorPrimary">@android:color/white</item><!--dialog background--><item name="android:windowBackground">@drawable/background_dialog</item></style>
We begin customising the dialog style by setting the values of the attributes to be applied on the dialog—for example, we can change the dialog button colour to be @android:
color
/holo_orange_dark
and also set the dialog background to a custom drawable in our drawable resource folder (android:windowBackground
set to @drawable/background_dialog
).
Here is my background_dialog.xml resource file.
<?xml version="1.0" encoding="utf-8"?><!-- From: support/v7/appcompat/res/drawable/abc_dialog_material_background_light.xml --><inset xmlns:android="http://schemas.android.com/apk/res/android" android:insetLeft="16dp" android:insetTop="16dp" android:insetRight="16dp" android:insetBottom="16dp"><shape android:shape="rectangle"><corners android:radius="10dp" /><solid android:color="@android:color/holo_green_dark" /><stroke android:width="5dp" android:color="@android:color/black" /></shape></inset>
Here we created a custom InsetDrawable
which allows us to add insets on any side of the ShapeDrawable
. We created a rectangle shape using the <shape>
tag. We set the android:shape
attribute of the <shape>
tag to a rectangle
(other possible values are line
, oval
, ring
). We have a child tag <corners>
that sets the radius of the rectangle corners. For a solid fill, we added the <solid>
tag with an android:color
attribute which indicates what color to use. Finally, we gave our drawable a border by using the <stroke>
tag on the <shape>
.
To apply this style to the dialog, we just pass the custom style to the second parameter in the AlertDialog.Builder
constructor.
AlertDialog dialog = new AlertDialog.Builder(this, R.style.CustomDialogTheme)
2. Confirmation Dialogs
According to the material design documentation:
Confirmation dialogs require users to explicitly confirm their choice before an option is committed. For example, users can listen to multiple ringtones but only make a final selection upon touching “OK.”
The following different kinds of confirmation dialog are available:
- multiple choice dialog
- single choice dialog
- date picker
- time picker
Multiple Choice Dialog
We utilize a multiple choice dialog when we want the user to select more than one item in a dialog. In a multiple choice dialog, a choice list is displayed for the user to choose from.
String[] multiChoiceItems = getResources().getStringArray(R.array.dialog_multi_choice_array); final boolean[] checkedItems = {false, false, false, false}; new AlertDialog.Builder(this) .setTitle("Select your favourite movies") .setMultiChoiceItems(multiChoiceItems, checkedItems, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int index, boolean isChecked) { Log.d("MainActivity", "clicked item index is " + index); } }) .setPositiveButton("Ok", null) .setNegativeButton("Cancel", null) .show();
To create a multiple choice dialog, we simply call the setMultiChoiceItems()
setter method on the AlertDialog.Builder
instance. Inside this method, we pass an Array
of type String
as the first parameter. Here's my array, located in the arrays resource file /values/arrays.xml.
<?xml version="1.0" encoding="utf-8"?><resources><string-array name="dialog_multi_choice_array"><item>The Dark Knight</item><item>The Shawshank Redemption</item><item>Saving Private Ryan</item><item>The Silence of the Lambs</item></string-array></resources>
The second parameter to the method setMultiChoiceItems()
accepts an array which contains the items that are checked. The value of each element in the checkedItems
array corresponds to each value in the multiChoiceItems
array. We used our checkedItems
array (the values of which are all false
by default) to make all items unchecked by default. In other words, the first item "Dark Knight"
is unchecked because the first element in the checkedItems
array is false
, and so on. If the first element in the checkedItems
array was true instead, then "Dark Knight"
would be checked.
Note that this array checkedItems
is updated when we select or click on any item displayed—for example, if the user should select "The Shawshank Redemption"
, calling checkedItems[1]
would return true
.
The last parameter accepts an instance of OnMultiChoiceClickListener
. Here we simply create an anonymous class and override onClick()
. We get an instance of the shown dialog in the first parameter. In the second parameter, we get the index of the item that was selected. Finally, in the last parameter, we find out if the selected item was checked or not.
Single Choice Dialog
In a single choice dialog, unlike the multiple choice dialog, only one item can be selected.
String[] singleChoiceItems = getResources().getStringArray(R.array.dialog_single_choice_array); int itemSelected = 0; new AlertDialog.Builder(this) .setTitle("Select your gender") .setSingleChoiceItems(singleChoiceItems, itemSelected, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int selectedIndex) { } }) .setPositiveButton("Ok", null) .setNegativeButton("Cancel", null) .show();
To create a single choice dialog, we simply invoke the setSingleChoiceItems()
setter on the AlertDialog.Builder
instance. Inside this method, we also pass an Array
of type String
as the first parameter. Here's the array we passed, which is located in the arrays resource file: /values/arrays.xml.
<?xml version="1.0" encoding="utf-8"?><resources><! -- ... --><string-array name="dialog_single_choice_array"><item>Male</item><item>Female</item><item>Others</item></string-array></resources>
The second parameter of the setSingleChoiceItems()
is used to determine which item is checked. The last parameter in onClick()
gives us the index of the item that was selected—for example, selecting the Female item, the value of selectedIndex
will be 1
.
Date Picker Dialog
This is a dialog picker that is used to select a single date.
To start, we'll create a Calendar
field instance in the MainActivity
and initialize it.
public class MainActivity extends AppCompatActivity { Calendar mCalendar = Calendar.getInstance(); // ...
Here we called Calendar.getInstance()
to get the current time (in the default time zone) and set it to the mCalendar
field.
DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { mCalendar.set(Calendar.YEAR, year); mCalendar.set(Calendar.MONTH, monthOfYear); mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); String date = DateFormat.getDateInstance(DateFormat.MEDIUM).format(calendar.getTime()); Log.d("MainActivity", "Selected date is " + date); } }, mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH)); datePickerDialog.show();
To show a date picker dialog, we create an instance of the DatePickerDialog. Here is the explanation of the parameter definitions when creating an instance of this type.
- The first parameter accepts a parent context—for example, in an
Activity
, you usethis
, while in aFragment
, you callgetActivity()
. - The second parameter accepts a listener of type
OnDateSetListener
. This listeneronDateSet()
is called when the user sets the date. Inside this method, we get the selected year, the selected month of the year, and also the selected day of the month. - The third parameter is the initially selected year.
- The fourth parameter is the initially selected month (
0
-11
). - The last parameter is the initially selected day of the month (
1
-31
).
Finally, we call the show()
method of the DatePickerDialog
instance to display it on the current screen.
Setting a Custom Theme
It's quite easy to customize the theme of the date picker dialog (similar to what we did to the alert dialog).
Briefly, you create a custom drawable, create a custom style or theme, and then apply that theme when creating a DatePickerDialog
instance in the second parameter.
DatePickerDialog datePickerDialog = new DatePickerDialog(this, R.style.MyCustomDialogTheme, listener, 2017, 26, 11);
Time Picker Dialog
The time picker dialog allows the user to pick a time, and adjusts to the user’s preferred time setting, i.e. the 12-hour or 24-hour format.
As you can see in the code below, creating a TimePickerDialog
is quite similar to creating a DatePickerDialog
. When creating an instance of the TimePickerDialog
, we pass in the following parameters:
- The first parameter accepts a parent context.
- The second parameter accepts an
OnTimeSetListener
instance that serves as a listener. - The third parameter is the initial hour of the day.
- The fourth parameter is the initial minute.
- The last parameter is to set whether we want the view in 24-hour or AM/PM format.
TimePickerDialog timePickerDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker timePicker, int hourOfDay, int minute) { mCalendar.set(Calendar.HOUR_OF_DAY, hourOfDay); mCalendar.set(Calendar.MINUTE, minute); String time = DateFormat.getTimeInstance(DateFormat.SHORT).format(calendar.getTime()); Log.d("MainActivity", "Selected time is " + time); } }, mCalendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), true); timePickerDialog.show();
The onTimeSet()
method is called every time the user has selected the time. Inside this method, we get an instance of the TimePicker
, the selected hour of the day chosen, and also the selected minute.
To display this dialog, we still call the show()
method.
The time picker can be styled in a similar way to the date picker dialog.
3. Bottom Sheet Dialog
According to the official Google material design documentation:
Bottom sheets slide up from the bottom of the screen to reveal more content.
To begin using the bottom sheet dialog, you have to import the design support artifact—so visit your app module's build.gradle file to import it.
dependencies { implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:design:26.1.0' }
Make sure that the activity or fragment for the bottom sheet dialog will pop up—its parent layout is the CoordinatorLayout
.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"<!-- ... --><!--bottom sheet container--><FrameLayout android:id="@+id/framelayout_bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="@string/bottom_sheet_behavior" /></android.support.design.widget.CoordinatorLayout>
Here we also have a FrameLayout
that would serve as a container for our bottom sheet. Observe that one of this FrameLayout
's attributes is app:layout_behavior
, whose value is a special string resource that maps to android.support.design.widget.BottomSheetBehavior
. This will enable our FrameLayout
to appear as a bottom sheet. Note that if you don't include this attribute, your app will crash.
public class MainActivity extends AppCompatActivity {// ... // ... private BottomSheetDialog mBottomSheetDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ... View bottomSheet = findViewById(R.id.framelayout_bottom_sheet); } }
Here we declared an instance of BottomSheetDialog
as a field to our MainActivity.java and initialized it in the onCreate()
method of our activity.
final View bottomSheetLayout = getLayoutInflater().inflate(R.layout.bottom_sheet_dialog, null); (bottomSheetLayout.findViewById(R.id.button_close)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mBottomSheetDialog.dismiss(); } }); (bottomSheetLayout.findViewById(R.id.button_ok)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "Ok button clicked", Toast.LENGTH_SHORT).show(); } }); mBottomSheetDialog = new BottomSheetDialog(this); mBottomSheetDialog.setContentView(bottomSheetLayout); mBottomSheetDialog.show();
In the preceding code, we inflated our bottom sheet layout R.layout.bottom_sheet_dialog
. We set listeners for the Cancel and Ok buttons in the bottom_sheet_dialog.xml. When the Cancel button is clicked, we simply dismiss the dialog.
We then initialized our mBottomSheetDialog
field and set the view using setContentView()
. Finally, we call the show()
method to display it on the screen.
Here is my bottom_sheet_dialog.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:orientation="vertical" android:padding="15dp"><TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginBottom="10dp" android:layout_marginTop="10dp" android:text="Title" android:textAppearance="@style/TextAppearance.AppCompat.Title" /><TextView android:id="@+id/tv_detail" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginTop="5dp" android:text="@string/lorem_ipsum" android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/><LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:gravity="right" android:orientation="horizontal"><Button android:id="@+id/button_close" style="@style/Base.Widget.AppCompat.Button.Borderless.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="CLOSE" /><Button android:id="@+id/button_ok" style="@style/Base.Widget.AppCompat.Button.Borderless.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="OK" /></LinearLayout></LinearLayout>
Make sure you check out How to Use Bottom Sheets With the Design Support Library by Paul Trebilcox-Ruiz here on Envato Tuts+ to learn more about botttom sheets.
4. Full-Screen Dialog
According to the official Google material design documentation:
Full-screen dialogs group a series of tasks (such as creating a calendar entry) before they may be committed or discarded. No selections are saved until “Save” is touched. Touching the “X” discards all changes and exits the dialog.
Let's now see how to create a full-screen dialog. First, make sure you include the Android support v4 artifact in your app's module build.gradle
. This is required to support Android 4.0 (API level 14) and above.
implementation 'com.android.support:support-v4:26.1.0'
Next, we will create a FullscreenDialogFragment
that extends the DialogFragment
super class.
public class FullscreenDialogFragment extends DialogFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.full_screen_dialog, container, false); (rootView.findViewById(R.id.button_close)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); return rootView; } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = super.onCreateDialog(savedInstanceState); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); return dialog; } }
Here we override the onCreateView()
(just as we would do with an ordinary Fragment
). Inside this method, we simply inflate and return the layout (R.layout.full_screen_dialog
) that will serve as the custom view for the dialog. We set an OnClickListener
on the ImageButton
(R.id.button_close
) which will dismiss the dialog when clicked.
We also override onCreateDialog()
and return a Dialog
. Inside this method, you can also return an AlertDialog
created using an AlertDialog.Builder
.
Our R.layout.full_screen_dialog
consists of an ImageButton
, a Button
, and some TextView
labels:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" android:fitsSystemWindows="true" android:orientation="vertical"><android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"><LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"><ImageButton android:id="@+id/button_close" android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" android:background="?attr/selectableItemBackgroundBorderless" android:tint="@android:color/white" app:srcCompat="@drawable/ic_close" /><TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:text="Dialog title" android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" android:textColor="@android:color/white" /><Button android:id="@+id/button_action" style="@style/Widget.AppCompat.Button.Borderless" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:paddingLeft="2dp" android:paddingRight="2dp" android:text="Action" android:textColor="@android:color/white" /></LinearLayout></android.support.design.widget.AppBarLayout><android.support.v4.widget.NestedScrollView android:id="@+id/nested_scroll_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none" android:scrollingCache="true"><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|center_vertical" android:text="Full Screen Dialog"/></android.support.v4.widget.NestedScrollView></LinearLayout>
In the ImageButton
widget, you will see an attribute app:srcCompat
which references a custom VectorDrawable
(@drawable/ic_close
). This custom VectorDrawable
creates the X button, which closes the full-screen dialog when tapped.
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"><path android:fillColor="#FFFFFFFF" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/></vector>
In order to use this app:srcCompat
attribute, make sure you include it in your build.gradle file. Next, configure your app to use vector support libraries and add the vectorDrawables
element to your build.gradle
file in the app module.
android { defaultConfig { vectorDrawables.useSupportLibrary = true }
We did this so that we can support all Android platform versions back to Android 2.1 (API level 7+).
Finally, to show the FullscreenDialogFragment
, we simply use the FragmentTransaction
to add our fragment to the UI.
FragmentManager fragmentManager = getSupportFragmentManager(); FullscreenDialogFragment newFragment = new FullscreenDialogFragment(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.add(android.R.id.content, newFragment).addToBackStack(null).commit();
5. Surviving Device Orientation
Note that all the dialogs discussed here, except the full-screen dialog, will be dismissed automatically when the user changes the screen orientation of the Android device—from portrait to landscape (or vice versa). This is because the Android system has destroyed and recreated the Activity
so as to fit the new orientation.
For us to sustain the dialog across screen orientation changes, we'll have to create a Fragment
that extends the DialogFragment
super class (just as we did for the full-screen dialog example).
Let's see a simple example for an alert dialog.
public class AlertDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); return (builder.setTitle("Activate superpowers?").setMessage("By activating superpowers, you'll have more powers to save the world.") .setPositiveButton("Activate", this) .setNegativeButton("Cancel", this) .create()); } @Override public void onCancel(DialogInterface dialog) { super.onCancel(dialog); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); } @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(getActivity(), "which is " + which, Toast.LENGTH_LONG).show(); } }
Here, we created a class that extends the DialogFragment
and also implements the DialogInterface.OnClickListener
. Because we implemented this listener, we have to override the onClick()
method. Note that if we tap the positive or negative button, this onClick()
method will be invoked.
Inside our onCreateDialog()
, we create and return an instance of AlertDialog
.
We've also overridden:
onCancel()
: this is called if the user presses the BACK button to exit the dialog.onDismiss()
: this is called whenever the dialog is forced out for any reason (BACK or a button click).
To show this dialog, we simply call the show()
method on an instance of our AlertDialogFragment
.
new AlertDialogFragment().show(getSupportFragmentManager(), "alertdialog_sample");
The first parameter is an instance of the FragmentManager
. The second parameter is a tag that can be used to retrieve this fragment again later from the FragmentManager
via findFragmentByTag()
.
Now, if you change the device orientation from portrait to landscape (or vice versa), the alert dialog won't be dismissed.
You can follow similar steps for the other dialog types to maintain the dialog during device rotation. You simply create a Fragment
that extends the DialogFragment
super class, and you create and return the particular dialog in onCreateDialog()
.
Progress Dialog (deprecated)
Some of you may have heard about ProgressDialog
. This simply shows a dialog with a progress indicator on it. I didn't include it here, because ProgressDialog
has been deprecated in API level 26—because it can lead to a bad user experience for your users. According to the official documentation:
ProgressDialog
is a modal dialog, which prevents the user from interacting with the app. Instead of using this class, you should use a progress indicator likeProgressBar
, which can be embedded in your app's UI. Alternatively, you can use a notification to inform the user of the task's progress.
Conclusion
In this tutorial, you learned the different ways of showing material design dialogs in an Android app. We covered the following material design dialog types:
- alerts
- single and multiple choice dialogs
- time and date pickers
- bottom sheet dialog
- full-screen dialog
You also learned how to create a custom style for a dialog and make your dialog survive orientation configuration changes between landscape and portrait using DialogFragment
.
It's highly recommended you check out the official material design guidelines for dialogs to learn how to properly design and use dialogs in Android.
To learn more about coding for Android, check out some of our other courses and tutorials here on Envato Tuts+!
- Android SDKHow to Create an Android Chat App Using Firebase
- Android LollipopGetting Started With RecyclerView and CardView on Android
- Android SDKHow to Pass Data Between Activities With Android Parcelable
- Android SDKJava vs. Kotlin: Should You Be Using Kotlin for Android Development?