In this video from my course on building a material design app, you'll learn how to create the user interface of a material design app. You’ll learn how to use FABs (FloatingActionButtons), input widgets with floating labels, action bar menu items, and more.
The tutorial builds on the basic setup work done earlier in the course, but you should be able to follow along, and you can always check the source code on GitHub.
How to Create FloatingActionButtons and TextInputLayouts
Create Two Activities
Start by creating two activities: one for the home screen (shown on the left in the screenshot below) and one for the other two screens shown, which are identical except for their titles.
Home Screen
Let's start by creating the activity for the home screen: right-click on your package name and select New > Activity > Blank Activity. You'll see the following screen:
As you can see, this activity template already has a floating action button in it. Change the title of the activity to shopping list and pick the Launcher Activity field to let Android Studio know that this is going to be the home screen. Press Finish to generate the activity. Android Studio tries to follow the material design guidelines closely.
If you open activity_main.xml, you will see the code for the floating action button. Change the value of the source property to @drawable/ic_add_24dp because we want this button to display the plus symbol. Change the tint property to add @android:color/white to change the color of the plus symbol to white.
Add and Edit Item Screens
The floating action button is now ready. Let us now move on to creating the activity for the add and edit item screens. Right-click on the package name again and select New > Activity > Empty Activity.
I'm going to call this ItemActivity and press Finish. This layout is going to have two text input widgets, one below the other. Therefore, it is easier to use a linear layout instead of a relative layout. Set its orientation to vertical.
Inside the layout, create a tag for a text input layout. Set its width to match_parent and set its height to wrap_content. Inside the tag, add an edit text tag and set its width to match_parent and height to wrap_content. Call it input_item_name. Next, set its hint attribute to Item name. The value you specify for the hint is going to be rendered as an animated floating label.
Now we have to repeat the same steps for the second input widget. So you can simply copy and paste all of this code. Change the id to input_item_quantity and change the hint to Quantity.
Add a Save Button
The layout of the activity is almost complete. What's missing is a save button. To add a button inside the action bar, we need to add it as a menu item.
Right-click on the Resources folder and select a New Android resource file. Change the Resource type to Menu and name the file menu_item_activity. Add the application namespace to the root element by simply typing in appNs and pressing Enter.
Next, create a new item tag to represent the save button. Set its id to save_action and set the title to Save. And now add an attribute called app:showAsAction, and set its value to always. This attribute is important because if you forget to add it, the save button is going to end up inside the overflow menu of the action bar.
We should now inflate the menu file inside ItemActivity. To do so, open ItemActivity.java and override its onCreate options menu method.
Next, override the onOptionsItemSelected method so that we are notified when the user presses the button. Inside it, add an if statement and use getItemId to check if the id of the selected option matches the id of the save button, which is R.id.save_action.
Code to Open ItemActivity
Let us now write code to open ItemActivity when the user presses the floating action button. So open MainActivity.java. The floating action button already has an onClick event listener attached to it. It contains some placeholder code which you can delete. We will now use an Intent to launch ItemActivity.
Initialize the Intent using MainActivity as the context and ItemActivity as the class. When ItemActivity opens this way, its TITLE should be Add item. We can use an extra for this purpose. And finally call startActivity to actually launch ItemActivity.
Open ItemActivity.java now because we need to use the extra we send. So add a condition here to check if the return value of the getIntent method has an extra called TITLE. If the condition is true, use setTitle to change the title. Note that you must use the getStringExtra method to fetch the value of the extra.
Add a "Back" Icon
Let us now add this "back" icon to our activity.
To do that, open AndroidManifest.xml. Here, inside the tag for ItemActivity, add a parentActivityName attribute and set its value to MainActivity.
Run the App
We can now run our app to see what we have just created.
Our home screen now has a floating action button. If you click on it, you will be taken to ItemActivity.
And if you try typing something into the input widgets, you should be able to see their floating labels.
Conclusion
You now know how to create user interfaces which have floating action buttons and text input layouts. Now, before we finish up, let me give you a quick tip. If you don't like the default colors of the app, which are shades of indigo and pink, you can easily change them by simply going to colors.xml and changing the hex values for the colors here.
In the next lesson of the course, you are going to learn how to use another material design compliant UI widget called a recycler view.
Watch the Full Course
Google's material design has quickly become a popular and widely implemented design language. Many Android users now expect their apps to conform to the material design spec, and app designers will expect you to be able to implement its basic principles.
In the full course, Build a Material Design App, I'll show you how to build a practical, fully functional material design app that is ready to publish on Google Play. Starting from the app design created by instructor Adi Purdila in his course Getting to Know Material Design, you will learn how to work with the various material design UI widgets available in the Android Support library. You will also learn how to perform read and write operations on a modern mobile database called Realm.
During this year's WWDC, Apple introduced a number of significant improvements to Core Data, taking the framework to the next level. In this article, I'll zoom in on:
Persistent Containers
Xcode 8 Support and Swift 3
Query Generations
Concurrency Improvements
1. Persistent Containers
NSPersistentContainer
Setting up the Core Data stack has always been a bit of a pain. Because it is the first concept developers new to the framework need to become familiar with, the learning curve has always been fairly steep. This is no longer true.
The team at Apple introduced a brand new member to the framework, which makes setting up and managing the Core Data stack of your application a breeze. This new member is the NSPersistentContainer class.
Setting up the Core Data stack is trivial with the NSPersistentContainer class. You can initialize an instance by invoking the init(name:) initializer. The name you pass to the initializer is used by the persistent container to find the data model in the application bundle, and it is also used to name the persistent store of the application.
let persistentContainer = NSPersistentContainer(name: "MyApplication")
The persistent container creates a persistent store coordinator, a managed object model, and a managed object context. It exposes several properties and methods to interact with the Core Data stack.
You can access a managed object context that is associated with the main queue of the application through the viewContext property of the NSPersistentContainer instance. As the name implies, this managed object context is meant to be used for any operations that relate to the user interface of the application.
You can also access the persistent store coordinator and the managed object model through the persistentStoreCoordinator and managedObjectModel properties.
If you need to perform a Core Data operation in the background, you can ask the persistent container for a private managed object context by invoking the newBackgroundContext() factory method. For lightweight operations, you can invoke the performBackgroundTask(_:) method. This method accepts a closure in which you can perform the background operation using a private managed object context.
NSPersistentStoreDescription
Apple also introduced the NSPersistentStoreDescription class. It encapsulates the information to create and load a persistent store, such as the location of the persistent store, its configuration, and whether it is a read-only persistent store.
Adding a persistent store to a persistent store coordinator is much more elegant using the NSPersistentStoreDescription class. The NSPersistentStoreCoordinator class defines a new method, addPersistentStore(with:completionHandler:), which accepts an NSPersistentStoreDescription instance and a completion handler.
// Create Persistent Store Description
let persistentStoreDescription = NSPersistentStoreDescription(url: url)
// Add Persistent Store to Persistent Store Coordinator
persistentContainer.persistentStoreCoordinator.addPersistentStore(with: persistentStoreDescription, completionHandler: { (persistentStoreDescription, error) in
if let error = error {
print("Unable to Add Persistent Store")
print("\(error), \(error.localizedDescription)")
} else {
// Successfully Added Persistent Store
}
}
Xcode 8 and Swift 3
Xcode 8
Xcode 8 also includes a number of changes that dramatically improve support for Core Data. Xcode now automatically generates NSManagedObject subclasses for every entity of the data model. And instead of adding the files for the subclasses to the project, cluttering up the project, they are added to the Derived Data folder. This means that Xcode can make sure they are updated when the data model changes, and you the developer don't need to worry about the files.
How does it work? If you open the data model of your project in Xcode 8 and select an entity, you see a new option in the Data Model Inspector on the right. The Codegen field in the Class section is what interests us.
As of Xcode 8.1, the value of this field is automatically set to Class Definition. This means that Xcode generates an NSManagedObject subclass for the selected entity. If you make any changes to the entity, the generated class is automatically updated for you. You don't need to worry about anything.
You can disable code generation, or you can instruct Xcode to only generate an extension (Swift) or category (Objective-C) for the class. If you choose the latter option, then you are in charge of creating an NSManagedObject subclass for the entity.
Simplifying Core Data
The Core Data framework also defines a new protocol, NSFetchedResultsType, which makes working with the framework much easier and more elegant in Swift. This is what it used to take to create a new managed object for the Category entity.
if let entity = NSEntityDescription.entity(forEntityName: "Category", in: managedObjectContext) {
let category = Category(entity: entity, insertInto: managedObjectContext)
}
As of iOS 10 and macOS 10.12, every NSManagedObject subclass knows what entity it belongs to.
category.entity
This means we only need to specify in which managed object context the managed object needs to be created.
let category = Category(context: managedObjectContext)
I'm sure you agree that this looks much more elegant and concise. Creating fetch requests is also much easier. Take a look at the example below.
let fetchRequest = Category.fetchRequest()
What I am showing you is only a subset of the improvements available in iOS 10, tvOS 10, macOS 10.12, and watchOS 3. There is much more for you to explore.
Query Generations
The most important announcement is probably the introduction of query generations. Query generations solve a problem that has been plaguing Core Data ever since it was released more than ten years ago.
What Problem Does It Solve?
Faulting is an essential aspect of the framework. It ensures Core Data performs well, and it also keeps the framework's memory footprint low. If you want to learn more about faulting, then I recommend another article I wrote last year.
Even though faulting works great and is essential for the framework, it can go wrong when a fault can no longer be fulfilled. This scenario can be avoided by deleting inaccessible faults—that is, faults that can no longer be fulfilled. But this is merely a workaround and can lead to unexpected behavior in your application.
Alternatively, the application can aggressively fetch every piece of data it needs from the persistent store, bypassing faulting. This results in decreased performance and a larger memory footprint. In other words, you disable one of the key features of the framework.
Query generations provide another solution that tackles the root of the problem. The idea is simple. The execution is a bit more complicated.
What Is It?
As of iOS 10 and macOS 10.12, it is possible to assign a query generation to a managed object context. This means that the managed object context interacts with a snapshot of the data stored in the persistent store. Even if other managed object contexts make changes (inserts, deletes, and updates), the snapshot of the managed object context remains unchanged.
The upside is that data cannot change from underneath the managed object context. In other words, the managed object context works with a specific generation of data, hence the name query generations.
How Does It Work?
A managed object context has three options to work with query generations. The first option doesn't change anything. The managed object context doesn't pin itself to a query generation. If the data of the persistent store changes, the managed object context needs to deal with that. This is the behavior you are used to, which can lead to inaccessible faults.
Alternatively, the managed object context can pin itself to a query generation the moment it loads data from the persistent store. As a third option, the managed object context can pin itself to a specific query generation.
Even though query generations are a more advanced concept of the Core Data framework, they solve a problem anyone working with Core Data faces at some point. The more complex your application, the more serious the problem is. It is great to see that the framework now provides a solution to this problem.
Concurrency Improvements
As I mentioned earlier, the improvements made to the Core Data framework have been nothing short of amazing. The improvements range from new classes that make working with the framework easier to resolving performance issues at the level of the persistent store coordinator.
Another improvement made to the framework relates to concurrency. Even though the persistent store coordinator should not be used on multiple threads, it knows how to lock itself when different managed object contexts access the persistent store coordinator from different threads. If the persistent store coordinator is locked when it is accessed by a managed object context, other managed object contexts need to wait for the lock to be removed before they can access the persistent store coordinator. This can cause performance problems, which can trickle up to the user interface, resulting in decreased performance.
As of iOS 10 and macOS 10.12, the persistent store coordinator no longer takes a lock when a managed object context pushes changes to the persistent store coordinator. Instead, the persistent store sends the requests of the persistent store coordinator immediately to the SQL store itself. Instead, the lock is taken at the level of the SQL store.
There are several important benefits to this approach. It is no longer necessary to use multiple Core Data stacks to avoid performance issues. This means that the architecture of your application can be simplified dramatically, even for large and complex applications.
Because this solution is implemented at the level of the SQL store, it is only available if your application uses a SQLite store as its persistent store. If your application uses multiple persistent stores, then each of these stores needs to be a SQLite store. This feature is enabled by default.
Conclusion
Core Data continues to improve year over year. It is fantastic to see that Apple is committed to making its persistence solution better with every iteration of its platforms. Even though there are alternative solutions available, such as Realm, my preferred choice continues to be Core Data.
To learn more about Core Data, check out my Core Data and Swift series here on Envato Tuts+.
During this year's WWDC, Apple introduced a number of significant improvements to Core Data, taking the framework to the next level. In this article, I'll zoom in on:
Persistent Containers
Xcode 8 Support and Swift 3
Query Generations
Concurrency Improvements
1. Persistent Containers
NSPersistentContainer
Setting up the Core Data stack has always been a bit of a pain. Because it is the first concept developers new to the framework need to become familiar with, the learning curve has always been fairly steep. This is no longer true.
The team at Apple introduced a brand new member to the framework, which makes setting up and managing the Core Data stack of your application a breeze. This new member is the NSPersistentContainer class.
Setting up the Core Data stack is trivial with the NSPersistentContainer class. You can initialize an instance by invoking the init(name:) initializer. The name you pass to the initializer is used by the persistent container to find the data model in the application bundle, and it is also used to name the persistent store of the application.
let persistentContainer = NSPersistentContainer(name: "MyApplication")
The persistent container creates a persistent store coordinator, a managed object model, and a managed object context. It exposes several properties and methods to interact with the Core Data stack.
You can access a managed object context that is associated with the main queue of the application through the viewContext property of the NSPersistentContainer instance. As the name implies, this managed object context is meant to be used for any operations that relate to the user interface of the application.
You can also access the persistent store coordinator and the managed object model through the persistentStoreCoordinator and managedObjectModel properties.
If you need to perform a Core Data operation in the background, you can ask the persistent container for a private managed object context by invoking the newBackgroundContext() factory method. For lightweight operations, you can invoke the performBackgroundTask(_:) method. This method accepts a closure in which you can perform the background operation using a private managed object context.
NSPersistentStoreDescription
Apple also introduced the NSPersistentStoreDescription class. It encapsulates the information to create and load a persistent store, such as the location of the persistent store, its configuration, and whether it is a read-only persistent store.
Adding a persistent store to a persistent store coordinator is much more elegant using the NSPersistentStoreDescription class. The NSPersistentStoreCoordinator class defines a new method, addPersistentStore(with:completionHandler:), which accepts an NSPersistentStoreDescription instance and a completion handler.
// Create Persistent Store Description
let persistentStoreDescription = NSPersistentStoreDescription(url: url)
// Add Persistent Store to Persistent Store Coordinator
persistentContainer.persistentStoreCoordinator.addPersistentStore(with: persistentStoreDescription, completionHandler: { (persistentStoreDescription, error) in
if let error = error {
print("Unable to Add Persistent Store")
print("\(error), \(error.localizedDescription)")
} else {
// Successfully Added Persistent Store
}
}
Xcode 8 and Swift 3
Xcode 8
Xcode 8 also includes a number of changes that dramatically improve support for Core Data. Xcode now automatically generates NSManagedObject subclasses for every entity of the data model. And instead of adding the files for the subclasses to the project, cluttering up the project, they are added to the Derived Data folder. This means that Xcode can make sure they are updated when the data model changes, and you the developer don't need to worry about the files.
How does it work? If you open the data model of your project in Xcode 8 and select an entity, you see a new option in the Data Model Inspector on the right. The Codegen field in the Class section is what interests us.
As of Xcode 8.1, the value of this field is automatically set to Class Definition. This means that Xcode generates an NSManagedObject subclass for the selected entity. If you make any changes to the entity, the generated class is automatically updated for you. You don't need to worry about anything.
You can disable code generation, or you can instruct Xcode to only generate an extension (Swift) or category (Objective-C) for the class. If you choose the latter option, then you are in charge of creating an NSManagedObject subclass for the entity.
Simplifying Core Data
The Core Data framework also defines a new protocol, NSFetchedResultsType, which makes working with the framework much easier and more elegant in Swift. This is what it used to take to create a new managed object for the Category entity.
if let entity = NSEntityDescription.entity(forEntityName: "Category", in: managedObjectContext) {
let category = Category(entity: entity, insertInto: managedObjectContext)
}
As of iOS 10 and macOS 10.12, every NSManagedObject subclass knows what entity it belongs to.
category.entity
This means we only need to specify in which managed object context the managed object needs to be created.
let category = Category(context: managedObjectContext)
I'm sure you agree that this looks much more elegant and concise. Creating fetch requests is also much easier. Take a look at the example below.
let fetchRequest = Category.fetchRequest()
What I am showing you is only a subset of the improvements available in iOS 10, tvOS 10, macOS 10.12, and watchOS 3. There is much more for you to explore.
Query Generations
The most important announcement is probably the introduction of query generations. Query generations solve a problem that has been plaguing Core Data ever since it was released more than ten years ago.
What Problem Does It Solve?
Faulting is an essential aspect of the framework. It ensures Core Data performs well, and it also keeps the framework's memory footprint low. If you want to learn more about faulting, then I recommend another article I wrote last year.
Even though faulting works great and is essential for the framework, it can go wrong when a fault can no longer be fulfilled. This scenario can be avoided by deleting inaccessible faults—that is, faults that can no longer be fulfilled. But this is merely a workaround and can lead to unexpected behavior in your application.
Alternatively, the application can aggressively fetch every piece of data it needs from the persistent store, bypassing faulting. This results in decreased performance and a larger memory footprint. In other words, you disable one of the key features of the framework.
Query generations provide another solution that tackles the root of the problem. The idea is simple. The execution is a bit more complicated.
What Is It?
As of iOS 10 and macOS 10.12, it is possible to assign a query generation to a managed object context. This means that the managed object context interacts with a snapshot of the data stored in the persistent store. Even if other managed object contexts make changes (inserts, deletes, and updates), the snapshot of the managed object context remains unchanged.
The upside is that data cannot change from underneath the managed object context. In other words, the managed object context works with a specific generation of data, hence the name query generations.
How Does It Work?
A managed object context has three options to work with query generations. The first option doesn't change anything. The managed object context doesn't pin itself to a query generation. If the data of the persistent store changes, the managed object context needs to deal with that. This is the behavior you are used to, which can lead to inaccessible faults.
Alternatively, the managed object context can pin itself to a query generation the moment it loads data from the persistent store. As a third option, the managed object context can pin itself to a specific query generation.
Even though query generations are a more advanced concept of the Core Data framework, they solve a problem anyone working with Core Data faces at some point. The more complex your application, the more serious the problem is. It is great to see that the framework now provides a solution to this problem.
Concurrency Improvements
As I mentioned earlier, the improvements made to the Core Data framework have been nothing short of amazing. The improvements range from new classes that make working with the framework easier to resolving performance issues at the level of the persistent store coordinator.
Another improvement made to the framework relates to concurrency. Even though the persistent store coordinator should not be used on multiple threads, it knows how to lock itself when different managed object contexts access the persistent store coordinator from different threads. If the persistent store coordinator is locked when it is accessed by a managed object context, other managed object contexts need to wait for the lock to be removed before they can access the persistent store coordinator. This can cause performance problems, which can trickle up to the user interface, resulting in decreased performance.
As of iOS 10 and macOS 10.12, the persistent store coordinator no longer takes a lock when a managed object context pushes changes to the persistent store coordinator. Instead, the persistent store sends the requests of the persistent store coordinator immediately to the SQL store itself. Instead, the lock is taken at the level of the SQL store.
There are several important benefits to this approach. It is no longer necessary to use multiple Core Data stacks to avoid performance issues. This means that the architecture of your application can be simplified dramatically, even for large and complex applications.
Because this solution is implemented at the level of the SQL store, it is only available if your application uses a SQLite store as its persistent store. If your application uses multiple persistent stores, then each of these stores needs to be a SQLite store. This feature is enabled by default.
Conclusion
Core Data continues to improve year over year. It is fantastic to see that Apple is committed to making its persistence solution better with every iteration of its platforms. Even though there are alternative solutions available, such as Realm, my preferred choice continues to be Core Data.
To learn more about Core Data, check out my Core Data and Swift series here on Envato Tuts+.
In this series, you'll learn how to use React Native to create page layouts commonly used in mobile apps. The layouts you'll be creating won't be functional—instead, the main focus of this series is to get your hands dirty in laying out content in your React Native apps.
If you're new to laying out React Native apps or styling in general, check out my previous tutorial:
To follow along with this series, I challenge you to try recreating each screen by yourself first, before you read my step-by-step instructions in the tutorial. You won't really benefit much from this tutorial just by reading it! Try first before looking up the answers here. If you succeed in making it look like the original screen, compare your implementation to mine. Then decide for yourself which one is better!
In this first part of the series, you'll create the following login page:
The login page is commonly used as the initial page for apps that require users to have an account.
Here are a couple of examples of this type of layout in the wild:
Project Setup
The first step, of course, is to set up a new React Native project:
react-native init react-native-common-screens
Once the project is set up, open the index.android.js file and replace the default code with the following:
import React, { Component } from 'react';
import {
AppRegistry
} from 'react-native';
import Login from './src/pages/Login';
export default class ReactNativeCommonScreens extends Component {
render() {
return (
<Login />
);
}
}
AppRegistry.registerComponent('ReactNativeCommonScreens', () => ReactNativeCommonScreens);
Create a src/pages folder and create a Login.js file inside it.
You'll also need the react-native-vector-icons package. This is specifically used to render the Facebook icon for the Facebook login button.
npm install --save react-native-vector-icons
Open the android/app/build.gradle file and add a reference to the package:
dependencies {
//rest of the dependencies are here at the top
compile project(':react-native-vector-icons') //add this
}
Do the same with the android/settings.gradle file by adding the following at the bottom:
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
Open android/app/src/main/java/com/react-native-common-screens/MainApplication.java and import the package:
import java.util.Arrays;
import java.util.List;
import com.oblador.vectoricons.VectorIconsPackage; //add this
Lastly, initialize the package:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VectorIconsPackage() //add this
);
}
Creating the Login Page
Okay, now that you've tried to code the layout yourself (no cheating, right?), I'll show you how I built my implementation. Open the src/pages/Login.js file and import the things that you'll need:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
ScrollView
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
Aside from the default React components and the react-native-vector-icons package, you'll also need to include three custom components:
import Container from '../components/Container';
import Button from '../components/Button';
import Label from '../components/Label';
The first one is the Container (src/components/Container.js), whose job is to add a bottom margin to whatever it wraps. It sounds trivial, and yes, you can actually just use View and add the same styles every time you want to use it. The advantage this offers is that you don't have to apply the same styles to a View multiple times, and it also allows you to reuse the component every time you need to add a bottom margin to something.
The Button component (src/components/Button.js), as the name suggests, is used to create buttons. If present, this spits out any child component that's added inside it. Otherwise, it outputs a Text component which shows the text inside the button. A default style is also added, but it won't be used if noDefaultStyles is present in the props. Unique button styles passed from the props are also optional.
Now you can move on to the actual login page. Inside the render() method, wrap everything in a ScrollView component. This is very important if you want your app to be able to cater to all sorts of device dimensions and screen orientations.
More often than not, no matter how little height you think your content is going to consume, there will always be a device that won't be able to display it completely. Thus the need for scroll bars for scrolling the content.
Apply the following styles to the ScrollView. flexDirection is optional, but it's a good practice to explicitly define it so that future developers will know exactly how the main content of the app is laid out just by glancing at the code.
Looking at the screenshot from earlier, the first piece of content that you want to add is the one at the very top, and that is the forgot password button. Note that the onPress props is supplied because the underlayColor won't actually be applied if there is no function supplied for when the button is pressed.
The styles used for it are pretty self-explanatory except for the alignSelf: 'flex-end'. This tells React Native to position the element at the very end of the current line. alignSelf is the equivalent of alignItems for specifying the alignment of the element itself and not its children. Using flex-end allows you to achieve an effect similar to that of float: right in CSS.
Now you may have started to notice why the Container component is important. It allows you to encapsulate default styles so you won't end up re-declaring them in every file. This is a basic principle of React: you should always strive for component reuse every time you see an opportunity.
The button for logging in to Facebook is a bit different from the buttons that you created earlier. This time it has some content inside it which displays an icon along with some text. These are added in place of the label props in order to customize the content of the button further.
There's really nothing worth noting here except for styles.inline, which is used as a helper class to horizontally stack all the elements inside of it. This achieves a similar effect to that of using <span> in HTML to wrap text that you want to display inline. In CSS, this can be achieved using either display: inline or display: inline-block.
The last elements on this screen are the Sign In and Cancel buttons. They need more space above them than the other elements, so it's best to wrap them in a container (footer) and apply marginTop to it. This makes more sense than declaring a new style just for these buttons.
Finally, don't forget to define the code that will be executed when any of the buttons are pressed!
press() {
//execute any code here
}
Conclusion
That's it! In this tutorial you've successfully created a login page using your Flexbox knowledge. Along the way, you also learned how to use a third-party library called React Native Vector Icons to easily add icons in your app. How did my solution compare to your own? Let us know in the discussion forum below.
In the next tutorial in this series, you'll learn how to create a calendar screen. In the meantime, check out some of our other tutorials on React Native and Flexbox.
CSS, despite its relatively low perceived skill ceiling, always seems to have a killer feature up its sleeve. Remember how media queries made responsive...
React Native, created by Facebook, lets you write native mobile apps in modern JavaScript. React Native apps will be transformed into native views specific...
Are you looking to improve your flexbox knowledge and at the same time learn how to build easily an attractive and unique layout? If so, be sure to read this...
Animations can breath life into your app and make your UIs more intuitive. In this tutorial, you'll learn how to implement different kinds of animations in...
Are you a hybrid app developer wanting to include face detection into your app, but you don't have any idea where to start? As a start, you could read An...
In this series, you'll learn how to use React Native to create page layouts commonly used in mobile apps. The layouts you'll be creating won't be functional—instead, the main focus of this series is to get your hands dirty in laying out content in your React Native apps.
If you're new to laying out React Native apps or styling in general, check out my previous tutorial:
To follow along with this series, I challenge you to try recreating each screen by yourself first, before you read my step-by-step instructions in the tutorial. You won't really benefit much from this tutorial just by reading it! Try first before looking up the answers here. If you succeed in making it look like the original screen, compare your implementation to mine. Then decide for yourself which one is better!
In this first part of the series, you'll create the following login page:
The login page is commonly used as the initial page for apps that require users to have an account.
Here are a couple of examples of this type of layout in the wild:
Project Setup
The first step, of course, is to set up a new React Native project:
react-native init react-native-common-screens
Once the project is set up, open the index.android.js file and replace the default code with the following:
import React, { Component } from 'react';
import {
AppRegistry
} from 'react-native';
import Login from './src/pages/Login';
export default class ReactNativeCommonScreens extends Component {
render() {
return (
<Login />
);
}
}
AppRegistry.registerComponent('ReactNativeCommonScreens', () => ReactNativeCommonScreens);
Create a src/pages folder and create a Login.js file inside it.
You'll also need the react-native-vector-icons package. This is specifically used to render the Facebook icon for the Facebook login button.
npm install --save react-native-vector-icons
Open the android/app/build.gradle file and add a reference to the package:
dependencies {
//rest of the dependencies are here at the top
compile project(':react-native-vector-icons') //add this
}
Do the same with the android/settings.gradle file by adding the following at the bottom:
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
Open android/app/src/main/java/com/react-native-common-screens/MainApplication.java and import the package:
import java.util.Arrays;
import java.util.List;
import com.oblador.vectoricons.VectorIconsPackage; //add this
Lastly, initialize the package:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VectorIconsPackage() //add this
);
}
Creating the Login Page
Okay, now that you've tried to code the layout yourself (no cheating, right?), I'll show you how I built my implementation. Open the src/pages/Login.js file and import the things that you'll need:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
ScrollView
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
Aside from the default React components and the react-native-vector-icons package, you'll also need to include three custom components:
import Container from '../components/Container';
import Button from '../components/Button';
import Label from '../components/Label';
The first one is the Container (src/components/Container.js), whose job is to add a bottom margin to whatever it wraps. It sounds trivial, and yes, you can actually just use View and add the same styles every time you want to use it. The advantage this offers is that you don't have to apply the same styles to a View multiple times, and it also allows you to reuse the component every time you need to add a bottom margin to something.
The Button component (src/components/Button.js), as the name suggests, is used to create buttons. If present, this spits out any child component that's added inside it. Otherwise, it outputs a Text component which shows the text inside the button. A default style is also added, but it won't be used if noDefaultStyles is present in the props. Unique button styles passed from the props are also optional.
Now you can move on to the actual login page. Inside the render() method, wrap everything in a ScrollView component. This is very important if you want your app to be able to cater to all sorts of device dimensions and screen orientations.
More often than not, no matter how little height you think your content is going to consume, there will always be a device that won't be able to display it completely. Thus the need for scroll bars for scrolling the content.
Apply the following styles to the ScrollView. flexDirection is optional, but it's a good practice to explicitly define it so that future developers will know exactly how the main content of the app is laid out just by glancing at the code.
Looking at the screenshot from earlier, the first piece of content that you want to add is the one at the very top, and that is the forgot password button. Note that the onPress props is supplied because the underlayColor won't actually be applied if there is no function supplied for when the button is pressed.
The styles used for it are pretty self-explanatory except for the alignSelf: 'flex-end'. This tells React Native to position the element at the very end of the current line. alignSelf is the equivalent of alignItems for specifying the alignment of the element itself and not its children. Using flex-end allows you to achieve an effect similar to that of float: right in CSS.
Now you may have started to notice why the Container component is important. It allows you to encapsulate default styles so you won't end up re-declaring them in every file. This is a basic principle of React: you should always strive for component reuse every time you see an opportunity.
The button for logging in to Facebook is a bit different from the buttons that you created earlier. This time it has some content inside it which displays an icon along with some text. These are added in place of the label props in order to customize the content of the button further.
There's really nothing worth noting here except for styles.inline, which is used as a helper class to horizontally stack all the elements inside of it. This achieves a similar effect to that of using <span> in HTML to wrap text that you want to display inline. In CSS, this can be achieved using either display: inline or display: inline-block.
The last elements on this screen are the Sign In and Cancel buttons. They need more space above them than the other elements, so it's best to wrap them in a container (footer) and apply marginTop to it. This makes more sense than declaring a new style just for these buttons.
Finally, don't forget to define the code that will be executed when any of the buttons are pressed!
press() {
//execute any code here
}
Conclusion
That's it! In this tutorial you've successfully created a login page using your Flexbox knowledge. Along the way, you also learned how to use a third-party library called React Native Vector Icons to easily add icons in your app. How did my solution compare to your own? Let us know in the discussion forum below.
In the next tutorial in this series, you'll learn how to create a calendar screen. In the meantime, check out some of our other tutorials on React Native and Flexbox.
CSS, despite its relatively low perceived skill ceiling, always seems to have a killer feature up its sleeve. Remember how media queries made responsive...
React Native, created by Facebook, lets you write native mobile apps in modern JavaScript. React Native apps will be transformed into native views specific...
Are you looking to improve your flexbox knowledge and at the same time learn how to build easily an attractive and unique layout? If so, be sure to read this...
Animations can breath life into your app and make your UIs more intuitive. In this tutorial, you'll learn how to implement different kinds of animations in...
Are you a hybrid app developer wanting to include face detection into your app, but you don't have any idea where to start? As a start, you could read An...
A typical Android app tends to be composed of many layers, modules or structures such as Fragments, Activities, Presenters, and Services. Effective communication between these components can become difficult if they are tightly coupled together.
In the lower level of your app architecture, such as the database, when an action happens, you might want to send data to a higher level such as the view. To do this, you might want to create a listener interface, async tasks or callbacks. All of these will work, but they have some major drawbacks:
direct or tight coupling
registering and unregistering multiple dependencies individually
repetition of code
difficulty in testing
increased risk of bugs
Using publish/subscribe or message bus architecture prevents all the potential problems highlighted above. It is a very good way to implement effective communications between components in an application without any of them needing to be aware of the others immediately. Using publish/subscribe in Android, any app component can publish events which it will hand over to the bus, and the relevant consumers can consume or subscribe to them.
To use greenrobot EventBus, you need to first add it to in the app module build.gradle file, include compile 'org.greenrobot:eventbus:3.0.0', and then sync your project afterwards.
An Event Subscriber
A subscriber simply subscribes to an event by registering in the event bus and can also unregister that event. To be a subscriber, you have to do three main things:
1. Register the subscriber in the event bus with register(). This informs the event bus that you want to begin receiving events. In an activity, this is in the onStart() method, while in a fragment put this in the onAttact(Activity activity) method.
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
2. Unregister the subscriber, which means tell the event bus to stop sending me events. In an activity, this is in the onStop() method, while in a fragment put this in the onDetach() method.
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3. Implement the onEvent() to indicate the type of event you want to receive and action to take when you receive the event. Notice the @Subscribe annotation at the top of this method. In this case, we want to subscribe to a normal event and not a sticky one—I'll explain the difference later.
@Subscribe
public void onEvent(MessageEvent event) {
Toast.makeText(this, "Hey, my message" + event.getMessage(), Toast.LENGTH_SHORT).show();.
}
Defining Event Messages
The events in greenrobot EventBus are just objects that you define. You can have different event classes if you want. They do not inherit any base class or interface—they're just POJO (Plain Old Java Objects).
public class MessageEvent {
public String mMessage;
public MessageEvent(String message) {
mMessage = message;
}
public String getMessage() {
return mMessage;
}
}
Post Event and Post Sticky Event
The main difference between post event and post sticky event is the caching mechanism employed inside the event bus. When someone posts a sticky event, this event is stored in a cache. When a new activity or fragment subscribes to the event bus, it gets the latest sticky event from the cache instead of waiting for it to be fired again to the event bus—so this event stays in the cache even after a subscriber has gotten it.
Sticky events are posted with the postSticky(MessageEvent) method, and non-sticky events with the post(MessageEvent) method.
For a regular, non-sticky event, if there no subscriber is found, the event will be thrown away. A sticky event will be cached, though, in case a subscriber comes along later.
So when do you decide to use post sticky event? You can do this if you are tracking down the user's location, or for simple caching of data, tracking battery levels, etc.
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.getMessage());
}
To subscribe to a sticky event, you include sticky = trueinside the @Subscribe annotation. This indicates that we want to receive a sticky event of type MessageEvent from the cache.
removeStickyEvent(Event) removes a sticky event from the cache, and removeAllStickyEvents() will remove all sticky events.
EventBus Thread Modes
There are four thread modes available for subscribers to choose from: posting, main, background, and async.
Posting
@Subscribe(threadMode = ThreadMode.POSTING)
This is the default. Subscribers will be called in the same thread as the thread where the event is posted. Including ThreadMode.POSTING in your @Subscribe annotation is optional.
Main
@Subscribe(threadMode = ThreadMode.MAIN)
In this thread mode, subscribers will receive events in the main UI thread, no matter where the event was posted. This is the thread mode to use if you want to update UI elements as a result of the event.
Background
@Subscribe(threadMode = ThreadMode.BACKGROUND)
In this thread mode, subscribers will receive events in the same thread where they are posted, just like for ThreadMode.POSTING. The difference is that if the event is posted in the main thread, then subscribers will instead get them on a background thread. This makes sure that event handling doesn't block the app's UI. Still, don't run an operation that will take a long time on this thread.
Async
@Subscribe(threadMode = ThreadMode.ASYNC)
In this thread mode, subscribers will always receive events independently from the current thread and main thread. This enables the subscribers to run on a separate thread. This is useful for long-running operations such as network operations.
Subscriber Priorities
If you want to change the order in which subscribers get events, then you need to specify their priority levels during registration. Subscribers with a higher priority get the event before subscribers with a lower priority. This only affects subscribers in the same thread mode. Note that the default priority is 0.
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
textField.setText(event.getMessage());
}
Cancelling Events
If you want to stop an event from being delivered to other subscribers, call the cancelEventDelivery(Object event) method inside the subscriber's event handling method.
@Subscribe
public void onEvent(MessageEvent event){
EventBus.getDefault().cancelEventDelivery(event);
}
Conclusion
In this tutorial, you learned about:
greenrobot EventBus and how it can improve your Android app
the difference between regular and sticky events
the different thread modes available and when to use each one
Another library you can use to implement an event bus is RxAndroid. Check out our article on RxAndroid here on Envato Tuts+, or try some of our other Android courses or tutorials.
The codebase of complex apps with many network connections and user interactions are often littered with callbacks. Such code is not only lengthy and hard to...
In this tutorial, you'll learn to use Loopj, an easy-to-use library for HTTP requests in Android. To help you learn, we're going to use Loopj to create...
In this tutorial, you'll learn how to create, send and receive both local and system-wide broadcasts. You'll also learn how to use a popular third-party...
Animations have become an important part of the Android user experience. Subtle, well-crafted animation is used throughout the Android OS and can make your...
A typical Android app tends to be composed of many layers, modules or structures such as Fragments, Activities, Presenters, and Services. Effective communication between these components can become difficult if they are tightly coupled together.
In the lower level of your app architecture, such as the database, when an action happens, you might want to send data to a higher level such as the view. To do this, you might want to create a listener interface, async tasks or callbacks. All of these will work, but they have some major drawbacks:
direct or tight coupling
registering and unregistering multiple dependencies individually
repetition of code
difficulty in testing
increased risk of bugs
Using publish/subscribe or message bus architecture prevents all the potential problems highlighted above. It is a very good way to implement effective communications between components in an application without any of them needing to be aware of the others immediately. Using publish/subscribe in Android, any app component can publish events which it will hand over to the bus, and the relevant consumers can consume or subscribe to them.
To use greenrobot EventBus, you need to first add it to in the app module build.gradle file, include compile 'org.greenrobot:eventbus:3.0.0', and then sync your project afterwards.
An Event Subscriber
A subscriber simply subscribes to an event by registering in the event bus and can also unregister that event. To be a subscriber, you have to do three main things:
1. Register the subscriber in the event bus with register(). This informs the event bus that you want to begin receiving events. In an activity, this is in the onStart() method, while in a fragment put this in the onAttact(Activity activity) method.
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
2. Unregister the subscriber, which means tell the event bus to stop sending me events. In an activity, this is in the onStop() method, while in a fragment put this in the onDetach() method.
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3. Implement the onEvent() to indicate the type of event you want to receive and action to take when you receive the event. Notice the @Subscribe annotation at the top of this method. In this case, we want to subscribe to a normal event and not a sticky one—I'll explain the difference later.
@Subscribe
public void onEvent(MessageEvent event) {
Toast.makeText(this, "Hey, my message" + event.getMessage(), Toast.LENGTH_SHORT).show();.
}
Defining Event Messages
The events in greenrobot EventBus are just objects that you define. You can have different event classes if you want. They do not inherit any base class or interface—they're just POJO (Plain Old Java Objects).
public class MessageEvent {
public String mMessage;
public MessageEvent(String message) {
mMessage = message;
}
public String getMessage() {
return mMessage;
}
}
Post Event and Post Sticky Event
The main difference between post event and post sticky event is the caching mechanism employed inside the event bus. When someone posts a sticky event, this event is stored in a cache. When a new activity or fragment subscribes to the event bus, it gets the latest sticky event from the cache instead of waiting for it to be fired again to the event bus—so this event stays in the cache even after a subscriber has gotten it.
Sticky events are posted with the postSticky(MessageEvent) method, and non-sticky events with the post(MessageEvent) method.
For a regular, non-sticky event, if there no subscriber is found, the event will be thrown away. A sticky event will be cached, though, in case a subscriber comes along later.
So when do you decide to use post sticky event? You can do this if you are tracking down the user's location, or for simple caching of data, tracking battery levels, etc.
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.getMessage());
}
To subscribe to a sticky event, you include sticky = trueinside the @Subscribe annotation. This indicates that we want to receive a sticky event of type MessageEvent from the cache.
removeStickyEvent(Event) removes a sticky event from the cache, and removeAllStickyEvents() will remove all sticky events.
EventBus Thread Modes
There are four thread modes available for subscribers to choose from: posting, main, background, and async.
Posting
@Subscribe(threadMode = ThreadMode.POSTING)
This is the default. Subscribers will be called in the same thread as the thread where the event is posted. Including ThreadMode.POSTING in your @Subscribe annotation is optional.
Main
@Subscribe(threadMode = ThreadMode.MAIN)
In this thread mode, subscribers will receive events in the main UI thread, no matter where the event was posted. This is the thread mode to use if you want to update UI elements as a result of the event.
Background
@Subscribe(threadMode = ThreadMode.BACKGROUND)
In this thread mode, subscribers will receive events in the same thread where they are posted, just like for ThreadMode.POSTING. The difference is that if the event is posted in the main thread, then subscribers will instead get them on a background thread. This makes sure that event handling doesn't block the app's UI. Still, don't run an operation that will take a long time on this thread.
Async
@Subscribe(threadMode = ThreadMode.ASYNC)
In this thread mode, subscribers will always receive events independently from the current thread and main thread. This enables the subscribers to run on a separate thread. This is useful for long-running operations such as network operations.
Subscriber Priorities
If you want to change the order in which subscribers get events, then you need to specify their priority levels during registration. Subscribers with a higher priority get the event before subscribers with a lower priority. This only affects subscribers in the same thread mode. Note that the default priority is 0.
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
textField.setText(event.getMessage());
}
Cancelling Events
If you want to stop an event from being delivered to other subscribers, call the cancelEventDelivery(Object event) method inside the subscriber's event handling method.
@Subscribe
public void onEvent(MessageEvent event){
EventBus.getDefault().cancelEventDelivery(event);
}
Conclusion
In this tutorial, you learned about:
greenrobot EventBus and how it can improve your Android app
the difference between regular and sticky events
the different thread modes available and when to use each one
Another library you can use to implement an event bus is RxAndroid. Check out our article on RxAndroid here on Envato Tuts+, or try some of our other Android courses or tutorials.
The codebase of complex apps with many network connections and user interactions are often littered with callbacks. Such code is not only lengthy and hard to...
In this tutorial, you'll learn to use Loopj, an easy-to-use library for HTTP requests in Android. To help you learn, we're going to use Loopj to create...
In this tutorial, you'll learn how to create, send and receive both local and system-wide broadcasts. You'll also learn how to use a popular third-party...
Animations have become an important part of the Android user experience. Subtle, well-crafted animation is used throughout the Android OS and can make your...
This tutorial is part of the Building Your Startup With PHP series on Envato Tuts+. In this series, I'm guiding you through launching a startup from concept to reality using my Meeting Planner app as a real-life example. Every step along the way, I'll release the Meeting Planner code as open-source examples you can learn from. I'm also addressing startup-related business issues as they arise.
Mobile Apps vs. the Responsive Web
Strategically, building mobile apps for Meeting Planner on iOS and Android makes sense, but financially I haven't raised the resources for this yet. Mathew Ingram recently wrote in Fortune that due to the plethora of offerings for mobile users, "statistically speaking at least—no one is going to download your app." So while certainly I could enhance the Meeting Planner experience with an app, its likelihood of adoption doesn't make immediate sense with my current resources.
However, it's extremely important to make Meeting Planner a great web experience on mobile devices.
In today's episode, I'll review and discuss making changes oriented to doing just that—essentially making our web application more of a responsive website, easily usable on mobile devices and tablets. Check out the results (on your phone or tablet)!
One of the challenges coding for today's episode was that I'm not a designer or CSS coder. Some days I feel like I shouldn't even be coding myself; at Microsoft I was a Group Program Manager, i.e. we had graphic designers, a fully-staffed usability lab, CSS didn't exist, etc.
Leading up to this work, I felt intimidated trying to learn media queries, breakpoints, and specialized CSS—it's not a subject matter I'm skilled at, and it's time consuming and very detail oriented. Yet, within 48 hours, everything came together quickly and beautifully. If you scan to the bottom of the story, you'll see how few lines of CSS were ultimately needed for all the changes. Suddenly as I began to browse Meeting Planner on my phone, I grew excited for how well the new responsive web experience was working.
Frankly, it made me feel that a dedicated mobile app just isn't necessary at the moment. We can build our audience with the mobile web experience for now, especially through the critical upcoming alpha and beta phases.
In the meantime, if you haven't tried out Meeting Planner yet, go ahead and schedule your first meeting from your phone or tablet. I do participate in the comment threads below, so tell me about your experience! You can also reach me on Twitter @reifman. I'm always interested in new feature requests and suggested tutorial topics.
As a reminder, all of the code for Meeting Planner is written in the Yii2 Framework for PHP. If you'd like to learn more about Yii2, check out our parallel series Programming With Yii2.
The Current Mobile Status
To begin, I browsed the current state of the Meeting Planner service with my iOS phone and took screenshots of the initial application. It's not terrible, but it's not great. Let's review what I found.
The Home Page
The home page looks good, although aesthetically I wish the lead text, "Making Scheduling Easy", would break a bit differently, i.e. on three approximately equal length lines. However, Bootstrap manages the drop-down menu well, and the rest of the page works functionally:
The Sign-Up Page
Again, other than the headline's aesthetic layout and left margin consistency, the sign-up page is basically functional:
Planning Meetings
Once the person begins planning meetings, the current index page needs improvement. There are too many columns. The subject is squished. Perhaps the information I chose to display here in the first place isn't essential. And, certainly, the command options aren't in view. The page needs to be adjusted for mobile more significantly.
Other pages function well, such as the new meeting request for a subject. However, mobile users probably don't want to be offered a textarea field to type in a longer message introducing the meeting:
Adding participants also becomes a bit dysfunctional with the bootstrap extensions we're using:
And the planning views for places and times begin to break down. Again, the desktop design offers too much detail and too many options for mobile:
Other Areas
The Places page functionally works but needs an improved layout of the buttons. And perhaps this functionality isn't needed for mobile users.
Similarly, the desktop tab and photo layout breaks down on mobile. It also needs to be rethought:
Developing Solutions
Certainly, there are a lot of areas of the site that can be improved. Some areas need to be rethought for mobile, some minimized, and others just aesthetically adjusted. Let's get to work.
Different Approaches
I had pretty much zero experience with media queries and breakpoints when I began this task. For a few days before, I procrastinated over diving into what I feared was an unfamiliar quagmire. I began with a practice media query to tease my editor:
@media only life
and (max-energy-level: 60%)
and (-caffeine-ratio: 2) {
.editorBossperson {
available-to:false;
visible-to:false;
}
}
Joking around helped break the mental ice in my head. I'm always available and visible to Envato's editorial gods.
There were a number of areas I began to consider:
Simplifying functionality, especially with the meeting planning process
Identifying the critical information to display for mobile
Concealing some functionality on mobile devices, such as elements, columns and menu options
Working with media-queries and breakpoints
Staying focused on the most important areas for the alpha release
One helpful concept I kept running into on the web was "Design for Mobile First." Unfortunately, I'm old school and hadn't done that. But it was helpful to rethink every page with this theme: Mobile First.
For example, the meeting index with four table columns had to go and was disorienting on portrait phones.
I kept asking myself how I would have designed all the pages to work from a phone.
Warming Up to Media Queries
Drop-Down Menus
It took me some effort to overcome my hesitation at diving into deep CSS. To warm up, I began working on minimizing the drop-down menus and simplifying the scope of mobile functionality.
For now, I decided to create a single basic media query for smaller devices and use that across the site. Here's frontend/site.css:
/* ----------- mobile displays ----------- */
@media only screen
and (min-device-width: 320px)
and (max-device-width: 667px)
and (-webkit-min-device-pixel-ratio: 2) {
/* hides drop down menu items and footer items */
.itemHide,li.menuHide {
display:none;
visible:false;
}
Making changes turned out to be relatively simple. For any menu item I wanted to hide on mobile, I just needed to add a CSS property, e.g. menuHide.
Here's the menuHide property added to /frontend/views/layouts/main.php:
Gradually, I realized that simplifying and reducing functionality in the mobile web would create the best experience. People can always go back to their desktop to access other features, at least for now. This would also be an opportunity to gather feedback from people during the alpha and beta phases.
Breadcrumbs
Yii's default layout includes a breadcrumb widget which is loaded via composer and harder to customize. I experimented with adding CSS to hide the first element and the first "/" divider:
It took some time but got me diving deeper into CSS, e.g. nth-child content, and built my confidence:
/* removes home and / from breadcrumb */
ul.breadcrumb li:first-child, li.tabHide {
display:none;
visible:false;
}
ul.breadcrumb li:nth-child(2)::before {
content:'';
}
I had no idea that CSS could modify content.
Here's the result:
Enhanced Button Spacing for Fingertips
Next, I added CSS to provide additional padding for buttons on mobile to make fingertip presses less error-prone. For example, here are the submit and cancel buttons on desktop devices:
This is the CSS I used and began adding to various buttons and clickable icons around the site:
Here's what that form looks like on mobile—notice the new padding between Submit and Cancel:
Responsive Text Wrapping
Making the home page heading, "Scheduling Made Easy," wrap actually turned out to take a bit more time. Ultimately, I added a <br /> tag to the text and hid it by default when not on mobile. But I also had to add a space in a span tag with the itemHide class.
Here's the CSS for .rwd-break. It's hidden by default and only appears in responsive displays, breaking the header text the way I want.
.rwd-break {
display:none;
}
/* ----------- mobile displays ----------- */
@media only screen
and (min-device-width: 320px)
and (max-device-width: 667px)
and (-webkit-min-device-pixel-ratio: 2) {
...
.rwd-break {
display:block;
}
}
Without the span tag space, the text would break without proper centering.
Simplifying the Meetings List Page
As I thought "mobile first" more and more, I realized that the phone-based user doesn't need all the functionality on my pages. They don't need all the tabs, they don't need the table of data about meetings, and they don't need all the icon button options. In fact, for the meeting page, they just need to be able to open meetings (they can cancel meetings from the meeting view page itself).
I combined the subject and participant columns into a single vertical column, and the result looks much better.
In /frontend/views/meeting/index.php, I added .tabHide to two of the four tabs:
Ultimately, these changes simplified the desktop interface in the process of improving mobile.
The Big Challenge: Meeting Scheduling
By far the most challenging task for me was to adapt the meeting scheduling page above for mobile. It was a mess on phones, and I was daunted. Separately, I've always worried how I would adopt this interface for multiple participants in the future—responsive requirements might just make this more difficult.
My use of Kartik's Bootstrap Switch Widget extension for Yii has its own limitations when it comes to modifying layout. Placing these elements in table columns worked well, but making table columns responsive wasn't as straightforward with media queries.
Certainly as I showed with the Meetings list page above, hiding columns is easy, but modifying the placement not so much.
I began by moving away from a horizontal table design for showing time and place options and towards a vertical, portrait style. And, apparently, tables and columns have their own capacity to wrap with HTML5 and CSS without media queries.
You can see the improved, empty meeting plan page here:
Each partial view required additional css columns for pre-defined Bootstrap grid layouts to work well, e.g. left col-xs4 and right col-xs-8. Here's an example:
Making the place and time scheduling forms responsive was the most difficult. I experimented and ultimately succeeded in using table columns that naturally wrap as the content window (or device) shrink.
I also eliminated showing participant status in its own column with disabled switches—you can't change them, so why show them as switches? Instead, I created a textual summary of the status of your participants for places and times. Here's the code for getWhenStatus():
public static function getWhenStatus($meeting,$viewer_id) {
// get an array of textual status of meeting times for $viewer_id
// Acceptable / Rejected / No response:
$whenStatus['text'] = [];
$whenStatus['style'] = [];
foreach ($meeting->meetingTimes as $mt) {
// build status for each time
$acceptableChoice=[];
$rejectedChoice=[];
$unknownChoice=[];
// to do - add meeting_id to MeetingTimeChoice for sortable queries
foreach ($mt->meetingTimeChoices as $mtc) {
if ($mtc->user_id == $viewer_id) continue;
switch ($mtc->status) {
case MeetingTimeChoice::STATUS_UNKNOWN:
$unknownChoice[]=$mtc->user_id;
break;
case MeetingTimeChoice::STATUS_YES:
$acceptableChoice[]=$mtc->user_id;
break;
case MeetingTimeChoice::STATUS_NO:
$rejectedChoice[]=$mtc->user_id;
break;
}
}
$temp ='';
// to do - update for multiple participants
// to do - integrate current setting for this user in style setting
if (count($acceptableChoice)>0) {
$temp.='Acceptable to '.MiscHelpers::getDisplayName($acceptableChoice[0]);
$whenStatus['style'][$mt->id]='success';
} else if (count($rejectedChoice)>0) {
$temp.='Rejected by '.MiscHelpers::getDisplayName($rejectedChoice[0]);
$whenStatus['style'][$mt->id]='danger';
} else if (count($unknownChoice)>0) {
$temp.='No response from '.MiscHelpers::getDisplayName($unknownChoice[0]);
$whenStatus['style'][$mt->id]='warning';
}
$whenStatus['text'][$mt->id]=$temp;
}
return $whenStatus;
}
Here's what it looks like on desktop —notice the landscape layout of the rows of text and switches:
And here's the mobile version, more portrait and stacked without media queries:
As an example, here's the CSS for the way I coded the table columns on the When (times) panel:
And here's the code for this partial form from /frontend/views/meeting-time/_list.php:
<?php
use yii\helpers\Html;
use frontend\models\Meeting;
use \kartik\switchinput\SwitchInput;
?><tr > <!-- panel row --><td ><table class="table-list"> <!-- list of times --><tr><td class="table-list-first"> <!-- time & status --><?= Meeting::friendlyDateFromTimestamp($model->start,$timezone) ?><?php
if ($whenStatus['text'][$model->id]<>'') {
?><br /><span class="smallStatus"><?php
echo $whenStatus['text'][$model->id];
?></span><br /><?php
}
?></td><td class="table-switches"> <!-- col of switches to float right --><table ><tr><td ><?php
if ($isOwner) {
showTimeOwnerStatus($model,$isOwner);
} else {
showTimeParticipantStatus($model,$isOwner);
}
?></td><td class="switch-pad"><?php
if ($timeCount>1) {
if ($model->status == $model::STATUS_SELECTED) {
$value = $model->id;
} else {
$value = 0;
}
if ($isOwner || $participant_choose_date_time) {
// value has to match for switch to be on
echo SwitchInput::widget([
'type' => SwitchInput::RADIO,
'name' => 'time-chooser',
'items' => [
[ 'value' => $model->id],
],
'value' => $value,
'pluginOptions' => [ 'size' => 'mini','handleWidth'=>60,'onText' => '<i class="glyphicon glyphicon-ok"></i> choose','onColor' => 'success','offText'=>'<i class="glyphicon glyphicon-remove"></i>'], // $whenStatus['style'][$model->id],
'labelOptions' => ['style' => 'font-size: 12px'],
]);
}
}
?></td></tr></table></td> <!-- end col with table of switches --></tr></table> <!-- end table list of times --></td></tr> <!-- end panel row -->
The best thing about these meeting view changes is that they'll simplify the UX design challenge for future meetings with many participants. Regardless of the number of people in a meeting, the view will remain basically the same as above. Essentially, this solved the greatest barrier to me expanding to multiple participant meetings—UX design.
What's Next?
I hope you've enjoyed following along as I work on the minutiae of responsive web design. As the code and visual changes to the site came together, I felt extremely satisfied and impressed with how little CSS was required. Taken together, you can see it here:
.rwd-break {
display:none;
}
table.table-list {
width:100%;
}
table.table-list td.table-list-first {
float: left;
display: inline;
width: auto;
}
table.table-list td.table-switches {
width: auto;
float: right;
display: inline;
padding-top: 10px;
}
.switch-pad {
padding-left:7px;
}
.smallStatus {
font-size:90%;
color: grey;
font-style: italic;
}
.setting-label label, #preferences label {
font-weight:normal;
}
/* ----------- mobile displays ----------- */
@media only screen
and (min-device-width: 320px)
and (max-device-width: 667px)
and (-webkit-min-device-pixel-ratio: 2) {
/* hides drop down menu items and footer items */
.itemHide,li.menuHide {
display:none;
visible:false;
}
/* removes home and / from breadcrumb */
ul.breadcrumb li:first-child, li.tabHide {
display:none;
visible:false;
}
ul.breadcrumb li:nth-child(2)::before {
content:'';
}
/* fingertip spacing for buttons */
a.icon-pad {
padding: 0 5px 0 2px;
}
.button-pad {
padding-left:7px;
}
.rwd-break {
display:block;
}
}
My future design efforts will begin, "What should this look like on mobile?"
As mentioned, I'm currently working feverishly to prepare Meeting Planner for alpha release. I am primarily focused on the key improvements and features that will make the alpha release go smoothly.
I'm tracking everything in Asana now, which I'll write about in another tutorial; it's been incredibly helpful. There are also some interesting new features still on their way.
I'm also beginning to focus more on the upcoming investment gathering effort with Meeting Planner. I'm just beginning to experiment with WeFunder based on the implementation of the SEC's new crowdfunding rules. Please consider following our profile. I will also write more about this in a future tutorial.
Haptic feedback adds a whole new dimension to mobile user interaction. Learn how to use the new iPhone 7 haptic feedback APIs to provide your users with a tactile response.
What Is Haptic Feedback?
In iOS 10 we have a new API to provide feedback to the user. Haptic feedback is an extension to the visual feedback that every user is used to already. If you have an iPhone 7 or iPhone 7 Plus, you may have noticed a small vibration of the device while scrolling a date picker or when changing the value of a switch—that's haptic feedback. The new UIFeedbackGenerator class can be used to implement the same behavior in your apps.
When to Use It
The first rule of haptic feedback from the iOS Human Interface Guidelines: do not overuse it! You shouldn't send useless feedback to your users. If you do, they might decide to turn off this feature for their whole device. Because it can be disabled, don't use haptic feedback as your only method of feedback with your user. It may not be available on some devices, and the requests will silently be ignored on older devices that don't support it.
In general, haptic feedback should be a response to an user-initiated action. In this way, it's easier for users to correlate the feedback with its source.
Finally, if you want to play a sound at the same time, you should take care to synchronize it.
UIImpactFeedbackGenerator provides a physical metaphor that complements the visual experience. For example, the user might feel a thud when a view slides into place or two objects collide. It has three variations: success, warning, and failure.
UINotificationFeedbackGenerator indicates that a task or action, such as depositing a check or unlocking a vehicle, has completed, failed, or produced a warning. It has three variations: light, medium, and heavy.
UISelectionFeedbackGenerator indicates that the selection is actively changing. For example, the user feels light taps while scrolling a picker wheel. This feedback is intended for communicating movement through a series of discrete values, not making or confirming a selection.
To explain these different types, the documentation has videos with sound to illustrate the pattern of each feedback type. Try them out yourself at the Apple Developer site.
How to Perform Feedback
Now let's start coding! We are going to build a very simple iOS app that gives a success feedback when a button is pressed. Open Xcode 8 and create a new iOS project.
Add a button in your Storyboard file and create an IBAction to trigger the feedback.
A feedback generator lifecycle looks like this:
Instantiate the generator.
Prepare the generator (optional).
Trigger feedback.
Release the generator (optional).
I'll walk you through each of these steps below.
Instantiate the Generator
First, declare a new variable of type UINotificationFeedbackGenerator. If you would like to use another type of feedback, just change this class.
var feedbackGenerator: UINotificationFeedbackGenerator? // Declare the generator type.
If you know in advance that the user is going to trigger a feedback, you should call the prepare() method. This will reduce the latency between your method call and the actual hardware feedback. An example is when you want to give multiple feedbacks during a gesture. In this case, first prepare the generator so that the latency is reduced.
Trigger Feedback
In the example that we are building, we have no way to know when the user will press the button. For this reason, inside the action we trigger the feedback right away.
We have declared the generator as optional so that, in case we don't need it anymore, we can set it as nil to release all its resources. This is an optional step.
There is currently no way to test haptic feedback in the Simulator. You should get your hands on an iPhone 7 and test out the flow of the app with the new feedback system in action. Run the app on your iPhone 7 and press the button on the screen. You should feel a success feedback!
When adding feedback support to your own app, make sure it feels right in the context that your users would expect, otherwise they could be confused.
Conclusion
Haptic feedback is a new great feature to make your app feel more realistic. Users always appreciate seeing that your app supports the latest features of their device, so go ahead and implement it right away.
In the meantime, check out some of our other courses and tutorials about Swift app development and new features in iOS 10.
In this article, I'll show you what you need to do to prepare your apps for iOS 10. As with every major release, iOS 10 introduces a slew of changes and...
In this tutorial, you will learn how to use the new UserNotificationsUI framework in iOS 10 to create custom interfaces for your app's local and push...
During this year's WWDC, Apple introduced a number of significant improvements to Core Data, taking the framework to the next level. In this article, I'll...
iOS 10 has just been released, and with it, Swift 3. Swift is a new programming language from Apple, designed specifically for creating iOS, macOS, and...
Haptic feedback adds a whole new dimension to mobile user interaction. Learn how to use the new iPhone 7 haptic feedback APIs to provide your users with a tactile response.
What Is Haptic Feedback?
In iOS 10 we have a new API to provide feedback to the user. Haptic feedback is an extension to the visual feedback that every user is used to already. If you have an iPhone 7 or iPhone 7 Plus, you may have noticed a small vibration of the device while scrolling a date picker or when changing the value of a switch—that's haptic feedback. The new UIFeedbackGenerator class can be used to implement the same behavior in your apps.
When to Use It
The first rule of haptic feedback from the iOS Human Interface Guidelines: do not overuse it! You shouldn't send useless feedback to your users. If you do, they might decide to turn off this feature for their whole device. Because it can be disabled, don't use haptic feedback as your only method of feedback with your user. It may not be available on some devices, and the requests will silently be ignored on older devices that don't support it.
In general, haptic feedback should be a response to an user-initiated action. In this way, it's easier for users to correlate the feedback with its source.
Finally, if you want to play a sound at the same time, you should take care to synchronize it.
UIImpactFeedbackGenerator provides a physical metaphor that complements the visual experience. For example, the user might feel a thud when a view slides into place or two objects collide. It has three variations: success, warning, and failure.
UINotificationFeedbackGenerator indicates that a task or action, such as depositing a check or unlocking a vehicle, has completed, failed, or produced a warning. It has three variations: light, medium, and heavy.
UISelectionFeedbackGenerator indicates that the selection is actively changing. For example, the user feels light taps while scrolling a picker wheel. This feedback is intended for communicating movement through a series of discrete values, not making or confirming a selection.
To explain these different types, the documentation has videos with sound to illustrate the pattern of each feedback type. Try them out yourself at the Apple Developer site.
How to Perform Feedback
Now let's start coding! We are going to build a very simple iOS app that gives a success feedback when a button is pressed. Open Xcode 8 and create a new iOS project.
Add a button in your Storyboard file and create an IBAction to trigger the feedback.
A feedback generator lifecycle looks like this:
Instantiate the generator.
Prepare the generator (optional).
Trigger feedback.
Release the generator (optional).
I'll walk you through each of these steps below.
Instantiate the Generator
First, declare a new variable of type UINotificationFeedbackGenerator. If you would like to use another type of feedback, just change this class.
var feedbackGenerator: UINotificationFeedbackGenerator? // Declare the generator type.
If you know in advance that the user is going to trigger a feedback, you should call the prepare() method. This will reduce the latency between your method call and the actual hardware feedback. An example is when you want to give multiple feedbacks during a gesture. In this case, first prepare the generator so that the latency is reduced.
Trigger Feedback
In the example that we are building, we have no way to know when the user will press the button. For this reason, inside the action we trigger the feedback right away.
We have declared the generator as optional so that, in case we don't need it anymore, we can set it as nil to release all its resources. This is an optional step.
There is currently no way to test haptic feedback in the Simulator. You should get your hands on an iPhone 7 and test out the flow of the app with the new feedback system in action. Run the app on your iPhone 7 and press the button on the screen. You should feel a success feedback!
When adding feedback support to your own app, make sure it feels right in the context that your users would expect, otherwise they could be confused.
Conclusion
Haptic feedback is a new great feature to make your app feel more realistic. Users always appreciate seeing that your app supports the latest features of their device, so go ahead and implement it right away.
In the meantime, check out some of our other courses and tutorials about Swift app development and new features in iOS 10.
In this article, I'll show you what you need to do to prepare your apps for iOS 10. As with every major release, iOS 10 introduces a slew of changes and...
In this tutorial, you will learn how to use the new UserNotificationsUI framework in iOS 10 to create custom interfaces for your app's local and push...
During this year's WWDC, Apple introduced a number of significant improvements to Core Data, taking the framework to the next level. In this article, I'll...
iOS 10 has just been released, and with it, Swift 3. Swift is a new programming language from Apple, designed specifically for creating iOS, macOS, and...
While building your app to provide a great experience is incredibly important, engaging with new and existing users is even more important to the long-term success of your app. In this tutorial you will learn how to use Firebase to enable Google Cloud Messaging and send notifications to your app, as well as how to allow users to share your app with their contacts who use either Android or iOS devices.
This tutorial will assume that you have already set up a project for Firebase and have access to the Firebase Console. To learn how to get started with Firebase for Android, check out one of our other tutorials:
Creating a back-end server requires a skill set that most independent app developers lack. Fortunately, there's Firebase, a cloud-based platform that...
With Firebase, creating real-time social applications is a walk in the park. And the best thing about it is that you don't have to write a single line of...
Firebase Notifications
Notifications are one of the easiest ways to let your users know about new events or features in your app and get them to open the app again. Using Firebase, you can send notifications to all users or to segments of your user base, allowing you to customize what your users receive and pique their interest.
Notifications While Your App Is in the Background
Displaying notifications when your app is in the background is incredibly easy, as it is handled automatically by the Firebase Messaging library. In your application, you will need to include the library with the following line in the dependenciesnode of your build.gradle file, and then install the app on a device.
Next, you'll need to go into your Firebase console and select the Notifications section in the left navigation panel.
Once you're in the correct section, you should see a screen prompting you to send your first message.
Once you have clicked on the blue button, you will be taken to a form that will allow you to add content to messages and select which groups should receive the message. Using this form, you can specify boolean conditions that a device or user must meet—such as geographical location or any other gathered data—in order to receive your notification.
Once you have sent the message, a notification should appear on your user's devices.
Notifications in the Foreground
One thing to notice is that notifications won't show up for a user if they are already in your app. In order to receive notifications in this situation, you will need to implement a Service that extends FirebaseMessagingService.
public class NotificationService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
}
}
You will also need to include this Service in your AndroidManifest.xml file.
Now that you have the general framework together, it's time to flesh out onMessageReceived in your FirebaseMessagingService class. The main purpose of this method is to take the data sent down with the RemoteMessage object and create a Notification based on what you receive.
There's a lot of information that can be passed down with the RemoteMessage. However, most of the options are only available if you use the Firebase back-end API, rather than the console. From the Firebase console, you are able to set a title, a message body, and custom key/value pairs.
Logging all of the available data from a RemoteMessage can be done like so:
Once you know what information is available, and how you want to handle it, you can create and display a notification for your users.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(remoteMessage.getNotification().getTitle());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentText(remoteMessage.getNotification().getBody());
NotificationManagerCompat.from(this).notify(0, builder.build());
And that's it! You should now be able to set up an Android app to send and receive notifications from the Firebase console.
App Invites
Getting new users on your app can be a daunting challenge, but word of mouth is a great way to get the ball rolling. Using Firebase, you can add the App Invites widget to your application, which will let your users share your app via email or SMS to other Android or iOS users.
Launching the Invite Prompt
Before you can start using this feature, you will need to import the package into your project by adding the following line to the dependenciesnode of your build.gradle file.
Once you have synced your project, you will be able to create a new Intent using the AppInviteInvitation.IntentBuilder class, which will launch a screen that allows users to select contacts to invite to the app. This builder provides various options for customizing the app invite screen:
setMessage: This will set the message that users see and can send to contacts over text message or email. This cannot be longer than 100 characters.
setCustomImage: Using this method, you can provide a URI to a custom image that will appear in the invite screen and invite email.
setCallToActionText: This method sets the text for the install button in emails. This has a limit of 32 characters.
setDeepLink: Allows you to provide metadata for your invite, which can be received on install for taking specific actions for your newly invited user.
setEmailHtmlContent: Allows you to override setMessage, setCustomImage, and setCallToActionText to create a custom HTML formatted email to send to potential new users.
setEmailSubject: Required if setEmailHtmlContent is used. As the name implies, this will set the subject for your custom email.
setOtherPlatformsTargetApplication: One of the more interesting options, this method will allow you to associate the Firebase client app ID for an iOS version of your app, allowing iOS users to install the proper version if it's shared by an Android user.
Once you've created your Intent, you can launch it with startActivityForResult to be notified when the user has returned from inviting others.
Now that you're able to invite other users to your app, let's take a moment to focus on deep linking options. When you create your Intent, you're able to add a URI as a deep link. When a user receives your invite on Android, you can use Google Play Services and the AppInvite API to intercept this URI and perform a custom action, such as presenting a welcome screen, for your new user.
boolean autodeeplink = true;
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(AppInvite.API)
.enableAutoManage(this, this)
.build();
AppInvite.AppInviteApi.getInvitation(mGoogleApiClient, this, autodeeplink)
.setResultCallback(
new ResultCallback<AppInviteInvitationResult>() {
@Override
public void onResult(AppInviteInvitationResult result) {
if (result.getStatus().isSuccess()) {
//Get intent information
Intent intent = result.getInvitationIntent();
String deepLink = AppInviteReferral.getDeepLink(intent);
String invitationId = AppInviteReferral.getInvitationId(intent);
}
}
}
);
You'll notice that we created a boolean named autodeeplink. When this is set to true, the Android system will automatically handle the received URI through filters in your AndroidManifest.xml file. If it is set to false, you can use the AppInvite API to extract information from the invitation and perform your custom action.
Conclusion
In this tutorial you have learned how to implement notifications from Firebase into your Android apps, and how to allow your users to easily share your app with their contacts. Understanding what's available in Firebase and how to implement the general features will go a long way in helping you quickly build apps that people enjoy using.
To learn more about Firebase or Android development in general, check out some of our other courses and tutorials here on Envato Tuts+.
In this video from my course on building a material design app, you'll learn how to create the user interface of a material design app. You’ll learn how to...
App crashes should never be ignored because they tend to drive users away. By using one of the many crash reporting solutions available today, you can...
During the 2016 Google I/O conference, Firebase was reintroduced to the developer community as a major resource for providing quick back-end support for web...
Years ago, when Android was still a budding mobile operating system, it was rather notorious for its ugly user interface. Because there were no design...
While building your app to provide a great experience is incredibly important, engaging with new and existing users is even more important to the long-term success of your app. In this tutorial you will learn how to use Firebase to enable Google Cloud Messaging and send notifications to your app, as well as how to allow users to share your app with their contacts who use either Android or iOS devices.
This tutorial will assume that you have already set up a project for Firebase and have access to the Firebase Console. To learn how to get started with Firebase for Android, check out one of our other tutorials:
Creating a back-end server requires a skill set that most independent app developers lack. Fortunately, there's Firebase, a cloud-based platform that...
With Firebase, creating real-time social applications is a walk in the park. And the best thing about it is that you don't have to write a single line of...
Firebase Notifications
Notifications are one of the easiest ways to let your users know about new events or features in your app and get them to open the app again. Using Firebase, you can send notifications to all users or to segments of your user base, allowing you to customize what your users receive and pique their interest.
Notifications While Your App Is in the Background
Displaying notifications when your app is in the background is incredibly easy, as it is handled automatically by the Firebase Messaging library. In your application, you will need to include the library with the following line in the dependenciesnode of your build.gradle file, and then install the app on a device.
Next, you'll need to go into your Firebase console and select the Notifications section in the left navigation panel.
Once you're in the correct section, you should see a screen prompting you to send your first message.
Once you have clicked on the blue button, you will be taken to a form that will allow you to add content to messages and select which groups should receive the message. Using this form, you can specify boolean conditions that a device or user must meet—such as geographical location or any other gathered data—in order to receive your notification.
Once you have sent the message, a notification should appear on your user's devices.
Notifications in the Foreground
One thing to notice is that notifications won't show up for a user if they are already in your app. In order to receive notifications in this situation, you will need to implement a Service that extends FirebaseMessagingService.
public class NotificationService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
}
}
You will also need to include this Service in your AndroidManifest.xml file.
Now that you have the general framework together, it's time to flesh out onMessageReceived in your FirebaseMessagingService class. The main purpose of this method is to take the data sent down with the RemoteMessage object and create a Notification based on what you receive.
There's a lot of information that can be passed down with the RemoteMessage. However, most of the options are only available if you use the Firebase back-end API, rather than the console. From the Firebase console, you are able to set a title, a message body, and custom key/value pairs.
Logging all of the available data from a RemoteMessage can be done like so:
Once you know what information is available, and how you want to handle it, you can create and display a notification for your users.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(remoteMessage.getNotification().getTitle());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentText(remoteMessage.getNotification().getBody());
NotificationManagerCompat.from(this).notify(0, builder.build());
And that's it! You should now be able to set up an Android app to send and receive notifications from the Firebase console.
App Invites
Getting new users on your app can be a daunting challenge, but word of mouth is a great way to get the ball rolling. Using Firebase, you can add the App Invites widget to your application, which will let your users share your app via email or SMS to other Android or iOS users.
Launching the Invite Prompt
Before you can start using this feature, you will need to import the package into your project by adding the following line to the dependenciesnode of your build.gradle file.
Once you have synced your project, you will be able to create a new Intent using the AppInviteInvitation.IntentBuilder class, which will launch a screen that allows users to select contacts to invite to the app. This builder provides various options for customizing the app invite screen:
setMessage: This will set the message that users see and can send to contacts over text message or email. This cannot be longer than 100 characters.
setCustomImage: Using this method, you can provide a URI to a custom image that will appear in the invite screen and invite email.
setCallToActionText: This method sets the text for the install button in emails. This has a limit of 32 characters.
setDeepLink: Allows you to provide metadata for your invite, which can be received on install for taking specific actions for your newly invited user.
setEmailHtmlContent: Allows you to override setMessage, setCustomImage, and setCallToActionText to create a custom HTML formatted email to send to potential new users.
setEmailSubject: Required if setEmailHtmlContent is used. As the name implies, this will set the subject for your custom email.
setOtherPlatformsTargetApplication: One of the more interesting options, this method will allow you to associate the Firebase client app ID for an iOS version of your app, allowing iOS users to install the proper version if it's shared by an Android user.
Once you've created your Intent, you can launch it with startActivityForResult to be notified when the user has returned from inviting others.
Now that you're able to invite other users to your app, let's take a moment to focus on deep linking options. When you create your Intent, you're able to add a URI as a deep link. When a user receives your invite on Android, you can use Google Play Services and the AppInvite API to intercept this URI and perform a custom action, such as presenting a welcome screen, for your new user.
boolean autodeeplink = true;
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(AppInvite.API)
.enableAutoManage(this, this)
.build();
AppInvite.AppInviteApi.getInvitation(mGoogleApiClient, this, autodeeplink)
.setResultCallback(
new ResultCallback<AppInviteInvitationResult>() {
@Override
public void onResult(AppInviteInvitationResult result) {
if (result.getStatus().isSuccess()) {
//Get intent information
Intent intent = result.getInvitationIntent();
String deepLink = AppInviteReferral.getDeepLink(intent);
String invitationId = AppInviteReferral.getInvitationId(intent);
}
}
}
);
You'll notice that we created a boolean named autodeeplink. When this is set to true, the Android system will automatically handle the received URI through filters in your AndroidManifest.xml file. If it is set to false, you can use the AppInvite API to extract information from the invitation and perform your custom action.
Conclusion
In this tutorial you have learned how to implement notifications from Firebase into your Android apps, and how to allow your users to easily share your app with their contacts. Understanding what's available in Firebase and how to implement the general features will go a long way in helping you quickly build apps that people enjoy using.
To learn more about Firebase or Android development in general, check out some of our other courses and tutorials here on Envato Tuts+.
In this video from my course on building a material design app, you'll learn how to create the user interface of a material design app. You’ll learn how to...
App crashes should never be ignored because they tend to drive users away. By using one of the many crash reporting solutions available today, you can...
During the 2016 Google I/O conference, Firebase was reintroduced to the developer community as a major resource for providing quick back-end support for web...
Years ago, when Android was still a budding mobile operating system, it was rather notorious for its ugly user interface. Because there were no design...
In this series, you'll learn how to use React Native to create page layouts commonly used in mobile apps. The layouts you'll be creating won't be functional—instead, the main focus of this series is to get your hands dirty in laying out content in your React Native apps.
If you're new to laying out React Native apps or styling in general, check out my previous tutorial:
To follow along with this series, I challenge you to try recreating each screen by yourself first, before you read my step-by-step instructions in the tutorial. You won't really benefit much from this tutorial just by reading it! Try first before looking up the answers here. If you succeed in making it look like the original screen, compare your implementation to mine. Then decide for yourself which one is better!
In this second part of the series, you'll create the following calendar page:
Calendar apps are used to track events and appointments added by the user. You'll find different variations in the wild, but most of them will have the same elements as a physical calendar would: the current month and year, the days in the month, and the events or appointments added by the user.
Here are a couple of examples of this type of layout:
Project Setup
The first step, of course, is to set up a new React Native project:
react-native init react-native-common-screens
Once the project is set up, open the index.android.js file and replace the default code with the following:
import React, { Component } from 'react';
import {
AppRegistry
} from 'react-native';
import Calendar from './src/pages/Calendar';
export default class ReactNativeCommonScreens extends Component {
render() {
return (
<Calendar />
);
}
}
AppRegistry.registerComponent('ReactNativeCommonScreens', () => ReactNativeCommonScreens);
Create a src/pages folder and create a Calendar.js file inside it.
You'll also need the react-native-vector-icons package. This is specifically used for the navigation icons as well as other icons that will be needed in the page.
npm install --save react-native-vector-icons
Open the android/app/build.gradle file and add a reference to the package:
dependencies {
//rest of the dependencies are here at the top
compile project(':react-native-vector-icons') //add this
}
Do the same with the android/settings.gradle file by adding the following at the bottom:
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
Open android/app/src/main/java/com/react-native-common-screens/MainApplication.java and import the package:
import java.util.Arrays;
import java.util.List;
import com.oblador.vectoricons.VectorIconsPackage; //add this
Lastly, initialize the package:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VectorIconsPackage() //add this
);
}
Creating the Calendar Page
Okay, now that you've tried to code the layout yourself (no cheating, right?), I'll show you how I built my implementation.
At first, I thought this would be the most difficult one to implement, but trust me, it's really not that complicated as long as you already know the basics. There are a couple of opportunities here to use JavaScript code to help with rendering.
Start by including all the components and packages that you'll need:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
ScrollView
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import { range } from 'lodash';
import Button from '../components/Button';
This time there's a new package which you haven't installed yet, and that is lodash. You won't really need the whole lodash library, just the range function. This is used for generating an array of numbers based on a specific range. You can install just this function by executing npm install --save lodash.range on your terminal.
The header has three elements in it: the button for going back to the previous page, the title of the current page, and the text showing a human-friendly representation of the currently selected date.
header has a flexDirection of row so that each header_item is stacked horizontally. The same flex value is assigned to each of them so they consume equal amounts of space. text_center and text_right are used to align the text inside of those header_items to the center and right. This is done because by default they're aligned on the left-most side of their container.
The calendar header allows the user to change the year and month.
There are at least two ways this can be implemented. The first method is to treat each element as a single item and apply justifyContent: 'space-between' to its container. The second method is to group all the elements that have to do with the year and group those that have to do with the month.
The second method is the one that's applied below. Semantically speaking, this makes much more sense because the button for navigating back a year, the year itself, and the button for navigating forward are all related, so you can treat them as a single thing by putting them in the same container. The same is true with the month controls.
From there, you can apply the same technique to those two groups of components in the same line. To add spaces between the two buttons (back and forward) and the label, we use justifyContent: 'space-between'. We use alignItems: 'center' to nudge all the elements inside it towards the center. Finally, we add left and right padding to add more space between the two groups.
So instead of having seven View or Text components rendering each day of the week, you can just have an array containing the days of the week. You can then iterate through those days using the Array.map() function. For each iteration, render a Text component that shows the day.
Note that in the code above, the toUpperCase() function is used to convert all the letters of each day to uppercase. React Native doesn't come with the text-transform CSS property, so this is the only way to achieve uppercase letters aside from manually using uppercase strings.
The renderWeeks() function uses the range() function in lodash to generate an array containing the days from the last month and the days of the current month. Those two arrays are then merged together.
However, you can't directly use the resulting array as the data source for the calendar days. That's because if you simply loop through the items and output a Text component for each day, there won't be any distinction between each week. You already know that to make each calendar day inline, you need to apply flexDirection: 'row' to its container. So applying it to a single container would result in having all the calendar days placed in a single line.
This means you need to have a separate container for each week. The question is how. Again, there are at least two ways to accomplish this.
The first method is to have a variable store how many days are currently outputted and then add a conditional statement that will render an opening <View> every time the variable contains 0 and a closing </View> every time it's 7. Once it's 7, reset it back to 0. This is the most straightforward method.
But I'll use a different method here. Below, the getWeeksArray() function is used to implement it. This function accepts the array of days and groups them into arrays containing seven days each. From there, you can loop through each of those arrays to render the week container. Then for each iteration, you again loop through the days inside the week to render the days. This is what the renderDays() function does.
renderWeeks() {
let past_month_days = range(27, 31);
let this_month_days = range(1, 30);
let days = past_month_days.concat(past_month_days, this_month_days);
let grouped_days = this.getWeeksArray(days);
return grouped_days.map((week_days, index) => {
return (<View key={index} style={styles.week_days}>
{ this.renderDays(week_days) }</View>
);
});
}
Next is the note added by the user for the currently selected day and the selected date and time. Again, it's better to group elements according to their purpose rather than how they're placed in the page. Certainly all these elements are related, so we'll place them inside the same container. But on a closer look, you'll start to see that you can group them further: the actual note and the selected date. With that in mind, here's the markup that you'll end up with:
<View style={styles.notes}><View style={styles.notes_notes}><Text style={styles.notes_text}>Riding my bike around the neighborhood.</Text></View><View style={[styles.notes_selected_date]}><Text style={styles.small_text}>8:23 PM</Text><Text style={styles.big_text}>14</Text><View style={styles.inline}><Icon name="bicycle" size={20} color="#CCC" /><Text style={styles.small_text}> THURSDAY</Text></View></View></View>
The selected date occupies less space than the note, so you have to apply a bigger flex value to the notes. flex: 3 and flex: 1 are used in this case, which means that the notes consume 3/4 of the available space and the selected date consumes 1/4. You can also use decimals (0.75 and 0.25) if that makes more sense to you. What's important is to pick a standard and stick to it. alignItems: 'flex-end' is used on notes_selected_date so that all its children will be aligned to the right. This is needed because by default they're aligned to the left.
That's it! In this tutorial you've created a calendar page. We've made a nice calendar layout for an app, and I've shown you how JavaScript code can be used to compensate for some of the limitations of Flexbox.
As you have seen, we needed a way to limit the number of days in a row to just seven days. Flexbox doesn't have a way to specify this, so we used JavaScript to reconstruct the original array of days in such a way that they're divided into groups containing seven days each. From there, all we had to do was to wrap each group inside a View and then apply flexDirection: 'row' to make each of them render in their own row.
In an upcoming tutorial, you'll learn how to implement the layout commonly used in gallery pages. In the meantime, check out some of our other tutorials on React Native and Flexbox.
When building mobile applications, you might not always want to go native if it means that you would have to develop your app for multiple platforms. But a...
React Native, created by Facebook, lets you write native mobile apps in modern JavaScript. React Native apps will be transformed into native views specific...
Are you looking to improve your flexbox knowledge and at the same time learn how to build easily an attractive and unique layout? If so, be sure to read this...
Animations can breath life into your app and make your UIs more intuitive. In this tutorial, you'll learn how to implement different kinds of animations in...
CSS, despite its relatively low perceived skill ceiling, always seems to have a killer feature up its sleeve. Remember how media queries made responsive...
A UI kit is a great way to get started with the design for your next app. Each one comes with a complete set of assets for every part of your app's user interface, along with original PSD Photoshop files for easy customization. Make your app or game stand out with one of these kits.
Let's start with all-in-one UI kits that can be used for any mobile app. Each of these kits comes with a large collection of designs for commonly-needed screens.
Bajing is a complete UI kit designed to help you design and prototype your apps faster than ever before. The kit comes complete with a set of 33 PSD files with layouts for common app screens. You'll find designs for login screens, user profiles, media players, and navigation bars. With the layered PSDs, you can customize or remix any one of these for use in your own app.
Next eCommerce is optimized for e-commerce apps. With 35 distinct and stylish options for page layouts, you're sure to find something here that works for your next app. And nothing is left out: you'll find product pages, landing pages, as well as common screens like login or signup pages. One feature that sets this kit apart is that it includes Sketch design files as well as Photoshop PSDs. Not only that, but all shapes are vectors and can be completely customized.
Dark UI Kit is a stylish light-on-dark UI design. With a flat design style, this would make a great starter for your next iOS app. The kit includes 12 PSD files, each one representing a different kind of screen and having editable layers as well as vector icons, buttons and shapes.
Limitless is an attention-grabbing, high contrast UI design that will make your app stand out in the app store. With a flat design and bold graphics, this app is modern, clean, and visually impactful. The UI kit comes with 20 different layouts for common screen such as a calendar, timeline, profiles, inbox, and music player. All the layouts are Retina-ready, all the shapes are vectors, and the PSDs are organized so it will be easy to customize the colour scheme.
Photo App UI Kit is a clean, modern dark-on-light design that is perfect for building a photography or social media app. The UI kit is designed to look right on iOS or Android and includes 20 different PSD files for galleries, feeds, profiles, and navigation bars. Free fonts from Google complement the design and help set it head and shoulders above the pack.
The Paycar App UI Kit has one goal: to help you design a great car sharing or booking app. With beautiful, full-screen map views, and layouts for all the screens a car-sharing app could need, Paycar can help make your app beautiful.
So far, we've looked at some pretty serious app designs: car sharing, photography, and e-commerce. Let's lighten up a bit and look at some fun assets that you could use for your next mobile game!
Game of Kings is a giant collection of assets for a fantasy game in flat relief style. This kit includes sprites, icons, monsters, tilesets and four luscious, seamlessly scrollable backgrounds. With bright colours and a bold but simple style, these assets are sure to dazzle and engage your users. Best of all, the images are all provided as vectors and can be resized, coloured or edited to your heart's content.
Flat Jetpack is a platformer game template with a fresh and colourful style. This kit has a full tank of assets, including seamless game backgrounds with parallax, animated character and enemy sprites, terrain, animated water, effects and more. Not only that, but it comes with a complete pack of user interface elements. This game kit will launch your next app into the stratosphere!
Wireframing
Wireframing is a vital part of the UI design process. Not only do wireframes help you understand your app better and plan its implementation, they also let you communicate your app design idea to the client.
App Wireframes UI Kit is a tool for sketching simple, beautiful mobile app wireframes. Just drag and drop elements within the included template to create clean, attractive user behaviour flow charts. These charts and simplified screens will help you map out your app and it will help your clients understand the planned content and layout of each page.
Simple Mobile UI Wireframe Design Kit has a creative approach to improving your UI design process. The kit includes a template with drag and drop screen elements. There are 57 of these included wireframe UI objects—just arrange them into an idea for an app screen. The template has a gridded background and is meant to be printed so you can pass it around to others for comments or corrections. Design. Print. Edit. Repeat!
CodeCanyon has hundreds of iOS app templates that you can use to jump-start your development. This video will show you how to install and customize a...
CodeCanyon has hundreds of Android app templates that you can use to jump-start the development of your next app. In this tutorial, I'll help you get started...
Ionic is a popular framework for creating modern, hybrid, mobile applications, using JavaScript, HTML, and CSS. Ionic is powered by AngularJS and Apache...
Project templates are a great way to learn from other people's work. This article lists a few popular iOS templates available on Envato Market. If you are...
Project templates are a great way to learn from other people's work. This article lists a few popular Android templates available on Envato Market. If you...
CodeCanyon has hundreds of Android app templates that you can use to jump start your development. This video will show you how to install and customize a...
2016-11-30T12:31:27.000Z2016-11-30T12:31:27.000ZAdam Brown
A UI kit is a great way to get started with the design for your next app. Each one comes with a complete set of assets for every part of your app's user interface, along with original PSD Photoshop files for easy customization. Make your app or game stand out with one of these kits.
Let's start with all-in-one UI kits that can be used for any mobile app. Each of these kits comes with a large collection of designs for commonly-needed screens.
Bajing is a complete UI kit designed to help you design and prototype your apps faster than ever before. The kit comes complete with a set of 33 PSD files with layouts for common app screens. You'll find designs for login screens, user profiles, media players, and navigation bars. With the layered PSDs, you can customize or remix any one of these for use in your own app.
Next eCommerce is optimized for e-commerce apps. With 35 distinct and stylish options for page layouts, you're sure to find something here that works for your next app. And nothing is left out: you'll find product pages, landing pages, as well as common screens like login or signup pages. One feature that sets this kit apart is that it includes Sketch design files as well as Photoshop PSDs. Not only that, but all shapes are vectors and can be completely customized.
Dark UI Kit is a stylish light-on-dark UI design. With a flat design style, this would make a great starter for your next iOS app. The kit includes 12 PSD files, each one representing a different kind of screen and having editable layers as well as vector icons, buttons and shapes.
Limitless is an attention-grabbing, high contrast UI design that will make your app stand out in the app store. With a flat design and bold graphics, this app is modern, clean, and visually impactful. The UI kit comes with 20 different layouts for common screen such as a calendar, timeline, profiles, inbox, and music player. All the layouts are Retina-ready, all the shapes are vectors, and the PSDs are organized so it will be easy to customize the colour scheme.
Photo App UI Kit is a clean, modern dark-on-light design that is perfect for building a photography or social media app. The UI kit is designed to look right on iOS or Android and includes 20 different PSD files for galleries, feeds, profiles, and navigation bars. Free fonts from Google complement the design and help set it head and shoulders above the pack.
The Paycar App UI Kit has one goal: to help you design a great car sharing or booking app. With beautiful, full-screen map views, and layouts for all the screens a car-sharing app could need, Paycar can help make your app beautiful.
So far, we've looked at some pretty serious app designs: car sharing, photography, and e-commerce. Let's lighten up a bit and look at some fun assets that you could use for your next mobile game!
Game of Kings is a giant collection of assets for a fantasy game in flat relief style. This kit includes sprites, icons, monsters, tilesets and four luscious, seamlessly scrollable backgrounds. With bright colours and a bold but simple style, these assets are sure to dazzle and engage your users. Best of all, the images are all provided as vectors and can be resized, coloured or edited to your heart's content.
Flat Jetpack is a platformer game template with a fresh and colourful style. This kit has a full tank of assets, including seamless game backgrounds with parallax, animated character and enemy sprites, terrain, animated water, effects and more. Not only that, but it comes with a complete pack of user interface elements. This game kit will launch your next app into the stratosphere!
Wireframing
Wireframing is a vital part of the UI design process. Not only do wireframes help you understand your app better and plan its implementation, they also let you communicate your app design idea to the client.
App Wireframes UI Kit is a tool for sketching simple, beautiful mobile app wireframes. Just drag and drop elements within the included template to create clean, attractive user behaviour flow charts. These charts and simplified screens will help you map out your app and it will help your clients understand the planned content and layout of each page.
Simple Mobile UI Wireframe Design Kit has a creative approach to improving your UI design process. The kit includes a template with drag and drop screen elements. There are 57 of these included wireframe UI objects—just arrange them into an idea for an app screen. The template has a gridded background and is meant to be printed so you can pass it around to others for comments or corrections. Design. Print. Edit. Repeat!
CodeCanyon has hundreds of iOS app templates that you can use to jump-start your development. This video will show you how to install and customize a...
CodeCanyon has hundreds of Android app templates that you can use to jump-start the development of your next app. In this tutorial, I'll help you get started...
Ionic is a popular framework for creating modern, hybrid, mobile applications, using JavaScript, HTML, and CSS. Ionic is powered by AngularJS and Apache...
Project templates are a great way to learn from other people's work. This article lists a few popular iOS templates available on Envato Market. If you are...
Project templates are a great way to learn from other people's work. This article lists a few popular Android templates available on Envato Market. If you...
CodeCanyon has hundreds of Android app templates that you can use to jump start your development. This video will show you how to install and customize a...
2016-11-30T12:31:27.000Z2016-11-30T12:31:27.000ZAdam Brown
Nobody writes bugs into their software on purpose, but have you ever considered just how many problems exist in your software applications that you aren’t even aware of?
Have you ever received support requests and been unable to replicate a problem, or do you spend hours looking through log files to find the root causes of issues? We’ve all been there, dealing with unexpected issues that occur in our codebase.
The picture is not always clear as to what the route cause of the problem was, and your team might be spending hours hunting through logs to work it out.
Raygun offers solutions this problem, with a real-time window into how your application is performing for your customers, helping you pinpoint the improvement that needs to be made in seconds. Plus, Raygun offers workflow functionalities to collaborate with your fellow developers all the way through to deploying the fix.
The Raygun platform offers essential software development tools that help keep a watchful eye on your web and mobile apps when issues arise. Offering both Error and Crash Reporting software, which helps detect, diagnose and destroy any exceptions your users encounter, and a Real User Monitoring tool that monitors each user session for things like slow loading assets, scripts and requests that could be slowing down your application’s performance, Raygun covers all bases.
Today we’ll be taking a deeper look into how you can set up real-time error and crash reporting for your applications with Raygun, regardless of which programming language and platform you use.
If you've ever clicked "Don't Send" on an operating system crash reporting dialog then you know that few users actively report bugs—most simply walk away in frustration.
In fact, a survey by Compuware reported that only 16% of users try a crashing app more than twice. It's vital to know if your software is crashing for your users. Raygun makes this easy.
With just a few short lines of code, you can integrate Raygun into your development environment in minutes. Raygun supports all major programming languages and platforms, so simply select the language you want to get started with. You'll instantly begin receiving reports of errors and crashes and will be able to study diagnostic information and stack traces on the Raygun dashboard.
For this tutorial, I'll show you examples of tracking JavaScript apps such as Ghost and PHP-based WordPress, but Raygun works everywhere: front end, back end, web, and mobile.
By pinpointing problems for you and telling you exactly where to look, Raygun helps you build healthier, more reliable software to delight your users and keep them coming back.
More importantly, Raygun is built for teams and supports integrations for workplace software such as team chat, e.g. Slack and Hipchat, project management tools, e.g. JIRA and Sprintly, and issue trackers, e.g. GitHub and Bitbucket. Raygun gives your team peace of mind that your software is performing as you want it to—flawlessly.
How cool are real-time Slack notifications for your entire team whenever your customers are encountering bugs, issues, and crashes!
In this tutorial, I'll walk you through setting up your application with Raygun step by step.
If you have any requests for future tutorials or questions and comments on today's, please post them below. You can also reach me on Twitter @lookaheadio or email me directly.
Getting Started
One of the most powerful features of Raygun is that it works with all the major programming languages and platforms, and it's amazingly easy to integrate. Just copy and paste the code into your application and Raygun will start monitoring for errors. In the case of WordPress, they provide a pre-built plugin.
Integrating Raygun With Your Application
After signing up, you'll be presented with a short Raygun integration wizard. It starts with selecting your language of choice. Here's the initial dashboard that you'll see:
Here's an example of integrating for use with any JavaScript code or platform.
Using Raygun With JavaScript
Once you select JavaScript, you'll be shown your Application API Key (the key is the same for all platforms you choose and is application specific). For each environment or language you want to monitor, you should create a separate application to organise errors into different buckets.
Raygun is easy to use regardless of which JavaScript package management system you prefer:
For example, with Bower, run:
bower install raygun4js
From NuGet, open the console and run:
Install-Package raygun4js
But you can also just load the library from Raygun's CDN within your application:
Once you've installed the plugin, you load the configuration menu from the WordPress dashboard and provide your API key:
Within a minute, you'll start seeing errors collected in your Raygun dashboard. If not, click the Send Test Error button to trigger one.
The Raygun Dashboard
Initially, you'll see an empty dashboard:
But once you've chosen your language and integrated your application, you'll see a dashboard like this—oh, theme developers—in which Raygun helped me discover a plethora of WordPress theme code that hadn't been kept up to date with the latest versions of PHP.
Tracking Errors Across Code Deployments
When you integrate Raygun with your deployment tools, it can track errors according to specific versions of released software. This can help you identify and repair bad deployments quickly and easily:
Raygun currently lets you assign error groups to one of five statuses. These are:
Active
Resolved
Resolved In Version x.xx
Ignored
Permanently Ignored
When an error is first received, it is assigned to Active and is visible in the first tab. You can then take action to change it to another status.
For example, as soon as I activated Raygun with WordPress and discovered a plethora of theme-related PHP compatibility issues, my email queue began to fill—but this was easily resolved by asking Raygun to only notify me of new reports.
You can also filter and manage issues by status through the interface quite easily. For example, it would be easy to delete all the errors resolved in WordPress version 4.3.
Raygun Error Detailed Views
When you click on errors, Raygun shows you their detail view with stack trace and a summary of which users and browsers or devices are being affected:
In detail view, Raygun also allows you and your team to comment and discuss specific issues:
Raygun User Tracking
If you implement user tracking with your Raygun integration, you can see exactly which of your authenticated users have run into specific errors and how often:
Raygun offers easy documentation for linking error reports to the current signed-in user. Here's an example for JavaScript:
By default, Raygun4JS assigns a unique anonymous ID for the current user. This is stored as a cookie. If the current user changes, to reset it and assign a new ID you can call:
Raygun.resetAnonymousUser();
To disable anonymous user tracking, call Raygun.init('apikey', { disableAnonymousUserTracking: true });.
You can provide additional information about the currently logged in user to Raygun by calling: Raygun.setUser('unique_user_identifier');.
This method takes additional parameters that are used when reporting over the affected users. The full method signature is:
setUser: function (user, isAnonymous, email, fullName, firstName, uuid)
Managing Your Team
Raygun is built around tracking issues across development teams. Through the settings area, it's easy to add applications that you're tracking and invite team members to participate:
As mentioned above, Raygun easily integrates with other team-based tools such as chat (Slack, Hipchat, etc.), project management (JIRA, Sprintly, etc.) and issue trackers (GitHub, Bitbucket, etc.).
Helpful Customer Support
Raygun support is excellent. In addition to the web-based documentation and email welcome guides, there's helpful support personnel (like Nick) ready to guide you deeper into the service—Nick's tips and availability just popped up as I was reviewing the service:
The Raygun API
If you'd like to tailor or customize event triggers, you can post errors via the Raygun API however you'd like from your application. This can be helpful for developers wishing to integrate monitoring or specialized reporting across their services or to make the development process easier.
Raygun Pulse Real User Monitoring
Lastly, even though we talked through the setup of Raygun’s Crash Reporting software here, adding Pulse - Real User Monitoring is just as easy when adding the Raygun code snippet.
Pulse provides you with all the session data from ‘real’ users when they are interacting with your applications. This is far more powerful than traditional synthetic testing, as data is taken directly from the user’s browser, letting you see how a user’s unique device, browser, OS, location or connection speed affected the application’s performance. Problematic scripts, requests and assets can be identified as the route cause and optimizations made to improve the overall performance of your application.
Couple Pulse with Crash Reporting data and you have all the tools available to ensure your customers are receiving a great user experience.
In Summary
I hope you've found Raygun easy to use and helpful to your development requirements. To recap, here are some of the major benefits of the service:
Raygun provides a complete overview of problems across your entire development stack. Intelligent grouping of errors lets you see the highest priority issues rather than flooding you with notifications for every error.
Raygun supports all major programming languages and platforms. Every developer can use it. Developer time is expensive, so stop wasting time trying to hunt down bugs. Fix issues faster and build more features instead!
Raygun is built for teams. You can invite unlimited team members to your account—no restrictions. Raygun helps you create a team workflow for fixing bugs and provides custom notifications and a daily digest of error events for all of your team.
For large corporate entities, Raygun Enterprise can provide cloud support or the ability to securely self-host a version of the service for your needs.
When you give Raygun a try, please let us know your questions and comments below. You can also reach me on Twitter @lookaheadio or email me directly. Or, if Raygun saves you a ton of time right away, you can browse my Envato Tuts+ instructor page to read the other tutorials I've written.
Nobody writes bugs into their software on purpose, but have you ever considered just how many problems exist in your software applications that you aren’t even aware of?
Have you ever received support requests and been unable to replicate a problem, or do you spend hours looking through log files to find the root causes of issues? We’ve all been there, dealing with unexpected issues that occur in our codebase.
The picture is not always clear as to what the route cause of the problem was, and your team might be spending hours hunting through logs to work it out.
Raygun offers solutions this problem, with a real-time window into how your application is performing for your customers, helping you pinpoint the improvement that needs to be made in seconds. Plus, Raygun offers workflow functionalities to collaborate with your fellow developers all the way through to deploying the fix.
The Raygun platform offers essential software development tools that help keep a watchful eye on your web and mobile apps when issues arise. Offering both Error and Crash Reporting software, which helps detect, diagnose and destroy any exceptions your users encounter, and a Real User Monitoring tool that monitors each user session for things like slow loading assets, scripts and requests that could be slowing down your application’s performance, Raygun covers all bases.
Today we’ll be taking a deeper look into how you can set up real-time error and crash reporting for your applications with Raygun, regardless of which programming language and platform you use.
If you've ever clicked "Don't Send" on an operating system crash reporting dialog then you know that few users actively report bugs—most simply walk away in frustration.
In fact, a survey by Compuware reported that only 16% of users try a crashing app more than twice. It's vital to know if your software is crashing for your users. Raygun makes this easy.
With just a few short lines of code, you can integrate Raygun into your development environment in minutes. Raygun supports all major programming languages and platforms, so simply select the language you want to get started with. You'll instantly begin receiving reports of errors and crashes and will be able to study diagnostic information and stack traces on the Raygun dashboard.
For this tutorial, I'll show you examples of tracking JavaScript apps such as Ghost and PHP-based WordPress, but Raygun works everywhere: front end, back end, web, and mobile.
By pinpointing problems for you and telling you exactly where to look, Raygun helps you build healthier, more reliable software to delight your users and keep them coming back.
More importantly, Raygun is built for teams and supports integrations for workplace software such as team chat, e.g. Slack and Hipchat, project management tools, e.g. JIRA and Sprintly, and issue trackers, e.g. GitHub and Bitbucket. Raygun gives your team peace of mind that your software is performing as you want it to—flawlessly.
How cool are real-time Slack notifications for your entire team whenever your customers are encountering bugs, issues, and crashes!
In this tutorial, I'll walk you through setting up your application with Raygun step by step.
If you have any requests for future tutorials or questions and comments on today's, please post them below. You can also reach me on Twitter @lookaheadio or email me directly.
Getting Started
One of the most powerful features of Raygun is that it works with all the major programming languages and platforms, and it's amazingly easy to integrate. Just copy and paste the code into your application and Raygun will start monitoring for errors. In the case of WordPress, they provide a pre-built plugin.
Integrating Raygun With Your Application
After signing up, you'll be presented with a short Raygun integration wizard. It starts with selecting your language of choice. Here's the initial dashboard that you'll see:
Here's an example of integrating for use with any JavaScript code or platform.
Using Raygun With JavaScript
Once you select JavaScript, you'll be shown your Application API Key (the key is the same for all platforms you choose and is application specific). For each environment or language you want to monitor, you should create a separate application to organise errors into different buckets.
Raygun is easy to use regardless of which JavaScript package management system you prefer:
For example, with Bower, run:
bower install raygun4js
From NuGet, open the console and run:
Install-Package raygun4js
But you can also just load the library from Raygun's CDN within your application:
Once you've installed the plugin, you load the configuration menu from the WordPress dashboard and provide your API key:
Within a minute, you'll start seeing errors collected in your Raygun dashboard. If not, click the Send Test Error button to trigger one.
The Raygun Dashboard
Initially, you'll see an empty dashboard:
But once you've chosen your language and integrated your application, you'll see a dashboard like this—oh, theme developers—in which Raygun helped me discover a plethora of WordPress theme code that hadn't been kept up to date with the latest versions of PHP.
Tracking Errors Across Code Deployments
When you integrate Raygun with your deployment tools, it can track errors according to specific versions of released software. This can help you identify and repair bad deployments quickly and easily:
Raygun currently lets you assign error groups to one of five statuses. These are:
Active
Resolved
Resolved In Version x.xx
Ignored
Permanently Ignored
When an error is first received, it is assigned to Active and is visible in the first tab. You can then take action to change it to another status.
For example, as soon as I activated Raygun with WordPress and discovered a plethora of theme-related PHP compatibility issues, my email queue began to fill—but this was easily resolved by asking Raygun to only notify me of new reports.
You can also filter and manage issues by status through the interface quite easily. For example, it would be easy to delete all the errors resolved in WordPress version 4.3.
Raygun Error Detailed Views
When you click on errors, Raygun shows you their detail view with stack trace and a summary of which users and browsers or devices are being affected:
In detail view, Raygun also allows you and your team to comment and discuss specific issues:
Raygun User Tracking
If you implement user tracking with your Raygun integration, you can see exactly which of your authenticated users have run into specific errors and how often:
Raygun offers easy documentation for linking error reports to the current signed-in user. Here's an example for JavaScript:
By default, Raygun4JS assigns a unique anonymous ID for the current user. This is stored as a cookie. If the current user changes, to reset it and assign a new ID you can call:
Raygun.resetAnonymousUser();
To disable anonymous user tracking, call Raygun.init('apikey', { disableAnonymousUserTracking: true });.
You can provide additional information about the currently logged in user to Raygun by calling: Raygun.setUser('unique_user_identifier');.
This method takes additional parameters that are used when reporting over the affected users. The full method signature is:
setUser: function (user, isAnonymous, email, fullName, firstName, uuid)
Managing Your Team
Raygun is built around tracking issues across development teams. Through the settings area, it's easy to add applications that you're tracking and invite team members to participate:
As mentioned above, Raygun easily integrates with other team-based tools such as chat (Slack, Hipchat, etc.), project management (JIRA, Sprintly, etc.) and issue trackers (GitHub, Bitbucket, etc.).
Helpful Customer Support
Raygun support is excellent. In addition to the web-based documentation and email welcome guides, there's helpful support personnel (like Nick) ready to guide you deeper into the service—Nick's tips and availability just popped up as I was reviewing the service:
The Raygun API
If you'd like to tailor or customize event triggers, you can post errors via the Raygun API however you'd like from your application. This can be helpful for developers wishing to integrate monitoring or specialized reporting across their services or to make the development process easier.
Raygun Pulse Real User Monitoring
Lastly, even though we talked through the setup of Raygun’s Crash Reporting software here, adding Pulse - Real User Monitoring is just as easy when adding the Raygun code snippet.
Pulse provides you with all the session data from ‘real’ users when they are interacting with your applications. This is far more powerful than traditional synthetic testing, as data is taken directly from the user’s browser, letting you see how a user’s unique device, browser, OS, location or connection speed affected the application’s performance. Problematic scripts, requests and assets can be identified as the route cause and optimizations made to improve the overall performance of your application.
Couple Pulse with Crash Reporting data and you have all the tools available to ensure your customers are receiving a great user experience.
In Summary
I hope you've found Raygun easy to use and helpful to your development requirements. To recap, here are some of the major benefits of the service:
Raygun provides a complete overview of problems across your entire development stack. Intelligent grouping of errors lets you see the highest priority issues rather than flooding you with notifications for every error.
Raygun supports all major programming languages and platforms. Every developer can use it. Developer time is expensive, so stop wasting time trying to hunt down bugs. Fix issues faster and build more features instead!
Raygun is built for teams. You can invite unlimited team members to your account—no restrictions. Raygun helps you create a team workflow for fixing bugs and provides custom notifications and a daily digest of error events for all of your team.
For large corporate entities, Raygun Enterprise can provide cloud support or the ability to securely self-host a version of the service for your needs.
When you give Raygun a try, please let us know your questions and comments below. You can also reach me on Twitter @lookaheadio or email me directly. Or, if Raygun saves you a ton of time right away, you can browse my Envato Tuts+ instructor page to read the other tutorials I've written.
Gaming technologies are constantly evolving. Nevertheless, a lot of core gameplay elements have remained unchanged for decades. High scores, leaderboards, quests, achievements, and multiplayer support are examples. If you are developing a game for the Android platform, you don't have to implement any of those elements manually. You can simply use the Google Play games services APIs instead.
In this tutorial, I'll show you how to create a simple Android game, add Google Play games services to it, and use the leaderboards and achievements APIs.
Prerequisites
To be able to follow this tutorial, you'll need the following:
An Android device that has the latest version of the Google Play services app installed.
1. Create a Simple Game
There's no fun in adding the Play games services APIs to a blank Android Studio project. Therefore, let us now create a game where the user gains points by simply clicking on a button. More precisely, the objective of the game will be to click on a button as often as possible within 60 seconds.
Step 1: Create the Game Layout
The layout of the game will have one Button widget, which the user will click to gain points, and two TextView widgets to display the score and the time left. If you put all of them inside a RelativeLayout and center them both horizontally and vertically, you should have a layout file that looks like this:
Our game will have a leaderboard screen and an achievements screen. To allow the user to navigate to those screens, add two more Button widgets at the end of the layout file.
Note that both the buttons have onClick attributes. We will create the Java methods they refer to in later steps.
Step 2: Implement the Game Logic
Inside the activity, create member variables for the gameplay widgets we defined in the layout XML file. Additionally, create an int variable for the score and a boolean variable for the game's state.
private Button mainButton;
private TextView scoreView;
private TextView timeView;
private int score = 0;
private boolean playing = false;
Initialize the widgets inside the activity's onCreate() method using the findViewById() method.
To listen for clicks on the Button widget, create and add an OnClickListener to it.
mainButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// More code goes here
}
});
We must now implement the following requirements:
The first time the user clicks on the button, the game must start.
Each subsequent click should increment the score variable.
The game must end when 60 seconds have elapsed.
We can use the playing variable to differentiate between the first click and all the subsequent clicks. To keep track of the time, we can use the abstract CountDownTimer class, which is ideal for our requirements. It has an onTick() method inside, which can accurately update timeView to display the number of seconds remaining. It also has an onFinish() method, which is called when the countdown is over.
Accordingly, add the following code to the onClick() method:
if(!playing) {
// The first click
playing = true;
mainButton.setText("Keep Clicking");
// Initialize CountDownTimer to 60 seconds
new CountDownTimer(60000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
timeView.setText("Time remaining: " + millisUntilFinished/1000);
}
@Override
public void onFinish() {
playing = false;
timeView.setText("Game over");
mainButton.setVisibility(View.GONE);
}
}.start(); // Start the timer
} else {
// Subsequent clicks
score++;
scoreView.setText("Score: " + score + " points");
}
At this point, you can run the project to play the game.
2. Add Games Services Dependencies
Configuring a project to use the Play games services APIs involves a lot of steps. By using Android Studio's Firebase Assistant, you can automate some of them.
Open the assistant window by going to Tools > Firebase. Next, in the Analytics section, click on the Log an Analytics event link. You can now connect your Android Studio project to a Firebase project by clicking on the Connectto Firebase button. Make sure that you choose the Create new Firebase project option in the dialog that pops up.
Once the connection has been established, press the Add Analytics to your app button to add all the required changes to the build.gradle files.
You must add the dependency for Play games services manually. Therefore, go to the build.gradle file of the app module and add the following compile dependency:
Furthermore, the games services APIs need an XML configuration file. Create one called games-ids.xml inside the res/values folder. We'll add content to it in a later step.
3. Register the Game
All games that use Google Play games services must be registered on the Play developer console. You'll have to pay a one-time registration fee of $25 to be able to access the console.
In the console, click on the gamepad icon to open the Game Services screen.
Next, click on the Set up Google Play game services button to start registering the game.
In the dialog that pops up, choose the second tab because our game is already using Google APIs. You must now be able to see your Firebase project's name in the list of available projects. After selecting it, choose a category for the game. For now, you can go with the Casual category.
After you press Continue, you can link Firebase Analytics and Play games services by clicking on the Link Firebase button.
Next, go to the Linked apps section to link your Android Studio project to the Play developer console. In the dialog that pops up, press the Android button and type in your project's package name in the Package name field.
Press the Save and continue button to generate a client ID for your game.
Our game has now been successfully registered with the Google Play developer console.
4. Add a Test User
The Play games services APIs will work only if your game is published on Google Play. However, to allow you to test your game, the developer console lets you associate a few test user accounts. By going to the Testing section, you can add or remove test user accounts. Make sure that you add the Google account you use on your phone or emulator here.
5. Create a Leaderboard
A leaderboard is nothing but a screen that displays the users' high scores. Play games services leaderboards allow users to see their daily, weekly and all-time high scores.
Creating a leaderboard on the Play developer console takes just a few clicks. Go to the Leaderboards section and press the Add leaderboard button. In the next screen, give a meaningful name to the leaderboard and press the Save button.
Our leaderboard is now ready.
6. Create an Achievement
Achievements are in-game awards users get for managing to do something special. A game that has a lot of achievements to unlock is usually more fun than one that doesn't. Consequently, most of the popular games on Google Play today have dozens, if not hundreds of achievements.
In this tutorial, we'll add just one achievement to our game. Its name will be Lightning Fast, and it will be unlocked when the user manages to tap the button more than 100 times in a minute. To create the achievement, go to the Achievements section and press the Add achievement button. After you type in the name and the description of the achievement, press the Save button.
7. Update the Game Configuration XML
Both the leaderboard and the achievement have unique identifiers. We must now add those identifiers to our Android Studio project, along with the application ID that was generated during the registration. You can do so by manually updating the games-ids.xml file we created earlier. However, I suggest that you use the automatically generated configuration code available in the developer console.
To get the auto-generated configuration code, you can go to either the Achievements section or the Leaderboards section, and press the Get resources link. You will see XML code that looks like this:
<?xml version="1.0" encoding="utf-8"?><!--
Google Play game services IDs.
Save this file as res/values/games-ids.xml in your project.
--><resources><string name="app_id">1234567890</string><string name="package_name">com.tutsplus.mylittlegame</string><string name="achievement_lightning_fast">HzkA4Kz04F8MRYIAze</string><string name="leaderboard_my_little_leaderboard">HzkA4Kz04F8MRYIBVU</string></resources>
Copy all the code and paste it in your project's games-ids.xml file.
8. Connect to Play Games Services
Before using the leaderboards and achievements APIs, we must create a GoogleApiClient instance and connect it to Play games services. Therefore, add a GoogleApiClient object as a member variable of your activity.
We must use the GoogleApiClient.Builder class to build the GoogleApiClient instance. While building the client, we can specify the API and API scope we are interested in by using the addApi() and addScope() methods.
Additionally, I suggest that you call the enableAutoManage() method to make sure that the client automatically manages the connection to the games services. The method, however, needs an OnConnectionFailedListener, which will be called when the connection fails. For now, we'll simply call the finish() method to close the app in case of a connection failure.
Accordingly, add the following code at the beginning of the onCreate() method:
apiClient = new GoogleApiClient.Builder(this)
.addApi(Games.API)
.addScope(Games.SCOPE_GAMES)
.enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(TAG, "Could not connect to Play games services");
finish();
}
}).build();
If you run the app now, you will be prompted to create a Gamer ID for yourself. Type in a Gamer ID of your choice and press the Sign in button.
9. Use the Leaderboard
Submitting a score to the leaderboard takes just one line of code. All you need to do is call the submitScore() method of the Games.Leaderboards class. As its arguments, it expects the GoogleApiClient instance, the ID of the leaderboard, and the score.
In our game, we must submit the score at the end of the 60 seconds. Therefore, add the following code to the onFinish() method:
To view the leaderboard, we must start a new activity with a leaderboard intent. To fetch the leaderboard intent, call the getLeaderboardIntent() method of the Games.Leaderboards class and pass the GoogleApiClient instance and leaderboard ID to it.
Our game's layout already has a button that can be pressed to open the leaderboard. The value of its onClick attribute is showLeaderboard. Therefore, add the following code to your activity:
public void showLeaderboard(View v) {
startActivityForResult(
Games.Leaderboards.getLeaderboardIntent(apiClient,
getString(R.string.leaderboard_my_little_leaderboard)), 0);
}
You can run the app now and play the game again. This time, when the game's over, your score will be submitted to the leaderboard. Here's what the default leaderboard looks like:
10. Use the Achievement
By calling the unlock() method of the Games.Achievements class, you can unlock any achievement. The method expects the GoogleApiClient instance and the ID of the achievement as its only arguments.
The Lightning Fast achievement we defined in the console must be unlocked when the user's score crosses 100 points. Therefore, right after the code to increment the score, add the following code:
The code needed to display the achievements screens is very similar to the one we wrote to display the leaderboard screen. All you need to do is pass the return value of the getAchievementsIntent() method to the startActivityForResult() method.
If you run your app and play the game again, you will see an achievement pop up when your score crosses 100 points for the first time.
Conclusion
You now know how to use the Google Play games services APIs in your Android Studio project. Play games services are not limited to the Android platform alone. They can be used just as easily on both the web and iOS platforms too. So Play games services allow you to create cross-platform games and offer consistent gaming experiences to users on multiple platforms.
To learn more about the Google Play games services APIs, you can refer to their official guides or, even better, check out some of our tutorials here on Envato Tuts+!
Gaming technologies are constantly evolving. Nevertheless, a lot of core gameplay elements have remained unchanged for decades. High scores, leaderboards, quests, achievements, and multiplayer support are examples. If you are developing a game for the Android platform, you don't have to implement any of those elements manually. You can simply use the Google Play games services APIs instead.
In this tutorial, I'll show you how to create a simple Android game, add Google Play games services to it, and use the leaderboards and achievements APIs.
Prerequisites
To be able to follow this tutorial, you'll need the following:
An Android device that has the latest version of the Google Play services app installed.
1. Create a Simple Game
There's no fun in adding the Play games services APIs to a blank Android Studio project. Therefore, let us now create a game where the user gains points by simply clicking on a button. More precisely, the objective of the game will be to click on a button as often as possible within 60 seconds.
Step 1: Create the Game Layout
The layout of the game will have one Button widget, which the user will click to gain points, and two TextView widgets to display the score and the time left. If you put all of them inside a RelativeLayout and center them both horizontally and vertically, you should have a layout file that looks like this:
Our game will have a leaderboard screen and an achievements screen. To allow the user to navigate to those screens, add two more Button widgets at the end of the layout file.
Note that both the buttons have onClick attributes. We will create the Java methods they refer to in later steps.
Step 2: Implement the Game Logic
Inside the activity, create member variables for the gameplay widgets we defined in the layout XML file. Additionally, create an int variable for the score and a boolean variable for the game's state.
private Button mainButton;
private TextView scoreView;
private TextView timeView;
private int score = 0;
private boolean playing = false;
Initialize the widgets inside the activity's onCreate() method using the findViewById() method.
To listen for clicks on the Button widget, create and add an OnClickListener to it.
mainButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// More code goes here
}
});
We must now implement the following requirements:
The first time the user clicks on the button, the game must start.
Each subsequent click should increment the score variable.
The game must end when 60 seconds have elapsed.
We can use the playing variable to differentiate between the first click and all the subsequent clicks. To keep track of the time, we can use the abstract CountDownTimer class, which is ideal for our requirements. It has an onTick() method inside, which can accurately update timeView to display the number of seconds remaining. It also has an onFinish() method, which is called when the countdown is over.
Accordingly, add the following code to the onClick() method:
if(!playing) {
// The first click
playing = true;
mainButton.setText("Keep Clicking");
// Initialize CountDownTimer to 60 seconds
new CountDownTimer(60000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
timeView.setText("Time remaining: " + millisUntilFinished/1000);
}
@Override
public void onFinish() {
playing = false;
timeView.setText("Game over");
mainButton.setVisibility(View.GONE);
}
}.start(); // Start the timer
} else {
// Subsequent clicks
score++;
scoreView.setText("Score: " + score + " points");
}
At this point, you can run the project to play the game.
2. Add Games Services Dependencies
Configuring a project to use the Play games services APIs involves a lot of steps. By using Android Studio's Firebase Assistant, you can automate some of them.
Open the assistant window by going to Tools > Firebase. Next, in the Analytics section, click on the Log an Analytics event link. You can now connect your Android Studio project to a Firebase project by clicking on the Connectto Firebase button. Make sure that you choose the Create new Firebase project option in the dialog that pops up.
Once the connection has been established, press the Add Analytics to your app button to add all the required changes to the build.gradle files.
You must add the dependency for Play games services manually. Therefore, go to the build.gradle file of the app module and add the following compile dependency:
Furthermore, the games services APIs need an XML configuration file. Create one called games-ids.xml inside the res/values folder. We'll add content to it in a later step.
3. Register the Game
All games that use Google Play games services must be registered on the Play developer console. You'll have to pay a one-time registration fee of $25 to be able to access the console.
In the console, click on the gamepad icon to open the Game Services screen.
Next, click on the Set up Google Play game services button to start registering the game.
In the dialog that pops up, choose the second tab because our game is already using Google APIs. You must now be able to see your Firebase project's name in the list of available projects. After selecting it, choose a category for the game. For now, you can go with the Casual category.
After you press Continue, you can link Firebase Analytics and Play games services by clicking on the Link Firebase button.
Next, go to the Linked apps section to link your Android Studio project to the Play developer console. In the dialog that pops up, press the Android button and type in your project's package name in the Package name field.
Press the Save and continue button to generate a client ID for your game.
Our game has now been successfully registered with the Google Play developer console.
4. Add a Test User
The Play games services APIs will work only if your game is published on Google Play. However, to allow you to test your game, the developer console lets you associate a few test user accounts. By going to the Testing section, you can add or remove test user accounts. Make sure that you add the Google account you use on your phone or emulator here.
5. Create a Leaderboard
A leaderboard is nothing but a screen that displays the users' high scores. Play games services leaderboards allow users to see their daily, weekly and all-time high scores.
Creating a leaderboard on the Play developer console takes just a few clicks. Go to the Leaderboards section and press the Add leaderboard button. In the next screen, give a meaningful name to the leaderboard and press the Save button.
Our leaderboard is now ready.
6. Create an Achievement
Achievements are in-game awards users get for managing to do something special. A game that has a lot of achievements to unlock is usually more fun than one that doesn't. Consequently, most of the popular games on Google Play today have dozens, if not hundreds of achievements.
In this tutorial, we'll add just one achievement to our game. Its name will be Lightning Fast, and it will be unlocked when the user manages to tap the button more than 100 times in a minute. To create the achievement, go to the Achievements section and press the Add achievement button. After you type in the name and the description of the achievement, press the Save button.
7. Update the Game Configuration XML
Both the leaderboard and the achievement have unique identifiers. We must now add those identifiers to our Android Studio project, along with the application ID that was generated during the registration. You can do so by manually updating the games-ids.xml file we created earlier. However, I suggest that you use the automatically generated configuration code available in the developer console.
To get the auto-generated configuration code, you can go to either the Achievements section or the Leaderboards section, and press the Get resources link. You will see XML code that looks like this:
<?xml version="1.0" encoding="utf-8"?><!--
Google Play game services IDs.
Save this file as res/values/games-ids.xml in your project.
--><resources><string name="app_id">1234567890</string><string name="package_name">com.tutsplus.mylittlegame</string><string name="achievement_lightning_fast">HzkA4Kz04F8MRYIAze</string><string name="leaderboard_my_little_leaderboard">HzkA4Kz04F8MRYIBVU</string></resources>
Copy all the code and paste it in your project's games-ids.xml file.
8. Connect to Play Games Services
Before using the leaderboards and achievements APIs, we must create a GoogleApiClient instance and connect it to Play games services. Therefore, add a GoogleApiClient object as a member variable of your activity.
We must use the GoogleApiClient.Builder class to build the GoogleApiClient instance. While building the client, we can specify the API and API scope we are interested in by using the addApi() and addScope() methods.
Additionally, I suggest that you call the enableAutoManage() method to make sure that the client automatically manages the connection to the games services. The method, however, needs an OnConnectionFailedListener, which will be called when the connection fails. For now, we'll simply call the finish() method to close the app in case of a connection failure.
Accordingly, add the following code at the beginning of the onCreate() method:
apiClient = new GoogleApiClient.Builder(this)
.addApi(Games.API)
.addScope(Games.SCOPE_GAMES)
.enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(TAG, "Could not connect to Play games services");
finish();
}
}).build();
If you run the app now, you will be prompted to create a Gamer ID for yourself. Type in a Gamer ID of your choice and press the Sign in button.
9. Use the Leaderboard
Submitting a score to the leaderboard takes just one line of code. All you need to do is call the submitScore() method of the Games.Leaderboards class. As its arguments, it expects the GoogleApiClient instance, the ID of the leaderboard, and the score.
In our game, we must submit the score at the end of the 60 seconds. Therefore, add the following code to the onFinish() method:
To view the leaderboard, we must start a new activity with a leaderboard intent. To fetch the leaderboard intent, call the getLeaderboardIntent() method of the Games.Leaderboards class and pass the GoogleApiClient instance and leaderboard ID to it.
Our game's layout already has a button that can be pressed to open the leaderboard. The value of its onClick attribute is showLeaderboard. Therefore, add the following code to your activity:
public void showLeaderboard(View v) {
startActivityForResult(
Games.Leaderboards.getLeaderboardIntent(apiClient,
getString(R.string.leaderboard_my_little_leaderboard)), 0);
}
You can run the app now and play the game again. This time, when the game's over, your score will be submitted to the leaderboard. Here's what the default leaderboard looks like:
10. Use the Achievement
By calling the unlock() method of the Games.Achievements class, you can unlock any achievement. The method expects the GoogleApiClient instance and the ID of the achievement as its only arguments.
The Lightning Fast achievement we defined in the console must be unlocked when the user's score crosses 100 points. Therefore, right after the code to increment the score, add the following code:
The code needed to display the achievements screens is very similar to the one we wrote to display the leaderboard screen. All you need to do is pass the return value of the getAchievementsIntent() method to the startActivityForResult() method.
If you run your app and play the game again, you will see an achievement pop up when your score crosses 100 points for the first time.
Conclusion
You now know how to use the Google Play games services APIs in your Android Studio project. Play games services are not limited to the Android platform alone. They can be used just as easily on both the web and iOS platforms too. So Play games services allow you to create cross-platform games and offer consistent gaming experiences to users on multiple platforms.
To learn more about the Google Play games services APIs, you can refer to their official guides or, even better, check out some of our tutorials here on Envato Tuts+!
In this series, you're learning how to use React Native to create page layouts commonly used in mobile apps. The layouts you're creating won't be functional—instead, the main focus of this series is to get your hands dirty in laying out content in your React Native apps.
If you're new to laying out React Native apps or styling in general, check out my previous tutorial:
To follow along with this series, I challenge you to try recreating each screen by yourself first, before you read my step-by-step instructions in the tutorial. You won't really benefit much from this tutorial just by reading it! Try first before looking up the answers here. If you succeed in making it look like the original screen, compare your implementation to mine. Then decide for yourself which one is better!
In this third post of the series, you'll create the following photo gallery page:
Galleries are often used to display a collection of related content in such a way that only the necessary information is presented. Most of the time this includes a photo, a title, and other relevant information.
Here are a couple of examples of this type of layout being used in the wild:
Project Setup
The first step, of course, is to set up a new React Native project:
react-native init react-native-common-screens
Once the project is set up, open the index.android.js file and replace the default code with the following:
import React, { Component } from 'react';
import {
AppRegistry
} from 'react-native';
import Gallery from './src/pages/Gallery';
export default class ReactNativeCommonScreens extends Component {
render() {
return (
<Gallery />
);
}
}
AppRegistry.registerComponent('ReactNativeCommonScreens', () => ReactNativeCommonScreens);
Create a src/pages folder and create a Gallery.js file inside it.
You'll also need the react-native-vector-icons package. This is specifically used for the icons in the footer.
npm install --save react-native-vector-icons
Open the android/app/build.gradle file and add a reference to the package:
dependencies {
//rest of the dependencies are here at the top
compile project(':react-native-vector-icons') //add this
}
Do the same with the android/settings.gradle file by adding the following at the bottom:
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
Open android/app/src/main/java/com/react-native-common-screens/MainApplication.java and import the package:
import java.util.Arrays;
import java.util.List;
import com.oblador.vectoricons.VectorIconsPackage; //add this
Lastly, initialize the package:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VectorIconsPackage() //add this
);
}
Creating the Gallery Page
Okay, now that you've tried to code the layout yourself (no cheating, right?), I'll show you how I built my implementation.
Unlike the previous two pages, the gallery page needs some images that will serve as its main content. You can go to Google and search for images or download the images from the GitHub repo. All the images that I used are labeled for reuse by their respective owners, so you can freely use them if you want. Once you have the images, save them inside the src/images directory. Due to the way the images will be laid out, all of them should have equal dimensions.
Start by creating the file (src/pages/Gallery.js) and add the boilerplate code:
import React, { Component } from 'react';
import {
StyleSheet,
View,
ScrollView,
Image,
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import Button from '../components/Button';
export default class Gallery extends Component {
...
}
This page needs a constructor() function where you define the paths to the images that you want to use. In React Native, the way you refer to images that are inside your working directory is by requiring them just like you would a JavaScript module. It's also important to note that you can't have dynamically generated image paths so you have to supply the actual path by hand.
You don't really need to define these in the state as the values won't change. You could actually define these in a separate file, import it, assign it to a variable, and then use it directly. But for the sake of simplicity, I decided to just put everything in the state.
Inside the render() method, you're going to break the trend of wrapping everything inside a ScrollView component, because the tab component at the lowermost portion of the screen should have a fixed position. This means that even if the photos goes over the available height, the tabs should still stay in place. To achieve this, use a View component to wrap everything and only wrap the collection of photos in a ScrollView. This allows you to apply the scroll only to the container of the collection of photos:
Now you may start to see a pattern here. Every time you need to use JavaScript code inside the render() function, you should create a separate function to hold that code, instead of putting it directly inside the render() function. This keeps it lean and clean.
Now let's move on to the styling. Although a ScrollView isn't used to wrap everything this time, it's important to note that you still have to supply flex: 1 to the main container so that it consumes the entirety of the available space.
The renderGallery() function is very similar to the renderWeeks() function that we used in the previous tutorial, which we used to render a calendar page. If you want a refresher on how that works, go ahead and read over the previous tutorial on calendar pages. What you need to know is that resizeMode is applied to the Image. In this case, it's set to cover, which makes the image occupy the entire available space of its container, while still maintaining its aspect ratio. This results in the image getting slightly blown up for devices with larger screens if the original image is smaller.
Finally, here is the styling for each row (item) and photo (photo). Note the use of flex: 1 on the actual photo. This is done because the Image component itself is its own container. You want the container itself to occupy half of the available space for each row—that's why a flex property should be assigned. If this isn't done, only the dimensions needed by the photo will be consumed, and the resizeMode that you added earlier won't even have an effect.
That's it! In this tutorial you learned how to implement the layout for a gallery page. We focused on how to deal with images when it comes to laying out your React Native apps. Often you have to use a combination of flex and resizeMode in order to make images flow the way you want them to. How did my solution compare to your own? Let us know in the discussion forum below.
In an upcoming tutorial, you'll learn how to implement the layout commonly used in news apps. In the meantime, check out some of our other tutorials on React Native and Flexbox!
CSS, despite its relatively low perceived skill ceiling, always seems to have a killer feature up its sleeve. Remember how media queries made responsive...
React Native, created by Facebook, lets you write native mobile apps in modern JavaScript. React Native apps will be transformed into native views specific...
Are you looking to improve your flexbox knowledge and at the same time learn how to build easily an attractive and unique layout? If so, be sure to read this...
Animations can breath life into your app and make your UIs more intuitive. In this tutorial, you'll learn how to implement different kinds of animations in...
Are you a hybrid app developer wanting to include face detection into your app, but you don't have any idea where to start? As a start, you could read An...