How to Create Ripple Animation for Changing Theme in Android?
Tags: android, java, kotlin, library
Published: 2021-09-16 10:10:12

Recently I have published a library on GitHub, which creates custom themes and changes them dynamically with ripple animation.
I will be grateful if you review it and send me your precious opinions and pull requests as a contributor.

## Demo:
![animation-ripple-android-theme](https://user-images.githubusercontent.com/6734608/129915453-b57a1618-2d20-42a3-85a7-57bd1c425522.gif)
</br></br>

## My Library on Github:
https://github.com/imandolatkia/Android-Animated-Theme-Manager
</br></br>

## Features:
* support **java** and **kotlin** projects.
* change theme **without recreating** activities and fragments.
* support multi fragments apps.
* ripple animation.
* **reverse animation**.
* changeable animation duration.
* changeable animation position.
* animation listener.
* observe changes of themes for custom actions with Livedata.
* easy to use, 5 or 7 tiny steps.
* **support any android APIs** (animation works on API>20).

</br></br>

## How to install? 
Add the following line to **app-level** build.gradle file, in dependencies scope:
```gradle
dependencies {
    ...
    implementation "io.github.imandolatkia:animatedThemeManager:1.1.2"
}
```
</br>

## How to use?
1- Create an abstract class that inherits from **AppTheme**. In this class create abstract methods to return related color for all UI element that you want to change them on each theme. For example, if you want to change the background color, text colors and icon colors in your **firstActivity**, do the following:

```kotlin
interface MyAppTheme : AppTheme {
    fun firstActivityBackgroundColor(context: Context): Int
    fun firstActivityTextColor(context: Context): Int
    fun firstActivityIconColor(context: Context): Int
    // any other methods for other elements
}
```


2- For each theme that you want in your app, create a class that extends from  **the class that was created in step 1 (MyAppTheme)**, and implement methods with related colors or resources, for example, if you want to have 3 themes, you should create 3 class and implement methods:

```kotlin
class LightTheme : MyAppTheme {

    override fun id(): Int { // set unique iD for each theme 
        return 0
    }

    override fun firstActivityBackgroundColor(context: Context): Int {
        return ContextCompat.getColor(context, R.color.background_light)
    }

    override fun firstActivityTextColor(context: Context): Int {
        return ContextCompat.getColor(context, R.color.text_light)
    }
    
     override fun firstActivityIconColor(context: Context): Int {
        return ContextCompat.getColor(context, R.color.icon_light)
    }
    
    ...
}

class NightTheme : MyAppTheme {...}
class PinkTheme : MyAppTheme {...}

```


3- Extend your activity from **ThemeActivity**:
```kotlin
MainActivity : ThemeActivity() {
...
}
```

4- Implement ThemeActivity's **2 abstract methods**:

```kotlin
// to sync ui with selected theme
override fun syncTheme(appTheme: AppTheme) {
    // change ui colors with new appThem here

    val myAppTheme = appTheme as MyAppTheme
    // set background color
    binder.root.setBackgroundColor(myAppTheme.firstActivityBackgroundColor(this))

    //set text color
    binder.text.setTextColor(myAppTheme.activityTextColor(this))
        
    // set icons color
    binder.share.setColorFilter(myAppTheme.firstActivityIconColor(this))
    binder.gift.setColorFilter(myAppTheme.firstActivityIconColor(this))        
    ...
}

// to get the start theme
override fun getStartTheme(): AppTheme {
    return LightTheme()
}

```

5- Change theme with the ThemeManager.instance.changeTheme() method:
```kotlin
// set change theme click listener
binder.lightButton.setOnClickListener {
  ThemeManager.instance.changeTheme(LightTheme(), it)
}
```

The first argument is the selected theme.

The second argument is the view that animation starts from the center of it.
</br></br>

## How to use in multi fragments app?
Repeat all previous 5 steps, and then:


6- Extend your fragments from **ThemeFragment**:
```kotlin
MyFragment : ThemeFragment() {
...
}
```


7- Implement ThemeFragment **syncTheme abstract methods**:

```kotlin
// to sync ui with selected theme
override fun syncTheme(appTheme: AppTheme) {
    // change ui colors with new appThem here
    ...
}
```


## Some other settings and customization:
###  ✔️ reverse animation
If you want to use the reverse animation, call reverseChangeTheme() instead of changeTheme():

```kotline
   binder.lightButton.setOnClickListener {
        ThemeManager.instance.reverseChangeTheme(LightTheme(), it)
   }
```
![reverse ripple theme animation](https://user-images.githubusercontent.com/6734608/130370446-1218d53d-9c25-4dc9-8d6b-f7e8a1056731.gif)

###  ✔️ change animation duration
If you want to change the animation duration, add your desired duration in milliseconds as the third argument of ThemeManager.instance.changeTheme(). The default value is 600:

```kotlin
   binder.lightButton.setOnClickListener {
        ThemeManager.instance.changeTheme(LightTheme(), it, 800)
   }
```

###  ✔️ change animation center position
If you want to start animation somewhere other than the view that clicked, send a Coordinate object instead of a View in ThemeManager.instance.changeTheme()
```kotlin
   binder.lightButton.setOnClickListener {
          binder.nightButton.setOnClickListener {
            ThemeManager.instance.changeTheme(NightTheme(), Coordinate(10, 20)
        }
   }
```

witch the Coordinate is:
```kotlin
 Coordinate(var x: Int, var y: Int) 
```

### ✔️ observe changes of themes yourself
If you want to observe changes of themes and do some custom action, you can use theme Livedata in any fragment or activity:

```kotlin
    ThemeManager.instance.getCurrentLiveTheme().observe(this) {
        Toast.makeText(this, "on Theme changed to ${it.id()}", Toast.LENGTH_SHORT).show()
    }
```


### ✔️ set animation listener
If you want to set an animation listener, use setThemeAnimationListener() method in your activity

```kotlin
     setThemeAnimationListener(MyThemeAnimationListener(this))
```
witch the MyThemeAnimationListener is:

```kotlin
    class MyThemeAnimationListener(var context: Context) : ThemeAnimationListener{
        override fun onAnimationStart(animation: Animator) {
           Toast.makeText(context, "onAnimationStart", Toast.LENGTH_SHORT).show()
        }

        override fun onAnimationEnd(animation: Animator) {
            Toast.makeText(context, "onAnimationEnd", Toast.LENGTH_SHORT).show()
        }

        override fun onAnimationCancel(animation: Animator) {
            Toast.makeText(context, "onAnimationCancel", Toast.LENGTH_SHORT).show()
        }

        override fun onAnimationRepeat(animation: Animator) {
            Toast.makeText(context, "onAnimationRepeat", Toast.LENGTH_SHORT).show()
        }
    }

```

[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=imandolatkia_Android-Animated-Theme-Manager)](https://sonarcloud.io/dashboard?id=imandolatkia_Android-Animated-Theme-Manager)