Bindroid


Source link: https://github.com/depoll/bindroid

Bindroid - Databinding for Android

Bindroid is an open-source utility library for Android apps whose primary goal is to simplify binding UI to data. It introduces an observability pattern for model objects and a number of simple methods for quickly binding these objects to your user interfaces. The result is a responsive, always-consistent and up-to-date user experience without having to write all of the glue to ensure that your UI is updated whenever the data it presents changes.

Bindroid dramatically simplifies implementing the MVVM pattern when building Android applications, though it can be just as useful even if you're not following this pattern.

Javadocs

You can find the Javadocs for Bindroid here.

Getting Started

Clone the repository, import it into Eclipse alongside your Android project, and add a reference to Bindroid to your project. Nothing more to it!

Building Your Model

At the heart of the binding framework is the concept of notifications when changes occur in your model. The classes in the com.bindroid.trackable package provide the tools you need to make your models notify observers of changes.

The most common class you'll use is TrackableField<T> and its trackable siblings, TrackableBoolean, TrackableByte, TrackableChar, TrackableDouble, TrackableFloat, TrackableInt, TrackableLong, and TrackableShort. These objects can replace the private fields in your models. Once you've backed your models with trackable fields, any changes to their values will be able to be tracked. This enables bindings to observe changes to models as they happen and update their targets.

A simple ViewModel might look like this:

public class FooModel {

// An int property named "Bar"
private TrackableInt bar = new TrackableInt(0);

public int getBar() {

  return bar.get();

}

public void setBar(int value) {

  bar.set(value);

}

 // A String property named "Baz"
private TrackableField<String> baz = new TrackableField<String>();

public String getBaz() {

  return baz.get();

}

public void setBaz(String value) {

  baz.set(value);

}
 
}

Trackables are infectious -- any property based upon trackable objects is automatically trackable, including complex calculated properties. For example, if you added a property called BarBaz to the model above, calculated based upon the values of Bar and Baz, it would still be tracked and bindings to it would be updated whenever either Bar or Baz change:

public String getBarBaz() {

int strLength = (getBaz() == null ? 0 : getBaz().length());

if ((strLength + getBar()) % 2 == 0) {

  return "Baz's length + Bar was even.";

}

return String.format("Bar: %d, Baz: %s", getBar(), getBaz());
 
}

Bindroid also provides a trackable list implementation called TrackableCollection<T>, allowing properties to be based upon values in the list. Anything tracking these values will be notified whenever the collection changes.

Building Your UI

You will begin by building your Android UI as you would normally, with a variety of layout XML files. Once you've placed all of your Views in the layout, it's time to create the object that you'll bind to and establish the bindings. Bindroid takes care of the rest!

Under the covers, Bindroid uses the Binding class to establish a binding between two properties. Most of the time, however, you will not use this class directly. Instead, you will create your bindings using the UiBinder class. UiBinder contains a number of overloads for a bind() method that aid in looking up UI elements based upon their resource IDs and binding to properties using reflection so that you don't have to build anonymous classes for every property that you bind to.

In your Activity's OnCreate() override or View's constructor, call bind() for each pair of properties you wish to track each other:

@Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

 ViewModel model = new ViewModel();

 UiBinder.bind(new EditTextTextProperty((EditText) this.findViewById(R.id.TextField)), model,

 "StringValue", BindingMode.TWO_WAY);

UiBinder.bind(this, R.id.TextView, "Text", model, "StringValue", BindingMode.ONE_WAY);
 
}

The string-based property names in the example above indicate the property path to bind. Any methods named "get" are considered a property called "Name", and if a corresponding setter is defined, it must be named "set". The property path also supports dot-notation to access nested properties and index notation to access lists and maps. For example, the string "Foo.Bar[baz].Bat[1]" would map to a binding to model.getFoo().getBar().get("baz").getBat().get(1).

Since the built-in Android views don't support Bindroid's property change notifications, two-way bindings require some sort of adapter to proffer the changes back to the model object. In the example above, we've used the EditTextTextProperty to bind the Text property on an EditText view to the StringValue property on our model. Bindroid provides a few of these property adapters for you. In most cases, however, a one-way binding can be accomplished simply using reflection (e.g. the Text property of a TextView, as shown above), since the binding framework doesn't need to be notified when the view's property value changes.

Conversions

Sometimes, the values on your model will not map directly to the types of values that the views in your UI expect. For example, you may have an int property on your model that will be displayed as a String in a TextView. Bindroid solves this problem using converters, which can be passed to a Binding or to UiBinder.bind():

@Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

 ViewModel model = new ViewModel();

 UiBinder.bind(new EditTextTextProperty((EditText) this.findViewById(R.id.TextField)), model,

 "StringValue", BindingMode.TWO_WAY);

UiBinder.bind(this, R.id.TextView, "Text", model, "StringValue", BindingMode.ONE_WAY);

UiBinder.bind(this, R.id.ListView, "Adapter", model, "Dates", BindingMode.ONE_WAY,

 new AdapterConverter(DateView.class));

 UiBinder.bind(this, R.id.CountTextView, "Text", model, "Count", BindingMode.ONE_WAY,

 new ToStringConverter("Count: %d"));

UiBinder.bind(this, R.id.TextLengthView, "Text", model, "TextLength", BindingMode.ONE_WAY,

 new ToStringConverter("Text length: %d"));

UiBinder.bind(this, R.id.SumView, "Text", model, "CountPlusTextLength", BindingMode.ONE_WAY,

 new ToStringConverter("Sum: %d"));

UiBinder.bind(this, R.id.EvenSpinner, "Visibility", model, "CountIsEven", BindingMode.ONE_WAY,

 new BoolConverter());

UiBinder.bind(this, R.id.OddSpinner, "Visibility", model, "CountIsEven", BindingMode.ONE_WAY,

 new BoolConverter(true));
 
}

Bindroid provides a number of built-in converters for the most commonly-used cases required by UI bindings. For example, any value can easily be converted to a String using the ToStringConverter. Lists and TrackableCollection<T>s can be easily converted into Adapters so that ListViews can display them properly. Nearly any value can be passed into a BoolConverter in order to transform it into a visibility or a boolean value for toggling a button.

If the built-in converters are insufficient, you can write your own by extending the ValueConverter class. This class has two methods you can override ( convertToSource() and convertToTarget()), one for each direction of the conversion. In the case of UI bindings, the "target" will always be the piece of UI being bound, and the "source" will be your model object. The remainder of the conversion is entirely up to you.

Adapting Existing Classes for Trackability

You may have existing classes that you want to expose as trackable objects that can be bound to using Bindroid. This process requires a little instrumentation, but is relatively straightforward. To set up your objects for trackability, you will use the Trackable class. Any time an accessor to your object is called, you must call trackable.track(). Whenever the value of the thing being accessed changes, you must call trackable.notifyTrackers(). Once you've done that, these values can easily be bound using Bindroid.

For example, let's suppose we have an existing model object ( Foo) using a legacy change notification mechanism. Your ViewModel that wraps this model would then look like this:

public class FooViewModel {

private FooModel model;
private Trackable trackable = new Trackable();

public FooViewModel(FooModel model) {

  this.model = model;
  model.addChangeListener(new ChangeListener() {

 public void propertyChanged() {

trackable.notifyTrackers();

 
}

  
}
);

}

 // Each wrapped property would need to call track()
public String getBar() {

  trackable.track();

  return model.getBar();

}

public void setBar(String value) {

  model.setBar(value);

}

 // Alternatively, you can directly expose the model, but call track()
// on the way in so that any listeners will know to listen for change
// notifications
public FooModel getFoo() {

  trackable.track();

  return model;

}
 
}

If you view the source for EditTextTextProperty, you will see a similar adaptation of an existing property change notification mechanism (in this case the built-in EditText listener) to a Trackable so that it can be used by Bindings.

Memory Management and Threading

One of the biggest potential pitfalls of of using observation patterns between UI and your model objects is that it's extremely easy to inadvertently leak your entire UI hierarchy. Bindroid takes out most of the guess-work here. If you use UiBinder to set up your bindings, they will hold only weak references to your UI, allowing the UI to be garbage collected even while it awaits change notifications from your model. On the other hand, UiBinder will ensure that you model object stays alive as long as any UI is listening for its changes.

Another common nuisance when working with UI is that changes that occur on background threads must notify the UI on the main thread. If you use UiBinder to set up your bindings, Bindroid will take care of this for you, dispatching change notifications to the main thread (only when necessary) to keep your UI up-to-date.

Other Uses

While Bindroid's main goal is to ease UI development, its components are built to provide general binding of any two properties on any two objects. It can be safely used as an adapter between objects of all sorts, setting them up to track and notify each other whenever changes occur. Please don't hesitate to explore the Binding class if you're curious about how to make use of this functionality.

Resources

Interactive Extensions for Java.

RxMarkdown is an Android library that helps to display simple markdown text in android.widget.EditText or android.widget.TextView.

An animated toggling Plus/Check button for Android.

Debug Purge allows you to remove debug apps installed on your mobile.

A simple library for implementing scratchable Views.

This is a small basic sample for use the basic sentences CRUD with realm library in android.

Topics


2D Engines   3D Engines   9-Patch   Action Bars   Activities   ADB   Advertisements   Analytics   Animations   ANR   AOP   API   APK   APT   Architecture   Audio   Autocomplete   Background Processing   Backward Compatibility   Badges   Bar Codes   Benchmarking   Bitmaps   Bluetooth   Blur Effects   Bread Crumbs   BRMS   Browser Extensions   Build Systems   Bundles   Buttons   Caching   Camera   Canvas   Cards   Carousels   Changelog   Checkboxes   Cloud Storages   Color Analysis   Color Pickers   Colors   Comet/Push   Compass Sensors   Conferences   Content Providers   Continuous Integration   Crash Reports   Credit Cards   Credits   CSV   Curl/Flip   Data Binding   Data Generators   Data Structures   Database   Database Browsers   Date &   Debugging   Decompilers   Deep Links   Dependency Injections   Design   Design Patterns   Dex   Dialogs   Distributed Computing   Distribution Platforms   Download Managers   Drawables   Emoji   Emulators   EPUB   Equalizers &   Event Buses   Exception Handling   Face Recognition   Feedback &   File System   File/Directory   Fingerprint   Floating Action   Fonts   Forms   Fragments   FRP   FSM   Functional Programming   Gamepads   Games   Geocaching   Gestures   GIF   Glow Pad   Gradle Plugins   Graphics   Grid Views   Highlighting   HTML   HTTP Mocking   Icons   IDE   IDE Plugins   Image Croppers   Image Loaders   Image Pickers   Image Processing   Image Views   Instrumentation   Intents   Job Schedulers   JSON   Keyboard   Kotlin   Layouts   Library Demos   List View   List Views   Localization   Location   Lock Patterns   Logcat   Logging   Mails   Maps   Markdown   Mathematics   Maven Plugins   MBaaS   Media   Menus   Messaging   MIME   Mobile Web   Native Image   Navigation   NDK   Networking   NFC   NoSQL   Number Pickers   OAuth   Object Mocking   OCR Engines   OpenGL   ORM   Other Pickers   Parallax List   Parcelables   Particle Systems   Password Inputs   PDF   Permissions   Physics Engines   Platforms   Plugin Frameworks   Preferences   Progress Indicators   ProGuard   Properties   Protocol Buffer   Pull To   Purchases   Push/Pull   QR Codes   Quick Return   Radio Buttons   Range Bars   Ratings   Recycler Views   Resources   REST   Ripple Effects   RSS   Screenshots   Scripting   Scroll Views   SDK   Search Inputs   Security   Sensors   Services   Showcase Views   Signatures   Sliding Panels   Snackbars   SOAP   Social Networks   Spannable   Spinners   Splash Screens   SSH   Static Analysis   Status Bars   Styling   SVG   System   Tags   Task Managers   TDD &   Template Engines   Testing   Testing Tools   Text Formatting   Text Views   Text Watchers   Text-to   Toasts   Toolkits For   Tools   Tooltips   Trainings   TV   Twitter   Updaters   USB   User Stories   Utils   Validation   Video   View Adapters   View Pagers   Views   Watch Face   Wearable Data   Wearables   Weather   Web Tools   Web Views   WebRTC   WebSockets   Wheel Widgets   Wi-Fi   Widgets   Windows   Wizards   XML   XMPP   YAML   ZIP Codes