It's a 4-th part of series of tutorials about Android Wear development. I recommend you to read previous parts, if you are new to Android development. You can find link at the right of this article.

One of the main problems of development for regular Android handheld devices is fragmentation. Developers have to make their applications suitable for a huge number of various sizes and screen resolutions, as well as for different versions of Android.

Though the Android Wear platform is relatively new (less than a year) and has less than 10 models of devices,some fragmentation already exists. These are:

2 versions of Android Wear firmware(4.4.2W и 5.0)

Different form factors of screens(round and square)

Not all Android watches support different sensors

In this tutorial we will learn how to make our application look good on round and square watches.

Problem

Solution

When you create layouts for Android Wear apps, you need to take into account devices with square and round screens. Any content placed near the corners of the screen may be cropped on round Android Wear devices, so layouts designed for square screens do not work well on round devices.For example, you can see on screenshot how application with the same layout looks on square and round screen.The text does not display correctly on devices with round screens.

There are two different approaches to solve this problem:



1. WatchViewStub

WatchViewStub

Add WatchViewStub as the main element of your activity's layout Specify a layout xml file for square screens with the rectLayout attribute of WatchViewStub Specify a layout xml file for round screens with the roundLayout attribute of WatchViewStub .

Google added a view called, which allows you to inflate one of two different layouts, depending on whether your app is running on a square or round device.To use this class for handling different screen shapes:

Your activity's layout xml file should look like this:

<android.support.wearable.view.WatchViewStub xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/watch_view_stub" android:layout_width="match_parent" android:layout_height="match_parent" app:rectLayout="@layout/rect_activity_main" app:roundLayout="@layout/round_activity_main" tools:context=".MainActivity" tools:deviceIds="wear"/>

Then create different layout definition files for square and round screens. For this example, you need to create the files:

res/layout/rect_activity_main.xml

res/layout/round_activity_main.xml.

You define these layouts in the same way that you create layouts for handheld apps, but taking into account the constraints of wearable devices. The system inflates the correct layout at runtime depending on the screen shape.



Example

Wearable App

Wear

"API:20 Android 4.4 (Kitkat Wear)"

As usual, part of routine work for us will be done by Android Studio.In order to learn how to work with different screens we will create new sample project named. We will chose onlyas form factor andas SDK for API. The most important window of creating project will be the last one:

As you can see on screenshot Android Studio will automatically create two xml-files with layouts for round ( round_activity_main ) and square( rect_activity_main ) screens. Also it will create activity_main layout with already inserted WatchViewStub .

There's one problem, because these layouts aren't inflated until the WatchViewStub defines screen type, you can't access its child's views yet. Because of that, you should attach an onLayoutInflatedListener to WatchViewStub , which will be called when the appropriate inner layout has been inflated. And in method onLayoutInflated() you can get access to inner views.

If you open Main Activity of Wearable App , then you will see that Android Studio did this work for you:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub); stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() { @Override public void onLayoutInflated(WatchViewStub stub) { // Now you can access your views TextView tv = (TextView) stub.findViewById(R.id.text); } }); }

2. Box Inset layout

You typically use the first approach when you want your app to look different depending on the shape of the device screen. But sometimes you want to use a similar layout on both screen shapes without having views cropped near the edges of round screens.

For this wearable support library provides a new layout manager called box inset layout. It extends frame layout and allows developers to use the same layout on both square and round screens. ( FrameLayout is the simplest type of layout. It is a layout manager which draws all child elements on top of each other. This allows to create nice visual effects.)

The gray square in figure below shows the area where BoxInsetLayout can automatically place its child views on round screens after applying the required window insets:



Example

In this example, we have a text view at the top, a cancel button at the right bottom, and an OK button at the left bottom. The same layout is used on a round screen. We want the view elements to be placed on the edge of the circle, not the square. This is achieved by adding the wearable layout_box attribute to your layout. It defines the set of edges: left, top,right and bottom to be aligned with the inside the box of the circle. This makes it easy to have backgrounds bleed into a full circle, but contain other elements to non-clipped areas. On a square screen the layout box parameter is ignored.

Before studying the layout file for this sample, you can notice that we used custom background and icons for ok and cancel buttons. In Android they represent resources, as well as strings("Title" in this example).

Resources in Android

Resources, like images, strings, styles and XML configuration files, are kept separate from the source code in Android applications. Resource files must be placed in the /res directory of your application in a predefined sub-folder. The specific sub-folder depends on type of resource which is stored.

Resources that we need in this tutorial:

Drawables ( /res/drawables ): images (e.g., png or jpeg files) or XML files which describe a Drawable object . Values ( /res/values ): Some simple values,like strings, colors, dimensions, styles and static arrays of strings or integers via XML files. By convention each type is stored in a separate file, e.g., strings are defined in the res/values/strings.xml file. Layout ( /res/layout ): XML files with layout descriptions are used to define the user interface for Activities and fragments.

This resource structure used for flexibility of applications. For example if we want your application to support few languages, we don't have to translate every element in application. We just need to add one more strings.xml.

Every resource gets an ID assigned by the Android. The gen directory in an Android project contains the R.java file which contains all these generated values. These references are static integer values. If you add a new resource file, the corresponding reference is automatically created in a R.java file.

Please notice: don't change R.java file!

The Android system provides methods to access the corresponding resource files via these Id s.

For example, to access a String with the R.string.yourString_ID in your source code, you would use the getString(R.string.yourString) method defined in the Activity class .

Please notice: If you want to know more about resources, go to page of Google official documentation about resources

For our example, add to res/values/strings.xml element:

<string name="title">Title</string>

You can add or edit strings or configure translations in translation editor provided by Android Studio:



Add these images in you project to res/drawable folder:

Cancel button







Ok button







Background



Layout of our example shoud look like this:

<?xml version="1.0" encoding="utf-8"?> <android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@drawable/background" android:layout_height="match_parent" android:layout_width="match_parent" android:padding="15dp" android:id="@+id/box"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" app:layout_box="all"> <TextView android:gravity="center" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="@string/title" android:textSize="20sp" android:textColor="@color/black" /> <ImageButton android:background="@null" android:layout_gravity="bottom|start" android:layout_height="50dp" android:layout_width="50dp" android:src="@drawable/ok" /> <ImageButton android:background="@null" android:layout_gravity="bottom|end" android:layout_height="50dp" android:layout_width="50dp" android:src="@drawable/cancel" /> </FrameLayout> </android.support.wearable.view.BoxInsetLayout>

Pay attention to these attributes:

android:padding (of BoxInsetLayout ) ="15dp"

This attribute assigns padding to the BoxInsetLayout element. Because the window insets of on round devices are larger than 15dp, this padding only applies to square screens .





(of ) This attribute assigns padding to the element. Because the window insets of on round devices are larger than 15dp, this padding only applies to . android:padding (of Frame Layout ) ="10dp"

This attribute assigns padding to the inner FrameLayout element. This padding applies to both square and round screens. The total padding between the buttons and the window insets is 25 dp on square screens (15+10) and 10 dp on round screens

.

(of ) This attribute assigns padding to the element. This padding applies to both square and round screens. The total padding between the buttons and the window insets is on square screens (15+10) and on round screens . app:layout_box="all"

This attribute ensures that the FrameLayout element and its children are boxed inside the area defined by the window insets on round screens. This attribute has no effect on square screens.

Thanks to these attributes our application will look good on round watches:

**

Logging

Logging is one of the means of tracking events that happen when your app is running. The Android’s developers add logging calls to their code to indicate that certain events have occurred. An event is described by a descriptive message which can optionally contain variable data (i.e. data that is potentially different for each occurrence of the event). Events also have an importance level which developers ascribe to the event.

To write log statements, we will use the android.util.Log class with Log.d() method. It means that our events have «debug» importance level. Android system creates lots of logs, that is why we need to filter all of them to find what we need. To do this we use attribute "tag" of Log.d() method.

Declare string constant "TAG" with value "MAIN_ACTIVITY" in MainActivity :

public static final String TAG = "MAIN_ACTIVITY";

Now we can call method Log.d(TAG, message_of_event) .

For example: we are going to monitor every time our application is run. To do so we will add this string in the beginning of onCreate() method:

Log.d(TAG,"Activity has started")

How to detect that your activity is running on a round screen?

Your View can request an object of WindowInsets class, which contains a specific set of insets (boundaries) of the window contents . If you want the window border to be represented for a View in custom way(customized by you), use the class onApplyWindowInsetsListener and its method onApplyWindowInsets . That will tell you the shape of the screen and a lot of other information about insents(for example, on the moto 360 it tells you that the bottom inset is 30 pixels)

To detect the shape of the screen your app will output into log shape of screen(round or not round). To do that:

Retrieve BoxInsetLayout by findViewId(R.id.box) into variable mainView . Set on it onApply WindowsInsetsListener in method onApplyWindowInsents we add check of property «insents.isRound()» and output in logs appropriate message.

Your code should look like this:

Now if we run our application and filter all messages in logs by " MAIN_ACTIVITY" tag, then we will see a message that the app started and type of screen(round or not). I created a watch emulator with squre screen and used it for testing this app. You can see the result on the screenshot:



Please notice: you can download complete project by this link.



Sum up

In this tutorial we studied how to make apps look great on round and square screens, Also we learned what is resources in Android and why is logging so important for Android developerPlease don't hesitate to ask any questions and leave feedback. Thank you for reading.