-
-
Save skleeschulte/3d33b29cd3bca8ce41ce to your computer and use it in GitHub Desktop.
Observable String for two-way data binding, based on Fabio Collini's article and Frode Nilsen's gist. This version works without an extra id in the resources and with all TextViews.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.databinding.BaseObservable; | |
import android.databinding.BindingAdapter; | |
import android.databinding.BindingConversion; | |
import android.os.Parcel; | |
import android.os.Parcelable; | |
import android.text.Editable; | |
import android.text.TextWatcher; | |
import android.widget.TextView; | |
import java.io.Serializable; | |
import java.util.Collections; | |
import java.util.Set; | |
import java.util.WeakHashMap; | |
/** | |
* An observable class that holds a String and is capable of two-way data binding with TextViews and | |
* its descendants (e.g. EditText). To be used with Android's Data Binding Library. | |
* | |
* Based on: | |
* - Fabio Collini's great blog article about two-way data binding | |
* (https://medium.com/@fabioCollini/android-data-binding-f9f9d3afc761) and | |
* - Frode Nilsen's implementation of TwoWayBoundString | |
* (https://gist.github.com/Nilzor/05a7db14819d09e45262). | |
* | |
* This version works without defining an id in the resources and with all TextViews. It also | |
* implements the Parcelable interface. | |
* For EditTexts, the cursor is moved to the end after a value change in the model. | |
* | |
* For Android API version < 9, <code>Set<Object> mEmitters</code> must be replaced with e.g. | |
* <code>Map<Object, Boolean> mEmitters</code>, because <code>Collections.newSetFromMap(...)</code> | |
* is not available. | |
* | |
* @author Stefan Kleeschulte | |
*/ | |
public class TwoWayBoundString extends BaseObservable implements TextWatcher, Parcelable, Serializable { | |
static final long serialVersionUID = 1L; | |
private String mValue; | |
private Set<Object> mEmitters = Collections.newSetFromMap(new WeakHashMap<Object, Boolean>()); | |
/** | |
* Creates a TwoWayBoundString with the given initial value. | |
* | |
* @param value the initial value for the TwoWayBoundString | |
*/ | |
public TwoWayBoundString(String value) { | |
mValue = value; | |
} | |
/** | |
* Creates a TwoWayBoundString with the initial value of <code>null</code>. | |
*/ | |
public TwoWayBoundString() { | |
} | |
/** | |
* @return the stored value. | |
*/ | |
public String get() { | |
return mValue; | |
} | |
/** | |
* Set the stored value. | |
*/ | |
public void set(String value) { | |
if (mValue == null && value == null) return; | |
if (mValue == null || !mValue.equals(value)) { | |
mValue = value; | |
notifyChange(); | |
} | |
} | |
/** | |
* Register this TwoWayBoundString instance as TextWatcher for the given TextView if it is not | |
* already registered. | |
* | |
* @param textView the TextView to register on | |
*/ | |
public void registerTextWatcher(TextView textView) { | |
if (!mEmitters.contains(textView)) { | |
mEmitters.add(textView); | |
textView.addTextChangedListener(this); | |
} | |
} | |
/** | |
* BindingAdapter enabling two-way-binding for <code>android:text</code> attributes (for | |
* TextViews, including EditTexts): Updates TextView if TwoWayBoundString value changed and | |
* registers TwoWayBoundString as TextWatcher for the TextView. | |
* | |
* @param textView the TextView to update | |
* @param twoWayBoundString the TwoWayBoundString instance that changed and that shall be | |
* notified about changes in the TextView | |
*/ | |
@BindingAdapter("android:text") | |
public static void bindTextView(TextView textView, final TwoWayBoundString twoWayBoundString) { | |
twoWayBoundString.registerTextWatcher(textView); | |
String newValue = twoWayBoundString.get(); | |
if (!textView.getText().toString().equals(newValue)) { | |
textView.setText(newValue); | |
if (textView instanceof EditText) { | |
((EditText) textView).setSelection(textView.getText().length()); | |
} | |
} | |
} | |
/** | |
* BindingConversion enabling one-way binding for other attributes. | |
* | |
* @param twoWayBoundString the TwoWayBoundString instance that changed | |
* @return plain String object | |
*/ | |
@BindingConversion | |
public static String twoWayBoundStringToString(TwoWayBoundString twoWayBoundString) { | |
return twoWayBoundString.get(); | |
} | |
/** | |
* TextWatcher interface. | |
*/ | |
@Override | |
public void afterTextChanged(Editable s) { | |
} | |
/** | |
* TextWatcher interface. | |
*/ | |
@Override | |
public void beforeTextChanged(CharSequence s, int start, int count, int after) { | |
} | |
/** | |
* TextWatcher interface - updates the value of this TwoWayBoundString instance when a bound | |
* TextView's text changes. | |
*/ | |
@Override | |
public void onTextChanged(CharSequence s, int start, int before, int count) { | |
this.set(s.toString()); | |
} | |
/** | |
* Parcelable interface. | |
*/ | |
@Override | |
public int describeContents() { | |
return 0; | |
} | |
/** | |
* Parcelable interface. | |
*/ | |
@Override | |
public void writeToParcel(Parcel dest, int flags) { | |
dest.writeString(mValue); | |
} | |
/** | |
* Parcelable interface. | |
*/ | |
public static final Parcelable.Creator<TwoWayBoundString> CREATOR = new Parcelable.Creator<TwoWayBoundString>() { | |
@Override | |
public TwoWayBoundString createFromParcel(Parcel source) { | |
return new TwoWayBoundString(source.readString()); | |
} | |
@Override | |
public TwoWayBoundString[] newArray(int size) { | |
return new TwoWayBoundString[size]; | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment