🧬 Android DataBinding kit for notifying data changes from Model layers to UI layers on MVVM architecture.

Overview

Bindables

License API Build Status Profile Medium

🧬 Android DataBinding kit for notifying data changes from Model layers to UI layers.
This library provides base classes for DataBinding (BindingActivity, BindingFragment, BindingViewModel),
and support ways in which notifying data changes without observable fields and LiveData.

UseCase

You can reference the good use cases of this library in the below repositories.

  • Pokedex - πŸ—‘οΈ Android Pokedex using Hilt, Motion, Coroutines, Flow, Jetpack (Room, ViewModel, LiveData) based on MVVM architecture.
  • DisneyMotions - 🦁 A Disney app using transformation motions based on MVVM (ViewModel, Coroutines, LiveData, Room, Repository, Koin) architecture.
  • MarvelHeroes - ❀️ A sample Marvel heroes application based on MVVM (ViewModel, Coroutines, LiveData, Room, Repository, Koin) architecture.
  • TheMovies2 - 🎬 A demo project using The Movie DB based on Kotlin MVVM architecture and material design & animations.

Download

Maven Central Jitpack

Gradle

Add below codes to your root build.gradle file (not your module build.gradle file).

allprojects {
    repositories {
        mavenCentral()
    }
}

And add a dependency code to your module's build.gradle file.

dependencies {
    implementation "com.github.skydoves:bindables:1.0.8"
}

SNAPSHOT

Bindables
Snapshots of the current development version of Bindables are available, which track the latest versions.

repositories {
   maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}

Setup DataBinding

If you already use DataBinding in your project, you can skip this step. Add below on your build.gradle and make sure to use DataBinding in your project.

plugins {
    ...
    id 'kotlin-kapt'
}

android {
  ...
  buildFeatures {
      dataBinding true
  }
}

BindingActivity

BindingActivity is a base class for Activities that wish to bind content layout with DataBindingUtil. It provides a binding property that extends ViewDataBinding from abstract information. The binding property will be initialized lazily but ensures to be initialized before being called super.onCreate in Activities. So we don't need to inflate layouts, setContentView, and initialize a binding property manually.

class MainActivity : BindingActivity<ActivityMainBinding>(R.layout.activity_main) {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

   binding.vm = viewModel // we can access a `binding` propety.

  // Base classes provide `binding` scope that has a receiver of the binding property.
  // So we don't need to use `with (binding) ...` block anymore.
   binding {
      lifecycleOwner = this@MainActivity
      adapter = PokemonAdapter()
      vm = viewModel
    }
  }
}

Extending BindingActivity

If you want to extend BindingActivity for designing your own base class, you can extend like the below.

abstract class BaseBindingActivity<T : ViewDataBinding> constructor(
  @LayoutRes val contentLayoutId: Int
) : BindingActivity(contentLayoutId) {
  
  // .. //  
}

BindingFragment

The concept of the BindingFragment is not much different from the BindingActivity. It ensures the binding property to be initialized in onCreateView.

class HomeFragment : BindingFragment<FragmentHomeBinding>(R.layout.fragment_home) {

  private val viewModel: MainViewModel by viewModels()

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
  ): View {
    super.onCreateView(inflater, container, savedInstanceState) // we should call `super.onCreateView`.
    return binding {
      adapter = PosterAdapter()
      vm = viewModel
    }.root
  }
}

Extending BindingFragment

If you want to extend BindingFragment for designing your own base class, you can extend like the below.

abstract class BaseBindingFragment<T : ViewDataBinding> constructor(
  @LayoutRes val contentLayoutId: Int
) : BindingFragment(contentLayoutId) {
 
  // .. //
}

BindingViewModel

BindingViewModel provides a way in which UI can be notified of changes by the Model layers.

bindingProperty

bindingProperty notifies a specific has changed and it can be observed in UI layers. The getter for the property that changes should be marked with @get:Bindable.

class MainViewModel : BindingViewModel() {

  @get:Bindable
  var isLoading: Boolean by bindingProperty(false)
    private set // we can prevent access to the setter from outsides.

  @get:Bindable
  var toastMessage: String? by bindingProperty(null) // two-way binding.

  fun fetchFromNetwork() {
    isLoading = true

    // ... //
  }
}

In our XML layout, the changes of properties value will be notified to DataBinding automatically whenever we change the value.

">
<ProgressBar
    android:id="@+id/progress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:gone="@{!vm.loading}"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

notifyPropertyChanged

we can customize setters of general properties for notifying data changes to UI layers using @get:Bindable annotation and notifyPropertyChanged() in the BindingViewModel.

@get:Bindable
var message: String? = null
  set(value) {
    field = value
    // .. do something.. //
    notifyPropertyChanged(::message) // notify data changes to UI layers. (DataBinding)
  }

Two-way binding

We can implement two-way binding properties using the bindingProperty. Here is a representative example of the two-way binding using TextView and EditText.

class MainViewModel : BindingViewModel() {
  // This is a two-way binding property because we don't set the setter as privately.
  @get:Bindable
  var editText: String? by bindingProperty(null)
}

Here is an XML layout. The text will be changed whenever the viewModel.editText is changed.

">
<androidx.appcompat.widget.AppCompatTextView
  android:id="@+id/textView"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@{viewModel.editText}" />

<EditText
  android:id="@+id/editText"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content" />

In your Activity or Fragment, we can set the viewModel.editText value whenever the EditText's input is changed. We can implement this another way using inversebindingadapter.

binding.editText.addTextChangedListener {
  vm.editText = it.toString()
}

Binding functions

We can implement bindable functions using @Bindable annotation and notifyPropertyChanged() in the BindingViewModel. And the @Bindable annotated method's name must start with get.

class MainViewModel : BindingViewModel() {
  @Bindable
  fun getFetchedString(): String {
    return usecase.getFetchedData()
  }

  fun fetchDataAndNotifyChaged() {
    usecase.fetchDataFromNetowrk()
    notifyPropertyChanged(::getFetchedString)
  }
}

Whenever we call notifyPropertyChanged(::getFetchedData), getFetchedString() will be called and the UI layer will get the updated data.

android:text="@{viewModel.fetchedData}"

Binding Flow

We can create a binding property from Flow using @get:Bindable and asBindingProperty. UI layers will get newly collected data from the Flow or StateFlow on the viewModelScope. And the property by the Flow must be read-only (val), because its value can be changed only by observing the changes of the Flow.

class MainViewModel : BindingViewModel() {

  private val stateFlow = MutableStateFlow(listOf<Poster>())

  @get:Bindable
  val data: List<Poster> by stateFlow.asBindingProperty()

  @get:Bindable
  var isLoading: Boolean by bindingProperty(false)
    private set

  init {
    viewModelScope.launch {
      stateFlow.emit(getFetchedDataFromNetwork())

      // .. //
    }
  }
}

Binding SavedStateHandle

We can create a binding property from SavedStateHandle in the BindingViewModel using @get:Bindable and asBindingProperty(key: String). UI layers will get newly saved data from the SavedStateHandle and we can set the value into the SavedStateHandle when we just set a value to the property.

@HiltViewModel
class MainViewModel @Inject constructor(
  private val savedStateHandle: SavedStateHandle
) : BindingViewModel() {

  @get:Bindable
  var savedPage: Int? by savedStateHandle.asBindingProperty("PAGE")

  // .. //

BindingRecyclerViewAdapter

We can create binding properties in the RecyclerView.Adapter using the BindingRecyclerViewAdapter. In the below example, the isEmpty property is observable in the XML layout. And we can notify value changes to DataBinding using notifyPropertyChanged.

class PosterAdapter : BindingRecyclerViewAdapter<PosterAdapter.PosterViewHolder>() {

  private val items = mutableListOf<Poster>()

  @get:Bindable
  val isEmpty: Boolean
    get() = items.isEmpty()

  fun addPosterList(list: List<Poster>) {
    items.clear()
    items.addAll(list)
    notifyDataSetChanged()
    notifyPropertyChanged(::isEmpty)
  }
}

In the below example, we can make the placeholder being gone when the adapter's item list is empty or loading data.

">
<androidx.appcompat.widget.AppCompatTextView
    android:id="@+id/placeholder"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/empty"
    app:gone="@{!adapter.empty || viewModel.loading}" />

BindingModel

We can use binding properties in our own classes via extending the BindingModel.

class PosterUseCase : BindingModel() {

  @get:Bindable
  var message: String? by bindingProperty(null)
    private set

  init {
    message = getMessageFromNetwork()
  }
}

Find this library useful? ❀️

Support it by joining stargazers for this repository. ⭐
And follow me for my next creations! 🀩

License

Copyright 2021 skydoves (Jaewoong Eum)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • App crashes on 2nd time debug app launch on andoroid 11 phone

    App crashes on 2nd time debug app launch on andoroid 11 phone

    Please complete the following information:

    • Library Version [ v1.0.9]
    • Affected Device(s) [e.g. Samsung Galaxy s10 with Android 11]

    Describe the Bug: App keeps crashing when launching 2nd time while development.

    You can try running current sample version in any android 11 to check the below issue.

    kotlin.jvm.KotlinReflectionNotSupportedError: Kotlin reflection implementation is not found at runtime. Make sure you have kotlin-reflect.jar in the classpath

    Below is class and line number where it crases,

    Class : BindingManager, LineNumber : 73( it.getter.hasAnnotation()). - It crases due to getter method of reflection jar.

    opened by keyur9779 6
  • Support for using onCreateDialog in a DialogFragment

    Support for using onCreateDialog in a DialogFragment

    Is your feature request related to a problem?

    I started to use your library in my project and it works really great, except for one thing. My DialogFragments are using onCreateDialog and an AlertDialogBuilder to inflate the layout. When trying to use the binding at that point it will result in an IllegalStateException with message "...binding cannot be accessed before onCreateView() or after onDestroyView()". Would it be possible to make your BindingDialogFragment to also support this use case in some way?

    opened by epkjoja 5
  • BottomSheetDialogFragment - Feature Request

    BottomSheetDialogFragment - Feature Request

    First of all thank you so much for this awesome library. Your code is, as usually, really well organized and useful.

    Is your feature request related to a problem?

    I was hoping to implement a BottomSheetDialogFragment, and to use DataBinding on it. Since I am using Bindables, I was looking for a binding class that would extend already from the BottomSheetDialogFragment class.

    Describe the solution you'd like:

    I know it is not hard to apply some work around from my side to this, but I also think that this would be a good feature to be implemented and made available.

    opened by pauloaapereira 3
  • SavedStateHandle default value

    SavedStateHandle default value

    How to add default value when using savedstatehandle?

      @get:Bindable
      var savedPage: Int? by savedStateHandle.asBindingProperty("PAGE")
    

    So i want this savedPage non nullable, by put default value, thank you

    opened by fjr619 1
  • ν”„λ ˆκ·Έ λ¨ΌνŠΈμ—μ„œ onCreateView 에 SuperCall μ—λŸ¬κ°€ μžˆμŠ΅λ‹ˆλ‹€.

    ν”„λ ˆκ·Έ λ¨ΌνŠΈμ—μ„œ onCreateView 에 SuperCall μ—λŸ¬κ°€ μžˆμŠ΅λ‹ˆλ‹€.

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { super.onCreateView(inflater, container, savedInstanceState) return binding { lifecycleOwner = this@SurveyFragment vm = viewModel

      setBindItem()
      subscribeUi()
    
    }.root
    

    }

    μ—λŸ¬: Overriding method should call super.onCreateView

    implementation 'com.github.skydoves:bindables:1.0.9' λ₯Ό μ‚¬μš©μ€‘μž…λ‹ˆλ‹€. ext.kotlin_version = "1.5.21" μ½”ν‹€λ¦° 버전

    allprojects { repositories { google() mavenCentral() maven { url "https://jitpack.io" } maven { url 'https://maven.google.com' } } }

    opened by MoonDaeYeong 1
  • How to combine with library bundler?

    How to combine with library bundler?

    Example i have variable from bundler like this

    private val name: String by bundle("name", "")
    

    but i want make it observable to with anotation @get:Bindable, because that variable used for data in edittext, so i need to make it into 2way binding

    opened by fjr619 1
  • throw IllegalStateException if binding accessed before view created or after destroyed

    throw IllegalStateException if binding accessed before view created or after destroyed

    Guidelines

    throw IllegalStateException instead of NullPointerException if view binding is accessed before onCreateView() or after onDestroyView()

    Types of changes

    What types of changes does your code introduce?

    Preparing a pull request for review

    Ensure your change is properly formatted by running:

    $ ./gradlew spotlessApply
    

    Please correct any failures before requesting a review.

    opened by Bloody-Badboy 0
Releases(1.1.0)
  • 1.1.0(Jun 18, 2022)

    What's Changed

    • Integrated binary validator by @skydoves in https://github.com/skydoves/Bindables/pull/18
    • Cleaned up Gradle & dependencies by @skydoves in https://github.com/skydoves/Bindables/pull/20
    • Migrated to maven publishing scripts by @skydoves in https://github.com/skydoves/Bindables/pull/25
    • Updated Gradle properties by @skydoves in https://github.com/skydoves/Bindables/pull/26

    Full Changelog: https://github.com/skydoves/Bindables/compare/1.0.9...1.1.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.9(Jul 31, 2021)

    πŸŽ‰ Released a new version 1.0.9! πŸŽ‰

    What's New?

    • Embedded proguard rules. (#13)
    • Unbind binding property when the lifecycle is destroyed. (#12)
    • Add explicit modifiers. (#14)
    • Refactored internal functions.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.8(Apr 12, 2021)

    πŸŽ‰ Released a new version 1.0.8! πŸŽ‰

    What's New?

    • Throw IllegalStateException if binding accessed before view created or after destroyed. (#7)
    • Find java beans related function name for mapping bindable resource. (#8)
    • Added CallSuper annotation for forcing to call super.onCreateView. (#9)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.7(Mar 14, 2021)

    πŸŽ‰ Released a new version 1.0.7! πŸŽ‰

    What's New?

    • Added an isSubmitted Bindable property in the BindingListAdapter that indicates an item list has been submitted.
    • Added BindingFragmentActivity and BindingComponentActivity.
    • Added clearAllProperties interface function in the BindingObservable.
    • Clear all binding properties callback when onClear called in viewmodels.
    • Clear all binding properties callback when onDetachedFromRecyclerView called in adapters.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.6(Mar 13, 2021)

    πŸŽ‰ Released a new version 1.0.6! πŸŽ‰

    What's New?

    • Added BindingListAdapter that extends ListAdapter and supports @bindable.
    • Improved internal validation of the @Bindable properties.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.5(Feb 25, 2021)

    πŸŽ‰ Released a new version 1.0.5! πŸŽ‰

    What's New?

    • Added BindingBottomSheetDialogFragment.
    • Destroy the binding backing property on onDestroyView in Fragments.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.4(Feb 20, 2021)

  • 1.0.3(Feb 19, 2021)

    πŸŽ‰ Released a new version 1.0.3! πŸŽ‰

    ❗ Important ❗

    Bindables recommended using after the 1.0.4 version for reducing the initialization issue by the R8 compiler on release products.

    What's New?

    • We don't need to initialize using the BindingManager anymore.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.2(Feb 14, 2021)

    πŸŽ‰ Released a first version 1.0.2! πŸŽ‰

    ❗ Important ❗

    Bindables recommended using after the 1.0.4 version for reducing the initialization issue by the R8 compiler on release products.

    Source code(tar.gz)
    Source code(zip)
Owner
Jaewoong Eum
Android software engineer. Digital Nomad. Open Source Contributor. ❀️ Love coffee, music, magic tricks and writing poems. Coffee Driven Development.
Jaewoong Eum
Android library for viewing and sharing in app databases.

DbInspector DbInspector provides a simple way to view the contents of the in-app database for debugging purposes. There is no need to pull the databas

Infinum 925 Dec 17, 2022
An Android helper class to manage database creation and version management using an application's raw asset files

THIS PROJECT IS NO LONGER MAINTAINED Android SQLiteAssetHelper An Android helper class to manage database creation and version management using an app

Jeff Gilfelt 2.2k Jan 7, 2023
A small library to help with Realm.IO integration in Android apps

Android Realm Asset Helper A small library of methods to help with Realm.IO integration in Android apps Copies a realm database from a the assets fold

Quality Mobile Puzzle Apps 29 Dec 28, 2021
A wrapper around Android's SQLiteDatabase with restoring capability

Restorable SQLiteDatabase RestorableSQLiteDatabase is a wrapper to replicate android's SQLiteDatabase class with restoring capability. This wrapper ma

Navid 21 Oct 21, 2022
A simple NoSQL client for Android. Meant as a document store using key/value pairs and some rudimentary querying. Useful for avoiding the hassle of SQL code.

SimpleNoSQL A simple NoSQL client for Android. If you ever wanted to just save some data but didn't really want to worry about where it was going to b

Colin Miller 389 Sep 25, 2022
A key-value database for Android

SnappyDB SnappyDB is a key-value database for Android it's an alternative for SQLite if you want to use a NoSQL approach. It allows you to store and g

Nabil Hachicha 1.8k Dec 28, 2022
Insanely easy way to work with Android Database.

Sugar ORM Insanely easy way to work with Android databases. Official documentation can be found here - Check some examples below. The example applicat

null 2.6k Dec 16, 2022
an android library for debugging what we care about directly in app.

EN | δΈ­ζ–‡ Pandora is a tool box that allows you to inspect and modify what includes networks, databases, UIs, etc. directly in your application. It is s

linjiang 1.5k Dec 30, 2022
Android Database Performance Benchmarks

β„Ή Check out our new performance test app that includes ObjectBox. Android Database Performance Benchmark This project evaluates Android databases and

Markus Junginger 80 Nov 25, 2022
Kodein-DB is a Kotlin/Multiplatform embedded NoSQL database that works on JVM, Android, Kotlin/Native and iOS.

Kodein-DB is a Kotlin/Multiplatform embedded NoSQL database that works on JVM, Android, Kotlin/Native and iOS. It is suited for client or mobile applications.

null 277 Dec 3, 2022
Test any type of cloud database on Android apps. No need of a dedicated backend.

Test any type of cloud database on Android apps. No need of a dedicated backend.

Arjun 9 May 9, 2022
Android with Real-time Database

Android with Real-time Database It was too much effort to build my own real-time database, but the result really satisfying, so it was worth it. Note

null 4 Jun 14, 2022
Pref-DB - Android SharedPreferences alternative library

Pref-DB Android SharedPreferences alternative library.

M.Fakhri 5 Sep 14, 2022
🧬 Android DataBinding kit for notifying data changes from Model layers to UI layers on MVVM architecture.

?? Android DataBinding kit for notifying data changes from Model layers to UI layers on MVVM architecture.

Jaewoong Eum 275 Dec 21, 2022
Utility for detecting and notifying when your Android app goes background / becomes foreground

Foredroid Utility for detecting and notifying when your Android app goes background / becomes foreground. API-level 14+. Usage: Initialise Foreground

Steve Liles 151 Nov 29, 2022
Frogo Android UI Kit - Frogo UI Design Kit Guideline

About This Project Follow-up project from frogo-ui-kit UI Kit for helping you in apps development Migrate from frogo-ui-kit Just renaming package com.

Frogobox 6 Nov 25, 2022
Android application showcasing the MVVM architecture, Clean code using Kotlin, Coroutine, Flow and databinding.

Code4Lyst Android application showcasing the MVVM architecture, Clean code using Kotlin, Coroutine, Flow and databinding. App Architecture MVVM CleanC

Plabon Modak 1 Nov 29, 2021
Mole Analysis Use Case for HMS ML Kit Custom Model

Mole Analysis Mole Analysis Use Case for HMS ML Kit Custom Model Introduction What is Melanoma? Melanoma is the most serious among skin cancers becaus

null 15 Aug 23, 2022
A Model-View-Presenter / Model-View-Intent library for modern Android apps

Mosby A Model-View-Presenter and Model-View-Intent library for Android apps. Dependency dependencies { compile 'com.hannesdorfmann.mosby3:mvi:3.1.1

Hannes Dorfmann 5.5k Jan 5, 2023