Swipeable RecyclerView in Android using Kotlin.

Introduction:

Let’s, first of all, see what is swipeable. Swipeable in terms of swiping items or elements both right and left side, to perform specific operations on it as shown in the sample output below. Before starting, let’s see what is Recycler View and when it should be used.

What is Recycler view?

Many apps need to display UI elements based on large data sets, or data that frequently changes. For example, a music app might need to display information about thousands of albums, but only a dozen of those albums might be on-screen at a time or an app like Gmail, which has lots of emails with different categories like sent and received etc. If the app created UI widgets for each of those albums or E-mails, the app would end up using a lot of memory and storage, potentially making the app slow and crash-prone. On the other hand, if the app created UI widgets each time a new album scrolled onto the screen and destroyed the widgets when it scrolled off, that would also cause the app to run slowly, since creating UI objects is a resource-intensive operation.

For this type of situations, we can use Recycler view, instead of List views to handle a large amount of data.

Why Swipeable Recycler View over simple one?

With swipeable recycler view, it would be easy to provide specific functionalities like edit an element or delete an element permanently etc.

Sample output:

Sample project: SwipableRecyclerView

Sample APK: Swipable RecyclerView_1.0

Seems Interesting right? So let’s get started!

Creating a New Project:

Open your Android Studio & create a new Project. For Kotlin support, check the checkbox of Kotlin support. Now give name whatever you like and We’ll keep all the things by default and clicked finish.

Adding Dependencies to Gradle:

To create recycler view, add following dependencies.

compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:design:25.3.1'

We don’t need any permissions from the user for Recycler view.

If your Android studio is not updated (lower version than 3.0), then you have to manually configure Kotlin. So add following line to your dependencies of a project (NOT module as above).

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

So the whole file will look like:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.1.2-4'
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Now rebuild your project. Thus you’ve configured Kotlin manually! Now let’s go ahead.

Now add following `Kotlin` code in your main activity.

MainActivity.kt

package com.example.admin.swipablerecyclerview

import android.content.DialogInterface
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.support.design.widget.FloatingActionButton
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.helper.ItemTouchHelper
import android.view.View
import android.view.ViewGroup
import android.widget.EditText

import java.util.ArrayList

class MainActivity : AppCompatActivity(), View.OnClickListener {

    private val names = ArrayList<String>()
    private var adapter: DataAdapter? = null
    private var recyclerView: RecyclerView? = null
    private var alertDialog: AlertDialog.Builder? = null
    private var et_name: EditText? = null
    private var edit_position: Int = 0
    private var view: View? = null
    private var add = false
    private val p = Paint()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initViews()
        initDialog()
    }

    private fun initViews() {
        val fab = findViewById(R.id.fab) as FloatingActionButton
        fab.setOnClickListener(this)
        recyclerView = findViewById(R.id.card_recycler_view) as RecyclerView
        recyclerView!!.setHasFixedSize(true)
        val layoutManager = LinearLayoutManager(applicationContext)
        recyclerView!!.layoutManager = layoutManager
        adapter = DataAdapter(names)
        recyclerView!!.adapter = adapter
        names.add("Kaushal")
        names.add("Alex")
        names.add("Ram")
        names.add("Abhishek")
        names.add("Narendra Modi")
        adapter!!.notifyDataSetChanged()
        initSwipe()

    }

    private fun initSwipe() {
        val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {

            override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
                return false
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                val position = viewHolder.adapterPosition

                if (direction == ItemTouchHelper.LEFT) {
                    adapter!!.removeItem(position)
                } else {
                    removeView()
                    edit_position = position
                    alertDialog!!.setTitle("Edit Name")
                    et_name!!.setText(names[position])
                    alertDialog!!.show()
                }
            }

            override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {

                val icon: Bitmap
                if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {

                    val itemView = viewHolder.itemView
                    val height = itemView.bottom.toFloat() - itemView.top.toFloat()
                    val width = height / 3

                    if (dX > 0) {
                        p.color = Color.parseColor("#388E3C")
                        val background = RectF(itemView.left.toFloat(), itemView.top.toFloat(), dX, itemView.bottom.toFloat())
                        c.drawRect(background, p)
                        icon = BitmapFactory.decodeResource(resources, R.drawable.ic_edit_white)
                        val icon_dest = RectF(itemView.left.toFloat() + width, itemView.top.toFloat() + width, itemView.left.toFloat() + 2 * width, itemView.bottom.toFloat() - width)
                        c.drawBitmap(icon, null, icon_dest, p)
                    } else {
                        p.color = Color.parseColor("#D32F2F")
                        val background = RectF(itemView.right.toFloat() + dX, itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat())
                        c.drawRect(background, p)
                        icon = BitmapFactory.decodeResource(resources, R.drawable.ic_delete_white)
                        val icon_dest = RectF(itemView.right.toFloat() - 2 * width, itemView.top.toFloat() + width, itemView.right.toFloat() - width, itemView.bottom.toFloat() - width)
                        c.drawBitmap(icon, null, icon_dest, p)
                    }
                }
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
            }
        }
        val itemTouchHelper = ItemTouchHelper(simpleItemTouchCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    private fun removeView() {
        if (view!!.parent != null) {
            (view!!.parent as ViewGroup).removeView(view)
        }
    }

    private fun initDialog() {
        alertDialog = AlertDialog.Builder(this)
        view = layoutInflater.inflate(R.layout.dialog_layout, null)
        alertDialog!!.setView(view)
        alertDialog!!.setPositiveButton("Save") { dialog, which ->
            if (add) {
                add = false
                adapter!!.addItem(et_name!!.text.toString())
                dialog.dismiss()
            } else {
                names[edit_position] = et_name!!.text.toString()
                adapter!!.notifyDataSetChanged()
                dialog.dismiss()
            }
        }
        et_name = view!!.findViewById(R.id.et_name) as EditText
    }

    override fun onClick(v: View) {

        when (v.id) {
            R.id.fab -> {
                removeView()
                add = true
                alertDialog!!.setTitle("Add Name")
                //   et_country.setText("");
                alertDialog!!.show()
            }
        }


    }

}

Related:

Drag and Swipe with RecyclerView

Swipeable RecyclerView in Android

ItemDecoration in RecyclerView in Android

Parse JSON Data using Retrofit and Rxjava in android and display in Recyclerview using cardview

Parsing the Json Data using Android Retrofit Api and display in Recyclerview with Cardview

Note the method named,onSwiped() here we are detecting swipe on the recycler view item and according to the direction of the swipe, we’ll decide the action: delete or.edit

And using the method named method,onChildDraw() we can draw the background of the element when it is swiped. We are creating an alert dialog when the user clicks on floating action button, for asking new name to be entered.When the user enters the new name we will notify recycler view to add that new name using method notifyDataSetChanged().

Here is our DataAdapter class.

DataAdapter.kt

package com.example.admin.swipablerecyclerview

/**
 * Created by ADMIN on 5/26/2017.
 */

import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView

import java.util.ArrayList

internal class DataAdapter(private val names: ArrayList<String>) : RecyclerView.Adapter<DataAdapter.ViewHolder>() {

    override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): DataAdapter.ViewHolder {
        val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.row_layout, viewGroup, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {

        viewHolder.tv_names.text = names[i]
    }

    override fun getItemCount(): Int {
        return names.size
    }

    fun addItem(country: String) {
        names.add(country)
        notifyItemInserted(names.size)
    }

    fun removeItem(position: Int) {
        names.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, names.size)
    }

    internal inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        var tv_names: TextView

        init {

            tv_names = view.findViewById(R.id.tv_names) as TextView
        }
    }
}

Using this we are setting data items to recycler view. Below are the necessary layout files.

row_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_marginBottom="1dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
 
        <TextView
            android:id="@+id/tv_names"
            android:layout_marginTop="15sp"
            android:layout_marginBottom="15sp"
            android:layout_gravity="center"
            android:textSize="21sp"
            android:paddingTop="30sp"
            android:textAlignment="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold" />
</LinearLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.admin.swipablerecyclerview.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/card_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:src="@drawable/ic_add"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="29dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_marginEnd="27dp" />

</RelativeLayout>

dialog_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/et_name"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

Now run the app and you will able to see the recycler view with different names. Swipe left to delete and swipe right to edit the swiped item.

For more information: https://www.mytrendin.com/swipeable-recyclerview-android-using-kotlin/

Patchwork Plaid — A modularization story

Patchwork Plaid — A modularization story

Illustrated by Virginia Poltrack

How and why we modularized Plaid and what’s to come

This article dives deeper into the modularization portion of Restitching Plaid.

In this post I’ll cover how we refactored Plaid away from a monolithic universal application to a modularized app bundle. These are some of the benefits we achieved:

  • more than 60% reduction in install size
  • greatly increased code hygiene
  • potential for dynamic delivery, shipping code on demand

During all of this we did not make changes to the user experience.

A first glance at Plaid

Navigating Plaid

Plaid is an application with a delightful UI. Its home screen displays a stream of news items from several sources.
News items can be accessed in more detail, leading to separate screens.
The app also contains “search” functionality and an “about” screen. Based on these existing features we selected several for modularization.

The news sources, (Designer News and Dribbble), became their own dynamic feature module. The about and search features also were modularized into dynamic features.

Dynamic features allow code to be shipped without directly including it in the base apk. In consecutive steps this enables feature downloads on demand.

What’s in the box — Plaid’s construction

Like most Android apps, Plaid started out as a single monolithic module built as a universal apk. The install size was just under 7 MB. Much of this data however was never actually used at runtime.

Code structure

From a code point of view Plaid had clear boundary definitions through packages. But as it happens with a lot of codebases these boundaries were sometimes crossed and dependencies snuck in. Modularization forces us to be much stricter with these boundaries, improving the separation.

Native libraries

The biggest chunk of unused data originates in Bypass, a library we use to render markdown in Plaid. It includes native libraries for multiple CPU architectures which all end up in the universal apk taking up around 4MB. App bundles enable delivering only the library needed for the device architecture, reducing the required size to around 1MB.

Drawable resources

Many apps use rasterized assets. These are density dependent and commonly account for a huge chunk of an app’s file size. Apps can massively benefit from configuration apks, where each display density is put in a separate apk, allowing for a device tailored installation, also drastically reducing download and size.

Plaid relies heavily on vector drawables to display graphical assets. Since these are density agnostic and save a lot of file size already the data savings here were not too impactful for us.

Stitching everything together

During the modularization task, we initially replaced ./gradlew assemble with ./gradlew bundle. Instead of producing an Android PacKage (apk), Gradle would now produce an Android App Bundle (aab). An Android App Bundle is required for using the dynamic-feature Gradle plugin, which we’ll cover later on.

Android App Bundles

Instead of a single apk, AABs generate a number of smaller configuration apks. These apks can then be tailored to the user’s device, saving data during delivery and on disk. App bundles are also a prerequisite for dynamic feature modules.

Configuration apks are generated by Google Play after the Android App Bundle is uploaded. With app bundles being an open spec and Open Source tooling available, other app stores can implement this delivery mechanism too. In order for the Google Play Store to generate and sign the apks the app also has to be enrolled to App Signing by Google Play.

Benefits

What did this change of packaging do for us?

Plaid is now is now more than 60 % smaller on device, which equals about 4 MB of data.

This means that each user has some more space for other apps.
Also download time has improved due to decreased file size.

Not a single line of code had to be touched to achieve this drastic improvement.

Approaching modularization

The overall approach we chose for modularizing is this:

  1. Move all code and resources into a core module.
  2. Identify modularizable features.
  3. Move related code and resources into feature modules.

green: dynamic features | dark grey: application module | light grey: libraries

The above graph shows the current state of Plaid’s modularization:

  • :bypass and external shared dependencies are included in core
  • :app depends on :core
  • dynamic feature modules depend on :app

Application module

The :app module basically is the already existing com.android.application, which is needed to create our app bundle and keep shipping Plaid to our users. Most code used to run Plaid doesn’t have to be in this module and can be moved elsewhere.

Plaid’s core module

To get started with our refactoring, we moved all code and resources into a com.android.library module. After further refactoring, our :core module only contains code and resources which are shared between feature modules. This allows for a much cleaner separation of dependencies.

External dependencies

A forked third party dependency is included in core via the :bypass module. Additionally, all other gradle dependencies were moved from :app to :core, using gradle’s apidependency keyword.

Gradle dependency declaration: api vs implementation

By utilizing api instead of implementationdependencies can be shared transitively throughout the app. This decreases file size of each feature module, since the dependency only has to be included in a single module, in our case :core. Also it makes our dependencies more maintainable, since they are declared in a single file instead of spreading them across multiple build.gradle files.

Dynamic feature modules

Above I mentioned the features we identified that can be refactored into com.android.dynamic-feature modules. These are:

:about
:designernews
:dribbble
:search

Introducing com.android.dynamic-feature

A dynamic feature module is essentially a gradle module which can be downloaded independently from the base application module. It can hold code and resources and include dependencies, just like any other gradle module. While we’re not yet making use of dynamic delivery in Plaid we hope to in the future to further shrink the initial download size.

The great feature shuffle

After moving everything to :core, we flagged the “about” screen to be the feature with the least inter-dependencies, so we refactored it into a new :about module. This includes Activities, Views, code which is only used by this one feature. Also resources such as drawables, strings and transitions were moved to the new module.

We repeated these steps for each feature module, sometimes requiring dependencies to be broken up.

In the end, :core contained mostly shared code and the home feed functionality. Since the home feed is only displayed within the application module, we moved related code and resources back to :app.

A closer look at the feature structure

Compiled code can be structured in packages. Moving code into feature aligned packages is highly recommended before breaking it up into different compilation units. Luckily we didn’t have to restructure since Plaid already was well feature aligned.

feature and core modules with their respective architectural layers

As I mentioned, much of the functionality of Plaid is provided through news sources. Each of these consists of remote and local data source, domain and UI layers.

Data sources are displayed in both the home feed and, in detail screens, within the feature module itself. The domain layer was unified in a single package. This had to be broken in two pieces: a part which can be shared throughout the app and another one that is only used within a feature.

Reusable parts were kept inside of the :corelibrary, everything else went to their respective feature modules. The data layer and most of the domain layer is shared with at least one other module and were kept in core as well.

Package changes

We also made changes to package names to reflect the new module structure.
Code only relevant only to the :dribbblefeature was moved from io.plaidapp to io.plaidapp.dribbble. The same was applied for each feature within their respective new module names.

This means that many imports had to be changed.

Modularizing resources caused some issues as we had to use the fully qualified name to disambiguate the generated R class. For example, importing a feature local layout’s views results in a call to R.id.library_imagewhile using a drawable from :core in the same file resulted in calls to

io.plaidapp.core.R.drawable.avatar_placeholder

We mitigated this using Kotlin’s import aliasing feature allowing us to import core’s R file like this:

import io.plaidapp.core.R as coreR

That allowed to shorten the call site to

coreR.drawable.avatar_placeholder

This makes reading the code much more concise and resilient than having to go through the full package name every time.

Preparing the resource move

Resources, unlike code, don’t have a package structure. This makes it trickier to align them by feature. But by following some conventions in your code, this is not impossible either.

Within Plaid, files are prefixed to reflect where they are being used. For example, resources which are only used in :dribbble are prefixed with dribbble_.

Further, files that contain resources for multiple modules, such as styles.xml are structurally grouped by module and each of the attributes prefixed as well.

To give an example: Within a monolithic app, strings.xml holds most strings used throughout.
In a modularized app, each feature module holds on to its own strings.
It’s easier to break up the file when the strings are grouped by feature before modularizing.

Adhering to a convention like this makes moving the resources to the right place faster and easier. It also helps to avoid compile errors and runtime crashes.

Challenges along the way

To make a major refactoring task like this more manageable it’s important to have good communication within the team. Communicating planned changes and making them step by step helped us to keep merge conflicts and blocking changes to a minimum.

Good intentions

The dependency graph from earlier in this post shows, that dynamic feature modules know about the app module. The app module on the other hand can’t easily access code from dynamic feature modules. But they contain code which has to be executed at some point.

Without the app knowing enough about feature modules to access their code, there is no way to launch activities via their class name in the Intent(ACTION_VIEW, ActivityName::class.java) way.
There are multiple other ways to launch activities though. We decided to explicitly specify the component name.

To do this we created an AddressableActivityinterface within core.

Using this approach, we created a function that unifies activity launch intent creation:

In its simplest implementation an AddressableActivity only needs an explicit class name as a String. Throughout Plaid, each Activity is launched through this mechanism. Some contain intent extras which also have to be passed through to the activity from various components of the app.

You can see how we did this in the whole file here:

Styling issues

Instead of a single AndroidManifest for the whole app, there are now separate AndroidManifests for each of the dynamic feature modules.
These manifests mainly contain information relevant to their component instantiation and some information concerning their delivery type, reflected by the dist: tag.
This means activities and services have to be declared inside the feature module that also holds the relevant code for this component.

We encountered an issue with modularizing our styles; we extracted styles only used by one feature out into their relevant module, but often they built upon :core styles using implicit inheritance.

Parts of Plaid’s style hierarchy

These styles are used to provide corresponding activities with themes through the module’s AndroidManifest.

Once we finished moving them, we encountered compile time issues like this:

* What went wrong:
Execution failed for task ‘:app:processDebugResources’.
> Android resource linking failed
~/plaid/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml:177: AAPT:
error: resource style/Plaid.Translucent.About (aka io.plaidapp:style/Plaid.Translucent.About) not found.
error: failed processing manifest.

The manifest merger tries to merge manifests from all the feature modules into the app’s module. That fails due to the feature module’s styles.xml files not being available to the app module at this point.

We worked around this by creating an empty declaration for each style within :core’s styles.xml like this:

<! — Placeholders. Implementations in feature modules. →
<style name=”Plaid.Translucent.About” />
<style name=”Plaid.Translucent.DesignerNewsStory” />
<style name=”Plaid.Translucent.DesignerNewsLogin” />
<style name=”Plaid.Translucent.PostDesignerNewsStory” />
<style name=”Plaid.Translucent.Dribbble” />
<style name=”Plaid.Translucent.Dribbble.Shot” />
<style name=”Plaid.Translucent.Search” />

Now the manifest merger picks up the styles during merging, even though the actual implementation of the style is being introduced through the feature module’s styles.

Another way to avoid this is to keep style declarations in the core module. But this only works if all resources referenced are in the core module as well. That’s why we decided to go with the above approach.

Instrumentation test of dynamic features

Along the modularization we found that instrumentation tests currently can’t reside within the dynamic feature module but have to be included within the application module. We’ll expand on this in an upcoming blog post on our testing efforts.

What is yet to come?

Dynamic code loading

We make use of dynamic delivery through app bundles, but don’t yet download these after initial installation through the Play Core Library. This would for example allow us to mark news sources that are not enabled by default (Product Hunt) to only be installed once the user enables this source.

Adding further news sources

Throughout the modularization process, we kept in mind the possibility of adding further news sources. The work to cleanly separate modules and the possibility of delivering them on demand makes this more compelling.

Finish modularization

We made a lot of progress to modularize Plaid. But there’s still work to do. Product Hunt is a news source which we haven’t put into a dynamic feature module at this point. Also some of the functionality of already extracted feature modules can be evicted from core and integrated into the respective features directly.

So, why did we decide to modularize Plaid?

Going through this process, Plaid is now a heavily modularized app. All without making changes to the user experience. We did reap several benefits in our day to day development from this effort:

Install size

Plaid is now on average more than 60 % smaller on a user’s device.
This makes installation faster and saves on precious network allowance.

Compile time

A clean debug build without caches now takes 32 instead of 48 seconds.*
All the while increasing from ~50 to over 250 tasks.

This time saving is mainly due to increased parallel builds and compilation avoidance thanks to modularization.

Further, changes in single modules don’t require recompilation of every single module and make consecutive compilation a lot faster.

*For reference, these are the commits I built for before and after timing.

Maintainability

We have detangled all sorts of dependencies throughout the process, which makes the code a lot cleaner. Also, side effects have become rarer. Each of our feature modules can be worked on separately with few interactions between them. The main benefit here is that we have to resolve a lot less merge conflicts.

In conclusion

We’ve made the app more than 60% smaller, improved on code structure and modularized Plaid into dynamic feature modules, which add potential for on demand delivery.

Throughout the process we always maintained the app in a state that could be shipped to our users. You can switch your app to emit an Android App Bundle today and save install size straight away. Modularization can take some time but is a worthwhile effort (see above benefits), especially with dynamic delivery in mind.

Go check out Plaid’s source code to see the full extent of our changes and happy modularizing…

For more details – https://medium.com/androiddevelopers/a-patchwork-plaid-monolith-to-modularized-app-60235d9f212e

Adopting Kotlin

Adopting Kotlin

Incorporating Kotlin in your large app

Illation by Virginia Poltrack

(This article is also available in Chinese at WeChat / 中文版请参考 WeChat)

One of the recurring questions developers ask me at conferences is “What’s the best way to add Kotlin to my existing Android app?” If you work in a team with more than a handful of people, adopting a new language can become complex. Over time, my answer has become longer and I’ve fine-tuned it based on my own experiences adding Kotlin to existing projects, and speaking with others — both at Google and the external developer community — about their journeys.

Here’s a guide to help you successfully introduce Kotlin to existing projects on larger teams. Many teams within Google, including the Android Developer Relations team, have used these techniques successfully. Two notable examples are the 2018 Google I/O app, which was rewritten completely in Kotlin, and Plaid, which has a mix of Java and Kotlin.

How to add Kotlin to an app

Designate a Kotlin champion

You should start by designating a person in your team to become the Kotlin expert and mentor. Quite often this person will be obvious: the one who is the most interested in using Kotlin. It could very well be you since you are reading this. This individual should focus on learning as much as possible about Kotlin and researching ideas on how to best incorporate Kotlin into the existing app. They should proactively share their Kotlin knowledge, and become the “go to” person for any questions from the team. This person should also participate in Java and Kotlin code reviews to ensure that changes follow Kotlin conventions and facilitate language interoperability (such as nullability annotations).

Learn the basics

While the Kotlin champion is spending time deep-diving into Kotlin, everyone else on the team should establish a baseline knowledge about Kotlin. If your team is just getting started with Kotlin there are a lot of resources you can use to learn the language and how it interacts with Android. I highly recommend starting with the Kotlin Koans, a series of small exercises that provides a tour of Kotlin’s features. It was a fun way for me to learn Kotlin.

The official site for Kotlin (kotlinlang.org) offers reference documents for working with the Kotlin standard library and step-by-step tutorialsfor accomplishing different tasks in Kotlin. The Android Developers site also has several resources on working with Kotlin in Android.

Form a study group

After the team is comfortable writing basic Kotlin, it’s a good time to form a study group. Kotlin is evolving quickly with many new features in the pipeline, such as Coroutines and Multiplatform. Regular group discussions help you understand upcoming language features and reinforces Kotlin best practices at your company.

Write tests in Kotlin

Many teams have found that writing tests in Kotlin is a great way to start using it in their projects, as it doesn’t impact your production code and isn’t bundled with your app package.

Your team can either write new tests or convert existing tests to Kotlin. Tests are useful to check for code regressions, and they add a level of confidence when refactoring your code. These tests will be especially useful when converting existing Java code into Kotlin.

Write new code in Kotlin

Before converting existing Java code to Kotlin, start by adding small pieces of Kotlin code to your app’s codebase. Begin with a small class or top-level helper function, making sure to add the relevant annotations to the Kotlin code to ensure proper interoperability from the Java code.

Analyze impact on APK size and build performance

Adding Kotlin to your app may increase both the APK size and build times. We recommend that you use Proguard to ensure that your release APKs are as small as possible and to reduce any increase in the number of methods. After running Proguard, the Kotlin impact on your APK size should be quite small, especially when you are just starting off.

For Kotlin-only projects and mixed-language projects (that are written in Java and Kotlin) there is a slight increase in compile and build times. However, many developers feel that the increased productivity of writing in Kotlin is a worthwhile trade-off. The Kotlin and Android teams are aware of longer build times and are continually striving to improve this important part of the development process. You should measure and monitor the build performance impact for your projects.

Update existing code to Kotlin

Once your team is comfortable using Kotlin, you can start to convert your existing code to Kotlin.

One extreme option is to start over and rewrite your app entirely in Kotlin. We took this approach with the 2018 Google I/O Android app, but this is probably not an option for most teams, as they need to ship software while adopting new technologies. Fortunately, Kotlin and the Java programming language are fully interoperable, so you can migrate your project one class at a time.

A more realistic approach is to use the code converter in Android Studio, which converts the code in a Java file to Kotlin. In addition, the IDE offers an option to convert to Kotlin any Java code pasted from the clipboard into a Kotlin file. The converter doesn’t always produce the most idiomatic Kotlin. You’ll need to review each file, but it’s a great way to save time and see how Kotlin looks like in your codebase.

Note that while Java and Kotlin are 100% interoperable they are not source compatible. It is not possible to write a single file in both Java and Kotlin. Consult the Kotlin and Androidguides for more tips on writing interoperable code between Java and Kotlin.

Convince management to adopt Kotlin

After gaining some experience with Kotlin, you may know in your heart of hearts that Kotlin is right for your team. But how do you convince your leadership or stakeholder teams about adopting Kotlin, when they don’t share your love for data classes, smart casts, and extension functions? How best to address this varies based on your specific situation. Below, we suggest some potential speaking points with data you can use to back up your claims.

  • The team is more productive with Kotlin.You can show data comparing the average lines of code per file between Kotlin and Java. It’s pretty common to see lines of code reduction of 25% or more. Less code to write also means less code to test and maintain, allowing your team to develop new features faster. In addition, you can track how fast your team spent developing a feature in Kotlin compared to a similar feature in Java.
  • Kotlin increases app quality. Kotlin’s null-safety feature is well-known, but there are many other safety features to help you avoid entire categories of code defects. One idea from Pinterest is to track your defect rate in each module of your app as you migrate them from Java to Kotlin. You should see the defect rate declining. You can watch Pinterest talk about this in this Android Developer Story video. There is also a recent academic research to back up this claim.
  • Kotlin makes your team happier.Happiness is hard to quantify, but you can find a way to demonstrate your team’s excitement about Kotlin to your management. Kotlin is the #2 most-loved programming language based on the 2018 StackOverflow survey.
  • The industry is moving towards Kotlin.26% of the top 1000 Android apps on Play are already using Kotlin. This includes heavy hitters like Twitter, Pinterest, WeChat, American Express, and many more. Kotlin is also the #2 fastest growing mobile programming language according to Redmonk. Dice has also publicized that the number of Kotlin jobs have experienced meteoric rise.

Going beyond

After you’ve had some hands-on experience adding Kotlin to your app, here are some additional tips to help incorporate Kotlin into your everyday development.

Define project-specific style conventions

The Kotlin and Android Kotlin style guides establish great baselines for formatting Kotlin code. Beyond those, it’s a good idea to establish conventions and idioms that work best for your team.

One strategy for implementing unified Kotlin style is to customize Android Studio’s code style settings. Another strategy is to use a linter. In the Sunflower and Plaid projects, we used ktlintto enforce code styles. Ktlint provides great style defaults that follow standard Kotlin style guides that can also be customized to your team’s specific needs.

Use only as much as needed

It’s easy to go overboard with Kotlin syntactic sugar, wrapping statements in apply, let, use, and other great language features. In general, it’s better to favor readability over minimizing lines of code. For example, in Plaid we determined that an apply block should have at least two lines. Explore what works best for your team and add that to your style guide.

Explore sample projects and case studies

The resources section below lists sample projects that Googlers have migrated to Kotlin, along with case studies from companies that have added Kotlin to their projects.

Frequently Asked Questions

As with any new technology, there are unknowns. Here are some questions we’ve heard from developers adopting Kotlin, along with suggestions to address them.

How do I convince fellow engineers to use a new language?

At Google I/O, I discussed various approach for engineers to adopt Kotlin with Andrey Breslav, the lead language designer of Kotlin. He and I agreed that the best approach is to try some Kotlin implementation with your team and then evaluate to see if it works for your situation. At the end of the day, if the minuses outweigh the pluses, you can pass on Kotlin — and Andrey says he’s OK with that!

Isn’t it hard to learn Kotlin?

Most developers pick up Kotlin fairly quickly. Experienced Java developers can use the same tools to develop in both Java and Kotlin. Ruby and Python developers will find similar language features in Kotlin, such as method chaining.

Will Google continue to support Kotlin for Android development?

Yes! Google is committed in its support for Kotlin.

In conclusion

I hope this guide provides inspiration for you and your teams to add Kotlin to your apps. While the steps may be daunting, almost everyone I’ve spoken with found a renewed joy in software development after adopting Kotlin

Note that while this article is mostly written for Android apps, its concepts are applicable to any Java-based project, from Android apps to server-side programming.

Thanks for reading, and good luck in your journey adding Kotlin to your app!

Continue exploring

The following is a collection of resources to aid your adoption of Kotlin:

— The official language site for Kotlin is kotlinlang.org

Developing Android apps with Kotlin from developer.android.com

— Style and interoperability guides

— Code samples

— Case studies

— Articles

Thanks to James Lau and Sean McQuillan

For more details –

Animating on a Schedule

Animating on a Schedule

Animations in the Google I/O app

I was recently part of a great team working on the Google I/O 2018 Android app. This is a conference companion app, allowing attendees and remote folks to find sessions, build a personalized schedule and reserve seats at the venue (if you’re lucky enough to be there!). We built a number of interesting animated features in the app that I believe greatly enhanced the experience. The code for this app has just been open sourced and I wanted to highlight a few of these instances and some interesting implementation details.

Some animated elements in the I/O app

Generally there are 3 types of animations we used in the app:

  1. Hero animations — used to reinforce branding and bring moments of delight
  2. Screen transitions
  3. State changes

I’d like to go into detail of a few of these.

Countdown

Part of the role of the app is to build excitement and anticipation for the conference. As such this year we included a large animated countdown to the conference start displayed both on the on-boarding screen and in the Info section. This was also a great opportunity to embed the event’s branding into the app, bringing a lot of character.

The countdown to the conference start

This animation was designed by a motion designer and handed off as a series of Lottie json files: each 1 second long showing a number animating ‘in’ then ‘out’. The Lottie format made it easy to drop the files into assets and even offered convenience methods like setMinAndMaxProgress which allowed us to play just the first or last half of an animation (to show a number animating in or out).

The interesting part was actually orchestrating these multiple animations into the overall countdown. To do this we created a custom CountdownView which is a fairly complexConstraintLayout holding a number of LottieAnimationViews. In this, we created a Kotlin delegate to encapsulate starting the appropriate animation. This allowed us to simply assign an Int to each delegate of the digit it should display and the delegate would set up and start the animation(s). We extended the ObservableProperty delegate which ensures that we only ran an animation when the digit changes. Our animation loop then simply posted a runnable every second (when the view is attached) which calculated which digit each view should display and updated the delegates.

Reservation

One of the key actions of the app is letting attendees reserve seats. As such we displayed this action prominently in a FAB on the session details screen. We felt that it was important to only report that the session was reserved once it had successfully completed on the backend (unlike less important actions like starring a session where we optimistically update the UI immediately). This might take a little time while we wait for a response from the backend so to make this more responsive we used animated icon to provide feedback that we’re working on it and to smoothly transition into the new state.

Feedback whilst reserving a seat at a session

This is complicated by the fact that there were a number of states this icon needed to reflect: the session might be reservable, they may already have reserved a seat, if the session is full then a waitlist might be available or they may be on the waitlist, or close to the session starting reservations are disabled. This resulted in many permutations of different states to animate between. To simplify these transitions, we decided to always go through a ‘working’ state; the animated hourglass above. Therefore each transition is actually a pair of: state 1 → working & working → state 2 . This simplified things greatly. We built each of these animations using shapeshifter; see the avd_state_to_state files here.

To display this, we used a custom view and an AnimatedStateListDrawable (ASLD). If you haven’t used ASLD before, it is (as its name implies) an animated version of StateListDrawable that you likely haveencountered — allowing you to not only provide different drawables per state but also transitionsbetween states (in the form of an AnimatedVectorDrawable or an AnimationDrawable). Here’s the drawabledefining the static images and the transitions into and out of the working state for the reservation icon.

We created a custom view to support our own custom states. Views offer some standard states like pressed or selected. Similarly, you can define your own and have the View route that to any Drawables it is displaying. We defined our own state_reservable, state_reserved etc. We then created an enum of these different states, encapsulating the view state plus any related attributes such as an associated content description. Our business logic could then simply set the appropriate value from this enum on the view (via data binding) which would update the drawable’s state, which kicked off an animation via the ASLD. The combination of custom states and AnimatedStateListDrawablewas a neat way to implement this, keeping the multitude of states in the declarative layers, resulting in minimal view code.

Speaker transition

Many of the screen transitions worked well with the standard window animations. One place we deviated from this is the transition into the speaker details screen. This displayed the speakers image on either side of the transition and was a perfect candidate for a shared element transition. This helps ease the context change between screens.

A shared element transition

This is a pretty standard shared element transition, using the platform ChangeBoundsand ArcMotion classes on an ImageView.

What was more interesting is how initiating this transition fitted into the Event pattern we used for navigation. Essentially, this pattern decouples input events (like taping on a speaker) from navigation events, putting the ViewModel in charge of how to respond to input. In this case, this decoupling means that the ViewModel exposed a LiveData of Events, which only knew the ID of the speaker to navigate to. Initiating a shared element transition requires the shared View, which we did not have at this point. We solved this by storing the speaker’s ID as a tag on the view when it is bound, so that the view can later be retrieved when we need to navigate to a particular speaker details screen.

Filters

A core part of the conference app is filtering the many events down to those you’re interested in. Each topic had a color associated with it for easy recognition and we received a great design for a custom ‘chip’ to use when selecting filters:

Animated filter chips

We looked at Chip from Material Components but opted to implement our own custom viewfor greater control over the display and animation between ‘checked’ states. This is implemented using canvas drawing and a StaticLayout for displaying text. The view has a single progress property [0–1] modelling unchecked–checked. To toggle the state, we simply animate this value and invalidate the view and the rendering code linearly interpolates positions and sizes of elements based on this.

Initially when implementing this, I made the view implement the Checkable interface and kicked off the animation when the setCheckedmethod set a new state. As we display multiple filters in a RecyclerView, this had the unfortunate effect of running the animation if a selected filter scrolled out and the view was rebound to an unselected filter scrolling in. Whoops. We therefore added a separate method to kick off the animation allowing us to differentiate between it being toggled by a click and an immediate update when binding new data to the view.

Additionally, when we introduced this toggle animation, we found that it was janking i.e. dropping frames. Was my animation code to blame? These filters are displayed in a BottomSheet in front of the main conference schedule screen. When a filter is toggled, we initiate the filtering logic to be applied to the schedule (and update the number of matching events in the filter sheet title). Some systracespelunking later, we determined that the issue was that when the filters were applied, the ViewPager of RecyclerViews displaying the schedule dutifully went off and updated to the newly delivered data. This caused a number of views to be inflated and bound. All of this work was blowing our frame budget… but the updating schedule wasn’t visible as it was behind the filter sheet. We made the decision to delay performing the actual filtering until the animation had run, we traded off some more implementation complexity for a smoother user experience. Initially I implemented this using postDelayed but this caused issues for UI tests. Instead we switched our method which started the animation to accept a lambda to be run on end. This allowed us to better respect the user’s animation settings and test the execution properly.

Get Animated

Overall I feel like the animations really contributed to the experience, character, branding and responsiveness of the app. Hopefully this post has helped explain both why and how they were used and given you pointers to their implementation.

For more details :

View at Medium.com

Android Animations Powered by Kotlin and RXJava2

Android Animations Powered by Kotlin and RXJava2

Dive into the Android animations and boost your RXJava2 and Kotlin skills with Ivan Škorić from PSPDFKit. He made a talk at the MBLT DEV 2017 conference and now we use this approach on a daily basis in our team at e-Legion ’cause it is really useful.

Animations on Android

There are 4 classes that are used by default on Android framework.

1. ValueAnimator is used for animating values. So you have out-of-the-box methods for animating specific values, mostly primitive values.

2. ObjectAnimator is a subclass of ValueAnimator that allows you to support animation for object properties.

3. AnimatorSet is basically used for scheduling animations. Example of usage in a sequence:

  • View comes on the left side of the screen.
  • After completing the first animation there is a need to perform an appearance animation for another View, etc.

4. ViewPropertyAnimator automatically starts and optimizes animations for the selected property View. This class we will be using most of the time. So we are going to use this ViewPropertyAnimator API and then wrap it in RxJava in reactive.

ValueAnimator

Take a look at ValueAnimator. You can use ValueAnimator.ofFloat that you want to animate float from 0 to 100. You set some Duration and then you start the animation.

Take a look at the example:

val animator = ValueAnimator.ofFloat(0f, 100f)
animator.duration = 1000
animator.start()
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
    override fun onAnimationUpdate(animation: ValueAnimator) {
        val animatedValue = animation.animatedValue as Float
textView.translationX = animatedValue
    }
})

Here we add UpdateListener and as the value gets updated we will apply that value to the View and move the object from 0 to 100, although this is not a good way to perform this operation.

ObjectAnimator

A better way to do so is ObjectAnimator:

val objectAnimator = ObjectAnimator.ofFloat(textView, “translationX”, 100f)
objectAnimator.duration = 1000
objectAnimator.start()

Give the command to change the desired View specific parameter to a certain value and set the time by setDuration. The thing is you have to have setTranslationX method in your class because what this will use reflection to get this method and then to actually animate the View. The problem is, it’s using reflection so it’s slower.

AnimatorSet

val bouncer = AnimatorSet()
bouncer.play(bounceAnim).before(squashAnim1)
bouncer.play(squashAnim1).before(squashAnim2)
val fadeAnim = ObjectAnimator.ofFloat(newBall, “alpha”, 1f, 0f)
fadeAnim.duration = 250
val animatorSet = AnimatorSet()
animatorSet.play(bouncer).before(fadeAnim)
animatorSet.start()

It is not really that convenient to use it, especially if there are a lot of things and you want to do some complex stuff, meaning you want to have some delays between animations. The more things you want to do the harder it gets to control it.

ViewPropertyAnimator

If you ever did animations on Android this is kind of go-to class for animating views. It provides a great API to schedule the animations you want to run.

ViewCompat.animate(textView)
        .translationX(50f)
        .translationY(100f)
        .setDuration(1000)
        .setInterpolator(AccelerateDecelerateInterpolator())
        .setStartDelay(50)
        .setListener(object : Animator.AnimatorListener {
            override fun onAnimationRepeat(animation: Animator) {}
            override fun onAnimationEnd(animation: Animator) {}
            override fun onAnimationCancel(animation: Animator) {}
            override fun onAnimationStart(animation: Animator) {}
        })

Run the method ViewCompat.animate that returns a ViewPropertyAnimator, and to animate translationX set the value to 50, translationY parameter — 100. Then you can declare duration of the animation, the interpolator you are going to use. Interpolator defines like in what way the animation is going to run. This example uses an interpolator that goes faster in the beginning and slows in the end. We can declare some start delay for the animation.

In addition, we have AnimatorListener. With this listener, you can listen to specific events happening during the animation runtime. This interface has 4 methods: onAnimationStart, onAnimationCancel, onAnimationEnd, onAnimationRepeat.

We will usually just care about the end of action. In API 16 it was decided to introduce withEndAction:

.withEndAction({ //API 16+
    //do something here where animation ends

})

With end action, you can define Runnable and something will run when this animation is finished.

The comments about the process of creating the animation as a whole:

1. start() method is optional. as soon as you define ViewPropertyAnimator the animations are going to get scheduled.

2. Only one animator can animate a specified view at a particular time. Which means just if you are animating a View, one animator can perform on it. If you want to do multiple animations like I want to move something but also want to expand it, you have to define it in one animator. You cannot just define two of them and then apply them at the same time because just one is affecting single View.

Why RxJava?

Let’s start with a simple example. Assume we create a method fadeIn:

fun fadeIn(view: View, duration: Long): Completable {
    val animationSubject = CompletableSubject.create()
    return animationSubject.doOnSubscribe {
        
ViewCompat.animate(view)
                .setDuration(duration)
                .alpha(1f)
                .withEndAction {
                     
animationSubject.onComplete()
                 }
    }

}

This is a fairly primitive solution, and to apply it to your project, you will need to take into account some peculiarities.

We are going to create a CompletableSubjectthat we’ll use to wait for the animation to complete, and then use the onCompletemethod to send messages to subscribers. To start the animation sequentially, you need to start the animation not immediately, but as soon as someone subscribes to it. This way, you can run multiple animations in a reactive style sequentially.

Consider the animation itself. In it, we transfer the View over which the animation will be performed, and specify the duration of the animation. And since this is an appearance animation, we have to specify transparency 1.

Let’s create a simple animation using what is written. We have four buttons. And what we want to do is animate them, fade them in with duration being 1000 milliseconds or one second:

val durationMs = 1000L
button1.alpha = 0f
button2.alpha = 0f
button3.alpha = 0f
button4.alpha = 0f
fadeIn(button1, durationMs)
        .andThen(fadeIn(button2, durationMs))
        .andThen(fadeIn(button3, durationMs))
        .andThen(fadeIn(button4, durationMs))
        .subscribe()

As a result, we have a simple syntax executes. We can use the andThen operator to run animations sequentially. When we subscribe to it, it will send the doonSubscribe event to Completable, which is the first in line for execution. After its completion, it will be signed to the second, third, and so on in the chain. Therefore, if an error occurs at some stage, the entire sequence produces an error. You also need to specify alpha 0 because we want the buttons to be invisible first. And here’s how it would look.

In Kotlin we can make extension method:

fun View.fadeIn(duration: Long): Completable 
    val animationSubject = CompletableSubject.create()
    return animationSubject.doOnSubscribe {
        
ViewCompat.animate(this)
                .setDuration(duration)
                .alpha(1f)
                .withEndAction {
                     
animationSubject.onComplete()
                 }
    }

}

We apply fadeIn on the view so we can make it extension of view class by defining this. We say View.fadeIn and then we just need duration. And we say ViewCompat.animate(this)because the object we are going to perform a method on is now referred to as this we can get it from this.

This is what Kotlin can do. Let’s see how the call to this function has changed in the animations:

button1.fadeIn(durationMs)
        .andThen(button2.fadeIn(durationMs))
        .andThen(button3.fadeIn(durationMs))
        .andThen(button4.fadeIn(durationMs))
        .subscribe()

Pretty nice syntax. We can basically read what it is going to do. FadeIn button 1 andThen fadeIn button 2 and fadeIn button 3 and so on.

Here we need to specify the duration each time we want to execute and every time it was one thousand. These are like default parameter values in Kotlin as well. So we can say the default execution type for animation is 1 second. And now we can remove this duration and each of these animations will execute in 1 second.

fun View.fadeIn(duration: Long = 1000L):

If there is a need to have button 2 execute in two seconds, we can specify specifically for that button. Other buttons will appear in 1 second.

button1.fadeIn()
        .andThen(button2.fadeIn(duration = 2000L))
        .andThen(button3.fadeIn())
        .andThen(button4.fadeIn())
        .subscribe()

Sequential execution

We were able to run a sequence from the animation using the andThen statement. What if I need to execute 2 animations simultaneously? There is an operator mergeWith, which will merge Completable as their past so there will all be run at the same time and the last one the callsonComplete then the whole thing will callonComplete. It runs all of them and it finishes once the last one finishes. If we change andThen to mergeWith, we will get an animation in which all buttons appear simultaneously, but the button 2 will appear a little longer than the others:

button1.fadeIn()
        .mergeWith(button2.fadeIn(2000))
        .mergeWith(button3.fadeIn())
        .mergeWith(button4.fadeIn())
        .subscribe()

We also can group the animations. For example, you want to fade in first two buttons together then you want to fade in third and fourth button.

(button1.fadeIn().mergeWith(button2.fadeIn()))
        .andThen(button3.fadeIn().mergeWith(button4.fadeIn()))
        .subscribe()

Combine the first and second buttons with the mergeWith, repeat the action for the third and fourth, and run these groups sequentially using the andThen operator. Now let’s improve the code by adding the fadeInTogether method:

fun fadeInTogether(first: View, second: View): Completable {
    return first.fadeIn()
            .mergeWith(second.fadeIn())
}

It allows you to run the fadeIn animation for two Views at the same time. How did the animation chain changes:

fadeInTogether(button1, button2)
        .andThen(fadeInTogether(button3, button4))
        .subscribe()

As a result:

Take a look at a more complex example. Let’s say we want to show an animation with some set delay. The interval will help:

fun animate() {
    val timeObservable = Observable.interval(100,    TimeUnit.MILLISECONDS)
    val btnObservable = Observable.just(button1, button2, button3, button4)
}

It will generate values every 100 milliseconds. Each button will appear after 100 milliseconds. Next, specify another Observable, which will issue buttons. In this case, we have 4 buttons. Let’s use the zip.

We have event streams in front of us:

Observable.zip(timeObservable, btnObservable,
        BiFunction<Long, View, Disposable>{ _, button ->
            
button.fadeIn().subscribe()
})

The string that we have up here is the first one the timeObservable. It will emit numbers in certain time frames. Let’s imagine this is 100 milliseconds.

And the second button observable will emit our Views and it is going to meet them right away together. Zip waits for one object to come from the first string and takes one object from the second stream, and then merges them together. Even though all these four will be available right away it will wait for the first stream to send their object. So you will merge first object from this one with the first view and then 100 milliseconds later when the second comes in it will merge with the second so you basically get our views coming in certain time intervals.

Let’s define what is called BiFunction in RxJava. The function takes two objects and returns the third one. We want to take time and we want to take view.and get disposable due to the fact that we trigger the FadeIn animation and subscribe to it. The value of time does not matter, so we get:

The Library or Project Name

Talk about a project that Ivan began to develop MBLT DEV 2017. He wanted to make the whole system more typified.

It presents a variety of skins for animation. We have already considered this above. It also contains ready-made animations that you can use. There is a generalized set of tools to create your own animations. This library will provide you with more powerful components for reactive programming.

fun fadeIn(view:View) : AnimationCompletable {
    return AnimationBuilder.forView(view)
            .alpha(1f)
            .duration(2000L)
            .build().toCompletable()
}

Let’s say we are going to create this fadeIn and now it is not going to return Completable but it is going to return AnimationCompletable. The class AnimationCompletable extended the Completable object so we have more options. One important thing is in our previous implementation we could not cancel the animations. So they would be drawn they would give a feedback but there is no cancel it. Now you can create animation Completable that will actually stop the animation once you unsubscribe from it.

We create the appearing animation using AnimationBuilder is one of the library classes. Specify which View the animation will be applied to. In fact, this class copies the behavior of the ViewPropertyAnimator, but with the difference that the output is a stream.

So you put alpha to 1f, duration to 2 seconds and then build. Now once you call build we are going to get the animation. Animation I define it as the immutable object so it holds parameters for the animation. It is not going to start anything. So it just going to define how the animation is going to look.

Run toCompletable, which will create AnimationCompletable. It will wrap the parameters of the animation in a kind of reactive way, and once you call subscribe — will execute the animation. If you dispose of it before it is finished, the animation will stop. You can also add a callback to it. You can say doOnAnimationReady, doOnAnimationStart, doOnAnimationEnd:

fun fadeIn(view:View) : AnimationCompletable {
    return AnimationBuilder.forView(view)
            .alpha(1f)
            .duration(2000L)
            .buildCompletable()
            .doOnAnimationReady { view.alpha = 0f }
}

In this example, we showed how to use AnimationBuilder conveniently, and changed the state of our View before starting the animation.

Video

We reviewed one of the options how to create layout and animation setting using Kotlin and RxJava. Here is a link to the project that describes the basic animations and examples for them, as well as the basic shell to work with the animation.

Learn more about the development process at Netflix, Badoo, Uber and other top companies