Introduction to Kotlin for Android Developers

At Google I/O 2017, the Android team at Google announced Kotlin as the official language for Android development. With this update, Android developers can now enjoy the features of a more modern programming language. That said, Android developers are not forced to used Kotlin and can keep using Java which is still supported.

Why using Kotlin?

  • Interoperability: Kotlin is a JVM base language, it is fully ‘inter-operable’ with Java. This means we can have Kotlin code can co-exist with Java code and vice versa.
  • Kotlin is a programming language developed by JetBrains, same company who developed IntelliJ. Android Studio IDE is based on IntelliJ.framework.
  • Modern programming language fully supported. All the features of Java 1.8 are not available when developing using Android SDK (if need to support lower version of Android).
  • Safer: Deal with null pointer at compile time. Preventing null pointer exceptions is much easier.
  • Expressiveness: Kotlin will in many case involve writing less code for solving same problem.

Before going deeper into these new features, lets start from basic.

 

Variables

Mutable Object

In Java, we would have following code to represent a mutable object of type String

String mObject = "Hello World";

 

This would be the equivalent in Kotlin:

var mObject: String = "Hello World"
  • The “var” keyword is used for mutable type object.
  • The type of the object (String) is specified before its name (mObject) You can think of it as mObject “extends” String. (e.g, mObject: String )
  • Semi-colon is optional in Kotlin.

Inferred Type

However, we can simplify the code as Kotlin can automatically Infer type of the object. Kotlin can infer the type of the object based on the value which it is set to. For example, since we are assigning the string “Hello” to the object, we don’t need to specify its Type:


var mObject = “Hello”

If now, we instead integer value “10”,  Kotlin will infer the Type of the object as Int


var mObject = 10

 

Immutable Object

An immutable object is one whose value is set once and cannot be changed. In Java, we are used to the keyword “final” to describe an immuatable object:


final String MOBJECT = "Hello World";

The “val” keyword is used to describe a immutable in Koltin:


val contantObject = "Hello World"

The general good practice is to always use immutable as far as possible. This makes code more predictable and will be important when working with Null safety feature which we will look into the next section.

 

Null safety feature

A common crash in many programming languages (including Java) is when accessing an attribute or method of a null reference. In Java this would result in a crash equivalent of a NullPointerException (NPE).  This error is also known as The Billion Dollar Mistake.

Kotlin’s type system is designed to prevent NullPointerExceptions.


val strOptional: String? = null

strOptional?.length

This is equivalent to following in Java:


if (strOptional != null)

    strOptional.length()

strOptional?.length: If and only if the object strOptional is not null, will the length method be call

Note that the “?” is required for the code/project to compile. We can use not-null assertion operator (!!) as below to force the code to compile, however in the above scenario, it would result in a NPE.


strOptional!!.length

 

Classes

Classes in Kotlin are declared using keyword class. We will create a Class which extends Activity from Android SDK.


class MainActivity: Activity {}

This would be equivalent of following in Java:


final class MainActivity extends Activity {}

Classes are closed by default which is equivalent of “final” in Java. So we won’t be able to extend the class “MainActivity” in another class. To make the class extendable, we have to explicitly declared class as open or abstract:

open class BaseActivity: Activity {}

MainActivity can extend BaseActivity of if BaseActivity class is “open”:

class MainActivity: BaseActivity {}

 

Data classes & Properties

Kotlin provide Data type classes and Properties which avoid many boilerplate which would be required in Java which creating POJO classes. Properties are equivalent to fields in Java but which includes getters and setters. However in Kotlin, we don’t need to explicitly declare getters and setters, it will be automatically created under the hood.

Let’s say we have a User model which requires the following attributes (properties in kotlin): “firstName” and “surname

public class User {
    private String firstName;
    private String surnameName;
    private String mood;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSurnameName() {
        return surnameName;
    }

    public void setSurnameName(String surnameName) {
        this.surnameName = surnameName;
    }

    public String getMood() {
        return mood;
    }

    public void setMood(String mood) {
        this.mood = mood;
    }
}

We can have the above class with only 1 line in Kotlin by using “data” class  contain firstName and lastName properties:

 data class User(val firstName: String, val lastName: String, val mood:String) 

Kotlin will generate the generic getters and setters methods under the hood for properties. That will allow us to write less and also make it easier to manage changes.

 

Instantiating an object

When instantiate an object in Kotlin, we don’t need to specify the “New” keyword each time as in Java:


val user = User("Frank", "Einstein", "happy")

 

Data classes come with some  handy functions:

  • copy() -> This is handy if we are using immutable objects.

A general practice is to use immutable objects when possible to avoid unexpected changes from other part of our code. e.g,


val user = User("Frank", "Einstein", "happy")

val userUpdated = user.copy(mood = "angry")

  • equals() -> compare all properties of object to another to make sure they are identical:
user.equals(userUpdated) // false since Frank's mood has changed

 

Functions

Let’s create a small function which takes two integers and return their sum in Java:


private void sum(int x, int y) {
    return x + y;
}

In Kotlin, functions are declared using the “fun” keyword. Parameters will be

fun sum(x: Int, y: Int): Int {
   return x + y
}

Note that for the parameter X or Y, we write the Name first, then its Type. The return type (Int) is at the end just before function body.

If the result can be calculated in a single expression we can omit the curly braces and simply specify the function body after “=” symbol as below:

fun sum(x: Int, y: Int): Int = return x + y

 

Higher-order function

Functions are first class citizen in Kotlin, which means function can be passed as variable, arguments or return from other functions. We will store same sum function into an object using lambda expressions as below:

val sum = { x: Int, y: Int -> x + y }
  • A lambda expression in Kotlin is surrounded by curly braces {}
  • the code before the “->” sign x: Int, y: Int  is parameter declaration for our function
  • the code after the “->” sign is the body of the function
  • the return type is automatically inferred with Kotlin as we already know the parameter is of type int, the expression will return and Int

 

Extension Function

Another interesting feature with Kotlin (and other modern programming language) is Extension function. If you are new to this concept, I will try to explain you the concept by using components from Android SDK.

Imagine you want to have a method for all your Activity classes. In Java, we would create a “BaseActivity” class which will have the required method implementations, and then have all of our activities extend that BaseActivity and inherit the method. Kotlin provides a special feature known as Extension Function. Instead of base calls, a static method will be created and made available to the existing Activity class from the Android SDK:

fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}
  • Note parameter default value is used for Duration parameter: duration: Int = Toast.LENGTH_SHORT
  • “this” -> refers to the Activity itself. Inside of the Extension we will have all methods which are accessible from the Activity class. For e.g getApplicationContext().

 

That works like magic. The extension function can be written anywhere in our project. I understand Kotlin basically generates a static method which is made available to actual Activity class:

class MainActivity : Activity(), MainContract.View {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        toast("this is toast from extension function")
    }

}

Extension function is very useful if we want to add a function into a code base which we cannot modify class inside of Library and Android SDK.

The Kotlin team developed a robust library for Android which implements many useful extension functions: https://github.com/JetBrains/anko which basically “adds” new features to Android Framework. It might be worth looking into.

 

Conclusion

Kotlin is similar to Java, it runs on JVM and is fully supported by Android Studio. I would say it’s a must for Android developers to give it a try. There are indeed many new features some which I covered in this blog post. I hope this is helps you started to get into Kotlin language. In future blog posts, I may go deeper inside specific features which I find useful. You can always  keep in touch by clicking on “Follow” button at the bottom right corner. Stay blessed. Cheers

MVVM – Android’s Architecture Components Part 2 – Repository Pattern

In the Part 1 of Android Architectural Component series, we have implemented two-way data binding and explained the Android Architectural components: ViewModel and Live Data, and LifeCycleOwner. However, the data layer part of the app was not complete, more specifically we did not implement the Repository Pattern. The previous example did not have a persistence solution to cache data from API. This might be required in many use cases for following reasons:

  1. User experience: making the user wait for some data is bad user experience, when possible, it needs to be avoided
  2. Data needs to be persisted: user needs to login once
  3. Waste of network resource: re-fetch same data
  4. Data lost: Flaky network can prevent you from obtaining data anytime
  5. Android OS/GC can clear data which is store in memory anytime.

In this blog post, we will go through the detail of our open source example of the MVVM android app implementing the Architectural Components guideline. Since this guideline was released only year, I did not find many open source project which is complete. Our example follows the Android App Guideline as strictly as possible and the source code is easy to understand:

Persistence data using Room DAO & LiveData

The Android Architecture Components provides a library to persist data: Room DAO. In a previous blog post, we have discussed about how to setup the Room Persistence Library: https://devanshramen.com/2017/09/20/intro-to-room-persistence-library/. Feel free to go back to this if you haven’t used Room before.

@Dao
public interface UserDao {
    
    @Query("SELECT * FROM user LIMIT 1")
    LiveData<User> getUser();

    @Insert
    (onConflict = OnConflictStrategy.REPLACE)
    void insert(User user);

    @Delete
    void delete(User user);

    @Query("DELETE FROM user")
    void deleteAll();
}

 

  • Returning LiveData

@Query(“SELECT * FROM user LIMIT 1”)
LiveData<User> getUser();

LiveData as mentioned in Part 1 of Android Architectural Component will notify any of its observers when its value changes. For instance, in our login example, the user entity will be updated when the Login API operation is successful (user will be inserted into user entity).

 

Fetching Data from Webservice

In the blog post, we were calling Login API operation inside of our ViewModel. We marked this part TODO and rightly so. Other than not providing a Persistence solution, the ViewModel is responsible for holding Data for the Activity and Fragment. Fetching data inside the ViewModel would violate the Single Responsibility Principal. This would make our app more difficult to maintain as it is scaled when new features are added. We will have a new class “UserRepository” to manage all operations which are at the “data layer”. (Note that we are using a mock api and does not include any authentication or use of token for this example in which we won’t go through this part)

 

UserRepository

The Repository class (modules if we use dagger2), will manage data which is being cached local repository or which needs to be sent to a remote repository (through web service). The UserRepository is using Room (userDao) and Retrofit for remote data.

public class UserRepository {
    UserDao userDao;
    Executor executor;

    public UserRepository() {
        this.userDao = AppDatabase.getAppDatabase(MApplication.context).userDao();
        executor = Executors.newSingleThreadExecutor();
    }

    public void clearUserCached() {
        executor.execute(() -> {
            userDao.deleteAll();
        });
    }

    public void loginUser(String email, String password) {

        getAPIService().login(new LoginRequest(email, password))
            .compose(RxUtils.applySchedulers())
            .subscribe(
                (LoginResponse response) -> {
                    executor.execute(() -> {
                        userDao.insert(response.getUser());
                    });
                },
                (Throwable e) -> {
                    e.printStackTrace();
                }
            );
    }

    public LiveData<User> getUser() {
        return userDao.getUser();  
    }
}

  • loginUser(String email, String password): 

We have now exposed a method for network call to perform the login operation inside of the UserRepository. The key point here is that when the operation is successfully we will update the RoomDao:

executor.execute(() -> {
    userDao.insert(response.getUser());
});

(note that the i/o operation for Room needs to be done in background thread – thereby use of executor)

 

  • LiveData<User> getUser(): 

Our LoginViewModel will setup its UserResponse LiveData attribute with this method such that the LiveData for User Entity in Room DAO will be used to detect if user data was updated.

 

  • Optimise code with Dependency Injection:

Note that we can optimise this class for scalability by using Dependency Injection to provide for UserDAO with a single implementation and thereby avoid some code duplication.

 

Updated LoginViewModel

public class LoginViewModel extends ViewModel {

    // Create a LiveData
    private LiveData<User> userResponse;

    public final ObservableField<String> email = new ObservableField<>();
    public final ObservableField<String> password = new ObservableField<>();

    public final ObservableField<String> errorEmail = new ObservableField<>();
    public final ObservableField<String> errorPassword = new ObservableField<>();

    UserRepository userRepository;

    public LoginViewModel() {
        userRepository = new UserRepository();
        userResponse = userRepository.getUser();
    }

    public LiveData<User> getUser() {
        return userResponse;
    }
    
    public void onBtnLoginClick() {
        if (validateInputs()) {
            userRepository.loginUser(email.get(), password.get());
        }
    }
   .
   .
   .
}

We were previously using an observable on LoginResponse. This has been updated to use LiveData and the UserRepository which has the implementation for calling login API and which can talk to UserDao. Note that the updated ViewModel will not have any implementation for fetching cached data or data from a Web service.

  • Subscribe to UserDao from UserRepository

The method getUser() from UserRepository returns the LiveData<User> from UserDao:

public LoginViewModel() {
    userRepository = new UserRepository();
    userResponse = userRepository.getUser();  
}

 

The LoginViewModel exposes the LiveData to the LoginActivity so that the Activity can take UI actions should the data in UserDao be updated:

public LiveData<User> getUser() {
    return userResponse;
}

 

  • Calling Login method from UserRespository: 
public void onBtnLoginClick() {
    if (validateInputs()) {
        userRepository.loginUser(email.get(), password.get());
    }
}

 

LoginActivity – User Observer

The Login will remain same as before but now we will get user object instead of LoginResponse. It will also be notified in theory of changes from UserDAO instead of actual Login API.

// The observer updates the UI when UserDAO is updated
mViewModel.getUser().observe(this, userResponse -> {
    if (userResponse != null) {
        Toast.makeText(LoginActivity.this, "Login success", Toast.LENGTH_SHORT).show();

        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();

    } else {
        Log.d("LoginActivity", "value user is null");
        // Show ERROR
    }
});

 

MainActivity

For the sake of the example, we have added a MainActivity class which will display the user’s details. The data will be retrieved directly from Room UserDAO without requiring to make another API call to fetch data. It also shows that the data was successfully cached.

  • Display cached User Details: 
// The observer updates the UI to display prefetched user details
mViewModel.getUser().observe(this, userResponse -> {

    if (userResponse != null) {
        binding.txtWelcome.setText("Welcome " + userResponse.getFirstName() + " " + userResponse.getLastName()
                + "\n\n" + "You are more than a " + userResponse.getJobTitle());

    } else {
        logoutUser();
    }
});

 

MainViewModel

The MainViewModel will also make use of the same method (userRepository.getUser()) from UserRepository to get cached user:

public class MainViewModel extends ViewModel {
    private UserRepository userRepository;

    public MainViewModel() {
        userRepository = new UserRepository();
    }

    public LiveData<User> getUser() {
        return userRepository.getUser();
    }

    public void clearUserData() {
        userRepository.clearUserCached();
    }
}

Conclusion:

That’s it, we have now implemented the Repository Pattern and completed the Part 2 of our MVVM using new Android Architectural components. Please find the full project source below:

https://github.com/devansh-ramen/Android-App-Architecture-MVVM

Do not forget hit the “Star” button on the project repository:

If you have any queries, feel free to post a comment in the Comment section below, I will respond to you as soon as possible.

 

Sharing data between your Apps on Android using Content Provider

Hello in this blog post we are going to talk about how you can share database or files between your apps on Android. This situation can be useful if for e.g, you have Widget App and another Launcher App which need to share same database. Or you have many applications which need consume similar set of data. Another possible scenario is if you want to share some important data which resides in your application to any other apps who may need it. You may already be consuming data from other apps (e.g, from Android OS, to access contacts, media files or calendar events). For all of the above scenerios, the Android SDK came up with “ContentProvider” as a solution.

ContentProvider is appropriate when you want to share your database or flat files between Android apps. Under the hood, Content Provider will a set the implementation for performance and security optimization. On implementation level, of the ContentProvider abstract class, we will need to override required abstract methods to provide our own implementation on how to perform CRUD operation (create, read, update, delete operation) . And our ContentProvider can be consumed through ContentResolver. I will provide you the full source and in the blog post I will go through the concepts and implementations.

Creating your own Provider to share database

In our example, we have an app which wants to share an SQLite database to other apps. We will do so by creating our own Content Provider share the “Phonebook” table. The Phonebook table contains person’s name and their corresponding phone number. I will assume you already know how to setup SQLite database here.

ContentProvider – Abstract methods to be implemented

ContentProvider is an abstract class and we will be required to implement the following methods to allow other consumers to access/update our PhoneBook table:

  • OnCreate() method – Prepares the content provider

In the OnCreate method, we can create an instance of our SqliteDatabase such that other methods will get an instance of dbManager to insert/delete/update objects.

private SqliteDatabaseManager dbManager;
@Override
public boolean onCreate() {
 dbManager = new SqliteDatabaseManager(getContext());
 return false;
}
  • getType() – Returns the MIME type for this URI
  • query() method – For any Query request
public Cursor query(@NonNull Uri uri, String[] projections, String selection, String[] selectionArgs, String sortOrder) {
}
  • insert() method – inserting into the Phonebook table. ContentValues will contain the data to be inserted. E.g, person’s name and phonenumber
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
}

Inside the insert method, we can insert the data into SQLite database.

  • delete() method – deleting object from Phonebook table
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
}
  • update() method – updating object from Phonebook table
@Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
}

Important methods:

URIMatcher – To deal with multiple URIs Android provides the helper class UriMatcher – https://developer.android.com/reference/android/content/UriMatcher.html

Contract Class – ContractPhonebook class

This class that we coded is also known as a contract class. A contract class explicitly specifies the schema (how a database and its tables are organized).

Exposing our ContentProvider – AndroidManifest.xml

We will need to declare “Provider” element to expose our ContentProvider inside the application tag in our AndroidManifest.xml file:

Refs: https://developer.android.com/guide/topics/manifest/provider-element.html

<permission android:name="com.devanshapps.programmingreferences.READ_DATABASE"
    android:protectionLevel="normal" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name="com.devanshapps.programmingreferences.MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <provider
        android:authorities="com.devanshapps.programmingreferences.Phonebook"
        android:name="com.devanshapps.programmingreferences.PhonebookProvider"
        android:permission="com.sqisland.android.protected_provider.ACCESS_DATA"
        android:exported="true"/>
</application>

Noticed that we have created our Permission with protectionLevel = “signature”:

<permission android:name="com.devanshapps.programmingreferences.READ_DATABASE"
    android:protectionLevel="signature" />

So any other apps which want to access our ContentProvider will get this permission. With android:protectionLevel=”signature”, we will only allow applications which use the same “keystore” to be able to obtain this permission. Basically we will have this permission if we want to limit access to e.g, only our own applications signed with same Keystore.

Using ContentResolver to insert data

String AUTHORITY = "com.devanshapps.programmingreferences.Phonebook";
String PATH  = "/phonebook";
Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + PATH);
ContentValues values = new ContentValues();
values.put("person_name", "Devansh Ramen");
values.put("phone_number", "12300000");
Uri mUri = getContentResolver().insert(CONTENT_URI, values);

The ContentProvider can be accessed with any other applications such that we are able to perform CRUD operation on the PhoneBook table.

Example Source code

Please find the full source code as discussed in the blog post on . github: https://github.com/devansh-ramen/Android-ContentProvider-Example

Conclusion

I hope you have learned when you will need to use Content Provider and how to create your own content providers or consume those from other apps on android. If you have any question or helpful information, we can “share” through the Comment Section below! 😉 Please subscribe for more post. Catch you in next one!! Happy coding

Reference:

https://developer.android.com/guide/topics/providers/content-provider-creating.html

Intro to Room Persistence Library

In almost every mobile app, we need to cache/save data on the client side. You want your data to be saved in a reliable and most manageable way.

In Google I/O 2017, Google introduced Room persistence layer which is an abstraction layer over SQLite. Essentially, it would allow you to map java objects into SQLite tables/fields instead of having to write complex SQL/SQLite boilerplates. A lot of useful third party abstraction like GreenDao, Ormlite were already available before that. However, if you need an “SQL” based database on Android, Room is probably the most reliable solution now. (If you don’t need SQL based, e.g, you have few or no relationship, you can check Realm mobile database)

Without further due, let see how we can use this new Library which comes in-built with Android SDK latest version.

So, you will have to set the Compile SDK to latest version in your app.gradle:

compileSdkVersion 26

Add Room Dependencies

compile 'android.arch.persistence.room:runtime:1.0.0-alpha8'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-alpha8'

Annotate a model as Entity

Using annotation you can persist any of your models to Room.

@Entity
public class User {

    @PrimaryKey
    private String userId;

    private String firstName;
    private String lastName;
    private String nickname;
}

We added 2 annotations “Entity” for the Model which we want to persist, we also need to specify the “Primary key” with any one field. So far so good.

Create DAO interface

This is where you define the list of methods (operations) which can be done on your entity. If you have used, “Retrofit”, it is similar to API service where we define all API methods. Room also uses Annotations for us to define list of operations which we can perform on the object. E.g, we want to insert/delete a User object.

@Dao
public interface UserDao {
    @Insert
    void insert(User item);

    @Insert
    void insertAll(List<User> items);

    @Delete
    void delete(User item);

    @Query("DELETE FROM user")
    void deleteAll();

    @Query("SELECT * FROM user")
    List<User> getListUser();
}

We can use “Insert” and “Delete” annotations for inserting and deleting. To fetch users data from DB, we will need to use “Query”. We will have to write a small SQL but the good thing here is that Room will tell you if there is any error in your SQL at compile-time.

Create app Database class

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {

    private static AppDatabase INSTANCE;

    public static AppDatabase getDatabase(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database")
                    .allowMainThreadQueries() // Avoid calling main thread if large dataset
                    .fallbackToDestructiveMigration() // Add custom migration when needed
                    .build();
        }
        return INSTANCE;
    }

    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            // Since we didn't alter the table, there's nothing else to do here.
        }
    };

    public static void destroyInstance() {
        INSTANCE = null;
    }
    public abstract UserDao userDao();
}

You can create a static instance of your Database inside you Application class

AppDatabase.getDatabase(context);
UserDao userDao = AppDatabase.getDatabase(context).userDao();

Or if you are using Dagger 2:

@Module
public class DatabaseModule {

    public DatabaseModule() {}

    @Singleton
    @Provides
    public AppDatabase getDBInstance(Context context) {
        return AppDatabase.getDatabase(context);
    }

    @Singleton
    @Provides
    public UserDao getUserDao(Context context) {
        return getDBInstance(context).userDao();
    }
}

UserDao – Insert/Delete/Select User objects

userDao.insert(new User());

We can access all of the methods we have defined inside UserDao interface so that we save or get data to/from Room Database.

Note that I have allow transactions on Main Thread (“allowMainThreadQueries” inside AppDatabase class). However if you have bigger database transaction, e.g, you can use “RxJava” for threading.

This was a basic introduction to Room to get started. We haven’t used any relationship and we are only storing one table. Something to lookup to maybe in another blog post. If you have any queries, please let me know. Enjoy!! Happy coding!!!

Regards,

Material Design: the Shared View Transition

Material design is a comprehensive guide for visual, motion, and interaction design across platforms and devices. Android now includes support for material design apps. To use material design in your Android apps, follow the guidelines defined in the material design specification and use the new components and functionality available in Android 5.0 (API level 21) and above.

Why Material design?

Following Google Material Design guidelines is trivial to make sure your app is up-to-date with latest trends and is following proper design standards. Android users are also getting used to Material Design apps and will thus be feel more at ease with your apps.

Shared View Transition

Shared View Transition is one of guidelines mentioned from Material design when dealing with transitions between Activities (different screens). Essentially, you will only use this if you have some Views (images) which are present in both the current activity (screen) and the next activity (screen).

On implementation level, you only need to specify common Views (e.g an imageView) which is visible in current and next Activity (screen). Android will perform a transformation (e.g transition and scaling) such that the images will seem to move from the current activity to the next. Let’s see an example below before we continue on its purpose:

Purpose

The aim behind the share view transition is make the transition look seamless. E.g when loading a new Activity (screen), we may need to fetch some data from API and/or show a different Layout/UI. Showing a Slide in/Fade transition is an easy way hide this process but this transition is a bit rough. The user will feel like he going to a new screen. In worse case, he may even think he is going to a new app!

To sum it up, the Shared View Transition blends in the transition from one to another screen by sharing common Views or images. There is a lot more to read on the Google Material Design website with regards to transition: https://material.io/guidelines/motion/material-motion.html#material-motion-implications-of-motion. Please have a look if you haven’t already.

Implementing

Let’s go ahead with it’s implementation. Let’s say we want to share an ImageView between two Activities. (Note that that it could be any View e.g, a RelativeLayout.)

Add Transition Name to the layout files

  • Current Activity Layout – add transitionName for the ImageView which should have transition effect
<ImageView
    android:id="@+id/img_calling_transition"
    android:layout_width="90dp"
    android:layout_height="50dp"
    android:layout_alignParentTop="true"
    android:transitionName="my_img_transition"/>
  • Next/Calling Activity Layout – add same transitionName for corresponding ImageView which should receive the transition:
<ImageView
    android:id="@+id/img_receiving_transition"
    android:layout_width="120dp"
    android:layout_height="80dp"
    android:layout_alignParentBottom="true"
    android:transitionName="my_img_transition"/>

Pass Scene to Intent (in Java)

“StartActivity” call with ActivityOptionsCompat bundle. Create a Scene passing in View which will be transition along with the transition name.

    ActivityOptionsCompat options = ActivityOptionsCompat.
            makeSceneTransitionAnimation(getActivity(), (View) imgCallingTransition, "my_img_transition");

    Intent intent = new Intent(getActivity(), MgProgressStep2Activity.class);
    this.startActivity(intent, options.toBundle());

That’s it. You are done already.

Conclusion

The shared view transition is very easy to implement. Behind the scene, android does the transformation ‘effect’ for us such that the ImageView from the first activity is transformed according to the size and position of the new ImageView in the calling activity and we have “Shared” image transition effect. The result is a more seemless transition which result from applying shared view effect.

This post was quite basic but I feel like this transition is a bit underused (at least in my past projects). Shared View transitions is recommended by material design specifications and is easy to implement transition. So go ahead and use it when applicable. Thanks. Leave a comment if you got any issue with it’s implementation. Click on follow button on side menu to get notified for next post.

Cheers

Catch you in the next one.