Alejandro Vidal Rodriguez
Agile Engineer @TribalScale
We are HIRING!!!!!
App with two activities and no functionality :D
Repository with steps by tags
git clone git@github.com:antoinecampbell/android-advanced-data-binding.git
git checkout step-1android {
....
dataBinding {
enabled = true
}
}
private void initViews() {
talkImage = (ImageView) findViewById(R.id.talk_detail_image);
talkTitle = (TextView) findViewById(R.id.talk_detail_title);
talkRating = (TextView) findViewById(R.id.talk_detail_rating);
}
ActivityTalkListBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_talk_list);with
@Override
public void onDataAvailable(List<Talk> data) {
binding.listOfTalks.setLayoutManager(new LinearLayoutManager(this));
binding.listOfTalks.setAdapter(new TalkAdapter(data, this, this));
}a class with the XML name in camel case will be created, in our case ActivityTalksListsBinding
public ActivityTalkListBinding(android.databinding.DataBindingComponent bindingComponent, View root) {
super(bindingComponent, root, 0);
final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);
this.headerImage = (android.widget.ImageView) bindings[1];
this.listOfTalks = (android.support.v7.widget.RecyclerView) bindings[2];
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="model"
type="gof.com.databindingtalk.models.Talk"/>
</data>
...
</layout> <TextView
android:id="@+id/talk_title"
style="@style/talk_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{model.title}"
tools:text="Talk title"/>View contactView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.talk_item, parent, false);@Override
public void onBindViewHolder(TalkViewHolder holder, int position) {
Talk currentTalk = talks.get(position);
holder.getBinding().setModel(currentTalk);
Picasso.with(context).load(currentTalk.imageUrl)
.into(holder.getBinding().talkItemImage);
holder.getBinding().executePendingBindings(); // important!
} DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.talk_item, parent, false);from
to
data
or how to create your own XML tags
<ImageView
android:id="@+id/talk_item_image"
android:layout_width="120dp"
android:layout_height="120dp"
android:background="@color/light_gray"
app:loadImageFromUrl="@{model.imageUrl}"/>@BINDINGADAPTER
public class TalkBindingAdapters {
@BindingAdapter("loadImageFromUrl")
public static void loadImageFromUrl(ImageView imageView, String url) {
if (imageView != null) {
Picasso.with(imageView.getContext()).load(url).into(imageView);
}
}
@BindingAdapter({"placeHolder", "loadImageFromUrl"})
public static void loadImageFromUrlWithPlaceHolder(ImageView imageView,
Drawable placeHolder, String url) {
if (imageView != null) {
Picasso.with(imageView.getContext()).load(url)
.placeholder(placeHolder).into(imageView);
}
}
}
binding adapters don't need to be static and can be injected.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="model"
type="gof.com.databindingtalk.models.Talk"/>
<variable
name="navigation"
type="gof.com.databindingtalk.base.AppNavigation"/>
</data>
<android.support.v7.widget.CardView
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> navigation.navigateToTalkDetail(context, model.id)}">
public class Talk {
public String title;
public int rating;
public String imageUrl;
public String id;
public Talk(String title, int rating, String imageUrl, String id) {
this.title = title;
this.rating = rating;
this.imageUrl = imageUrl;
this.id = id;
}
}
public class Talk {
public ObservableInt rating = new ObservableInt();
...
public Talk(String title, int rating, String imageUrl, String id) {
...
this.rating.set(rating);
}
}
from
to
public class TalkListPresenter extends BasePresenter {
public TalkListPresenter() {
}
public void getTalks() {
final List<Talk> talks = new ArrayList<>();
talks.add(new Talk("Databinding with android", 3, "...", "1"));
...
view.onDataAvailable(talks);
delayUpdate(talks);
}
private void delayUpdate(final List<Talk> talks) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
talks.get(0).rating.set(1000);
}
}, 2000);
}
}@BindingAdapter("app:blinkOnChange")
public static void blinkOnChange(TextView textView, int oldContent, int newContent) {
if (oldContent != 0 && textView != null && oldContent != newContent) {
int colorFrom = ContextCompat.getColor(textView.getContext(), R.color.transparent);
int colorTo = ContextCompat.getColor(textView.getContext(), R.color.colorAccent);
int textColorFrom = ContextCompat.getColor(textView.getContext(), R.color.black);
int textColorTo = ContextCompat.getColor(textView.getContext(), R.color.white);
AnimatorSet animations = new AnimatorSet();
AnimatorSet inBlink = new AnimatorSet();
inBlink.playTogether(UIUtils.getValueBackgroundAnimator(textView, colorFrom, colorTo),
UIUtils.getValueTextColorAnimator(textView, textColorFrom, textColorTo));
AnimatorSet outBlink = new AnimatorSet();
outBlink.playTogether(UIUtils.getValueBackgroundAnimator(textView, colorTo, colorFrom),
UIUtils.getValueTextColorAnimator(textView, textColorTo, textColorFrom));
animations.playSequentially(inBlink, outBlink);
animations.start();
}
}any binding adapter can receive old and new values
<TextView
android:id="@+id/talk_title"
style="@style/talk_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{model.title}"
tools:text="Talk title"/>if we can get model updates with:
<EditText
android:id="@+id/talk_title"
style="@style/talk_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={model.title}"
tools:text="Talk title"/>we can update our model with:
android:onClick="@{() -> service.purchaseItem(model.itemId)}">
android:visibility="@{model.age > 18 ?
Collection.split(model.name).get(0,8) :
String.format(model.surname + model.country)}"
android:visibility="@{model.publicName}"
@BindingAdapter({AUTO_SCROLL_LINEAR_LAYOUT, ELAPSED_TIME})
public static void autoScrollRecyclerView(final RecyclerView view, final boolean autoScroll, final long elapsed) {
if (!autoScroll) return;
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
TimerTask task;
@Override
public void onViewAttachedToWindow(View v) {
Timer autoScrollTimer = new Timer();
RecyclerView recyclerView = (RecyclerView) v;
recyclerView.smoothScrollToPosition(0);
task = new TimerTask() {
@Override
public void run() {
new UiJob(() -> {
if (recyclerView != null) {
final LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (manager != null) {
int current = manager.findFirstVisibleItemPosition();
int count = manager.getItemCount();
recyclerView.smoothScrollToPosition(current == count - 1 ? 0 : current + 1);
}
}
});
}
};
autoScrollTimer.scheduleAtFixedRate(task, elapsed, elapsed);
}
@Override
public void onViewDetachedFromWindow(View v) {
task.cancel();
}
});
}contact
goofyahead@gmail.com
https://www.linkedin.com/in/alejandrovidalrodriguez
https://medium.com/@goofyahead
https://github.com/goofyahead