Dominando o Data Binding

no Android
+Nelson Glauber
@nglauber
www.nglauber.com.br
@nglauber
+NelsonGlauber
www.nglauber.com.br
Por que Data Binding?
Estudo de caso
public class Book {
private String id;
private String title;
private String author;
private String coverUrl;
private int pages;
private int year;
private Publisher publisher;
private boolean available;
private MediaType mediaType;
private float rating;
// Getters and setters...
}
Book book = (Book)getIntent().getSerializableExtra(EXTRA_LIVRO);
ImageView imgCover = (ImageView) findViewById(R.id.image_cover);
TextView txtTitle = (TextView) findViewById(R.id.text_title);
TextView txtAuthor = (TextView) findViewById(R.id.text_author);
TextView txtPages = (TextView) findViewById(R.id.text_pages);
TextView txtYear = (TextView) findViewById(R.id.text_year);
TextView txtPublisher = (TextView) findViewById(R.id.text_publisher);
TextView txtAvailable = (TextView) findViewById(R.id.text_available);
TextView txtMediaType = (TextView) findViewById(R.id.text_media_type);
RatingBar ratingBook = (RatingBar) findViewById(R.id.rating_book);
Glide.with(this).load(book.getCoverUrl()).into(imgCover);
txtTitle.setText(book.getTitle());
txtAuthor.setText(book.getAuthor());
txtPages.setText(getString(R.string.text_format_book_pages, book.getPages()));
txtYear.setText(getString(R.string.text_format_book_year, book.getYear()));
txtPublisher.setText(book.getPublisher().getName());
txtAvailable.setText(book.isAvailable() ?
R.string.text_book_available : R.string.text_book_unavailable);
txtMediaType.setText(book.getMediaType().toString());
ratingBook.setNumStars(book.getRating());
book.setCoverUrl(imageFile.getAbsolutePath());
book.setTitle(editPages.getText().toString());
book.setAuthor(editAuthor.getText().toString());
book.setPages(Integer.parseInt(editPages.getText().toString()));
book.setYear(Integer.parseInt(editYear.getText().toString()));
book.setPublisher((Publisher) spinnerPublisher.getSelectedItem());
book.setAvailable(checkAvailable.isChecked());
book.setMediaTypeValue(radioMediaEbook.isChecked() ?

MediaType.EBOOK : MediaType.PAPER);
book.setRating(ratingBook.getRating());
saveBook(book);
Book book = (Book)getIntent().getSerializableExtra(EXTRA_BOOK);
ActivityDetailViewBinding mBinding =

DataBindingUtil.setContentView(this, R.layout.activity_detail_view);
mBinding.setBook(book);
Conceitos básicos
Configuração
Passo 1
Não tem passo 2 o/
android {
...
dataBinding {
enabled true
}
}
res/layout/activity_main.xml
activity_main.xml
ActivityMainBinding
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout ...>
<TextView ...
android:id=“@+id/text_view_name”
android:text="Hello World!" />
</LinearLayout>
</layout>
public class MainActivity extends AppCompatActivity {
ActivityMainBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mBinding.textViewName.setText(“DataBinding is cool!!!”);
}
}
MainActivity.java
camelCase
Se não gostar do nome da classe…
MainActivity.java
res/layout/activity_main.xml
<layout ...>
<data class="AwsomeBinding">
...
</data>
public class MainActivity extends AppCompatActivity {
AwsomeBinding binding;
...
Mapeando objetos na UI
Book book = (Book)getIntent().getSerializableExtra(EXTRA_LIVRO);
ImageView imgCover = (ImageView) findViewById(R.id.image_cover);
TextView txtTitle = (TextView) findViewById(R.id.text_title);
TextView txtAuthor = (TextView) findViewById(R.id.text_author);
TextView txtPages = (TextView) findViewById(R.id.text_pages);
TextView txtYear = (TextView) findViewById(R.id.text_year);
TextView txtPublisher = (TextView) findViewById(R.id.text_publisher);
TextView txtAvailable = (TextView) findViewById(R.id.text_available);
TextView txtMediaType = (TextView) findViewById(R.id.text_media_type);
RatingBar ratingBook = (RatingBar) findViewById(R.id.rating_book);
Glide.with(this).load(book.getCoverUrl()).into(imgCover);
txtTitle.setText(book.getTitle());
txtAuthor.setText(book.getAuthor());
txtPages.setText(getString(R.string.text_format_book_pages, book.getPages()));
txtYear.setText(getString(R.string.text_format_book_year, book.getYear()));
txtPublisher.setText(book.getPublisher().getName());
txtAvailable.setText(book.isAvailable() ?
R.string.text_book_available : R.string.text_book_unavailable);
txtMediaType.setText(book.getMediaType().toString());
ratingBook.setNumStars(book.getRating());
Book book = (Book)getIntent().getSerializableExtra(EXTRA_BOOK);
ActivityDetailViewBinding mBinding =

DataBindingUtil.setContentView(this, R.layout.activity_detail_view);
mBinding.setBook(book);
<layout xmlns:android="http://schemas.android.com/apk/res/android" ...>
<data>
<variable
name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
</data>
<LinearLayout ... >
<ImageView android:src="@{book.coverUrl}" ... />
<TextView android:text="@{book.title}" ... />
<TextView android:text="@{book.author}" ... />
<TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... />
<TextView android:text="@{@string/text_format_book_year(book.year)}" ... />
<TextView android:text="@{book.publisher.name}" ... />
<TextView android:text="@{book.available ?
@string/text_book_available :
@string/text_book_unavailable}" ... />
<TextView android:text="@{book.mediaTypeValue}" ... />
<RatingBar android:rating="@{book.rating}" ... />
</LinearLayout>
</layout>
<layout xmlns:android="http://schemas.android.com/apk/res/android" ...>
<data>
<variable
name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
</data>
<LinearLayout ... >
<ImageView android:src="@{book.coverUrl}" ... />
<TextView android:text="@{book.title}" ... />
<TextView android:text="@{book.author}" ... />
<TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... />
<TextView android:text="@{@string/text_format_book_year(book.year)}" ... />
<TextView android:text="@{book.publisher.name}" ... />
<TextView android:text="@{book.available ?
@string/text_book_available :
@string/text_book_unavailable}" ... />
<TextView android:text="@{book.mediaTypeValue}" ... />
<RatingBar android:progress="@{book.rating}" ... />
</LinearLayout>
</layout>
<!-- strings.xml -->
<string name="text_format_book_pages">Número de páginas: %1$d</string>
<string name="text_format_book_year">Ano de publicação: %1$d</string>
<layout xmlns:android="http://schemas.android.com/apk/res/android" ...>
<data>
<variable
name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
</data>
<LinearLayout ... >
<ImageView android:src="@{book.coverUrl}" ... />
<TextView android:text="@{book.title}" ... />
<TextView android:text="@{book.author}" ... />
<TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... />
<TextView android:text="@{@string/text_format_book_year(book.year)}" ... />
<TextView android:text="@{book.publisher.name}" ... />
<TextView android:text="@{book.available ?
@string/text_book_available :
@string/text_book_unavailable}" ... />
<TextView android:text="@{book.mediaType}" ... />
<RatingBar android:progress="@{book.rating}" ... />
</LinearLayout>
</layout>
Tá pronto? Pode
rodar???
NÃO!
<layout xmlns:android="http://schemas.android.com/apk/res/android" ...>
<data>
<variable
name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
</data>
<LinearLayout ... >
<ImageView android:src="@{book.coverUrl}" ... />
<TextView android:text="@{book.title}" ... />
<TextView android:text="@{book.author}" ... />
<TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... />
<TextView android:text="@{@string/text_format_book_year(book.year)}" ... />
<TextView android:text="@{book.publisher.name}" ... />
<TextView android:text="@{book.available ?
@string/text_book_available :
@string/text_book_unavailable}" ... />
<TextView android:text="@{book.mediaTypeValue}" ... />
<RatingBar android:progress="@{book.rating}" ... />
</LinearLayout>
</layout>
Binding Adapters #1
public class TextBinding {
@BindingAdapter({"android:text"})
public static void setMediaTypeText(TextView textView, MediaType mediaType){
Context context = textView.getContext();
switch (mediaType) {
case EBOOK:
textView.setText(context.getString(R.string.text_book_media_ebook));
break;
case PAPER:
textView.setText(context.getString(R.string.text_book_media_paper));
break;
default:
textView.setText(null);
}
}
}
TextBinding.java
<ImageView
android:id="@+id/image_cover"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:background="#CCC"
android:scaleType="centerCrop"
android:src="@{book.coverUrl}"
app:placeHolder=“@{@drawable/ic_photo}”/>
res/values/attrs.xml
<attr name="placeHolder" format="reference"/>
public class ImageBinding {
@BindingAdapter({"android:src"})
public static void setImageUrl(ImageView imageView, String url){
Glide.with(imageView.getContext())
.load(url)
.into(imageView);
}
@BindingAdapter({"android:src", "placeHolder"})
public static void setImageUrl(ImageView imageView, String url,
Drawable placeholder){
Glide.with(imageView.getContext())
.load(url)
.placeholder(placeholder)
.into(imageView);
}
}
ImageBinding.java
E se os dados mudarem?
public class Book extends BaseObservable {
// Atributos...
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
notifyPropertyChanged(BR.title);
}
@Bindable
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
notifyPropertyChanged(BR.author);
}
// Demais getters/setters
Book.java
public class Book implements Observable {
private PropertyChangeRegistry mCallbacks;
// Copia as coisas do BaseObservable :)
}
public class BaseObservable implements Observable {
private transient PropertyChangeRegistry mCallbacks;
public BaseObservable() {
}
@Override
public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
mCallbacks.add(callback);
}
@Override
public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
if (mCallbacks != null) {
mCallbacks.remove(callback);
}
}
public synchronized void notifyChange() {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, 0, null);
}
}
public void notifyPropertyChanged(int fieldId) {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
}
import android.databinding.ObservableBoolean;
import android.databinding.ObservableField;
import android.databinding.ObservableInt;
public class Book {
//...
private final ObservableField<String> title;
private final ObservableInt year;
private final ObservableBoolean available;
public Book() {
//...
this.title = new ObservableField<>();
this.year = new ObservableInt(2016);
this.available = new ObservableBoolean(false);
}
public ObservableField<String> getTitle() { return title; }
public void setTitle(String title) { this.title.set(title); }
public ObservableInt getYear() { return year; }
public void setYear(double year) { this.year.set(year); }
public ObservableBoolean isAvailable() { return available; }
public void setAvailable(boolean available) { this.available.set(available); }
}
Fragments e Adapters
BookFragment.java
private FragmentBookBinding mBinding;
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
mBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_book,
container, false);
...
return mBinding.getRoot();
}
res/layout/item_book.xml
<layout ...>
<data>
<variable
name="book"
type="br.com.nglauber.livrosfirebase.model.Book"/>
</data>
<android.support.v7.widget.CardView ...>
<RelativeLayout ...>
<ImageView android:src="@{book.coverUrl}" ... />
<TextView android:text="@{book.title}" ... />
<TextView android:text="@{book.author}" ... />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
BookViewHolder.java
public static class BookViewHolder extends RecyclerView.ViewHolder {
ItemBookBinding binding;
public BookViewHolder(ItemBookBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
@Override
public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ItemBookBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_book,
parent,
false);
final BookViewHolder vh = new BookViewHolder(binding);
vh.itemView.setOnClickListener(...);
return vh;
}
@Override
public void onBindViewHolder(BookViewHolder holder, int pos) {
Book book = mBooksList.get(pos);
holder.binding.setBook(book);
holder.binding.executePendingBindings();
}
BookAdapter.java
Two-way Data Binding
<layout ...>
<data>
<variable name="texto" type="String"/>
</data>
<LinearLayout ... >
<TextView ...
android:id="@+id/textViewNome"
android:text="@{texto}" />
<EditText ...
android:id="@+id/editTextNome"
android:text="@={texto}"/>
</LinearLayout>
</layout>
<layout ...>
<data>
...
<import type="br.com.nglauber.livrosfirebase.model.MediaType" />
<variable name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
</data>
<LinearLayout ...>
<ImageView android:src="@{book.coverUrl}" ... />
<EditText android:text="@={book.title}" ... />
<EditText android:text="@={book.author}" ... />
<EditText android:text="@={book.pages}" ... />
<EditText android:text="@={book.year}" ... />
<Spinner ...> <!-- trataremos desse em breve. -->
<CheckBox android:checked="@={book.available}" ... />
<RadioGroup ...>
<RadioButton ...
android:checked="@{book.mediaTypeValue == MediaType.EBOOK}" ... />
<RadioButton ...
android:checked="@{book.mediaTypeValue == MediaType.PAPER}" ... />
</RadioGroup>
<RatingBar
android:rating="@={book.rating}" ... />
...
</LinearLayout>
</layout>
<layout ...>
<data>
...
<import type="br.com.nglauber.livrosfirebase.model.MediaType" />
<variable name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
</data>
<LinearLayout ...>
<ImageView android:src="@{book.coverUrl}" ... />
<EditText android:text="@={book.title}" ... />
<EditText android:text="@={book.author}" ... />
<EditText android:text="@={book.pages}" ... />
<EditText android:text="@={book.year}" ... />
<Spinner ...> <!-- trataremos desse em breve. -->
<CheckBox android:checked="@={book.available}" ... />
<RadioGroup ...>
<RadioButton ...
android:checked="@{book.mediaTypeValue == MediaType.EBOOK}" ... />
<RadioButton ...
android:checked="@{book.mediaTypeValue == MediaType.PAPER}" ... />
</RadioGroup>
<RatingBar
android:progress="@={book.rating}" ... />
...
</LinearLayout>
</layout>
Binding Adapters #2
Cuidado com loops!
EditText

android:text="@={book.title}"
public class Book extends BaseObservable {
// Atributos...
@Bindable
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
notifyPropertyChanged(BR.title);
}
// Demais getters/setters
public class EditTextBindingAdapters {
@InverseBindingAdapter(attribute = "android:text")
public static int getTextAsInt(EditText editText) {
try {
return Integer.parseInt(editText.getText().toString());
} catch (Exception e){
return 0;
}
}
@BindingAdapter({"android:text"})
public static void setTextFromInt(EditText editText, int value){
if (getTextAsInt(editText) != value) {
editText.setText(String.valueOf(value));
}
}
}
EditTextBindingAdapters.java
Tratamento de eventos
<layout ...>
<data>
...
<variable
name="presenter"
type=“br.com.nglauber.livrosfirebase.DetailEditActivity"/>
</data>
<LinearLayout ...>
...
<TextView ...
android:onLongClick="@{presenter::longClick}" />
<Button ...
android:onClick=“@{presenter::clickSaveBook}”/>
<EditText ...
android:onFocusChange="@{presenter::focusChanged}"/>
</LinearLayout>
</layout>
res/layout/activity_detail_edit.xml
@Override
protected void onCreate(Bundle savedInstanceState) {
...
binding.setBook(book);
binding.setPresenter(this);
}
public void clickSaveBook(View view) {
Toast.makeText(this, book.toString(), Toast.LENGTH_SHORT).show();
}
public boolean longClick(View view){
book.setRating(book.getRating() + 1);
return true;
}
public void focusChanged(View v, boolean focus){
if (!focus) showProduct(v);
}
DetailEditActivity.java
Respeite a
assinatura do
método
<layout ...>
<data>
<import type="br.com.nglauber.livrosfirebase.model.MediaType" />
...
<variable
name="presenter"
type="br.com.nglauber.livrosfirebase.DetailEditActivity" />
</data>
<LinearLayout ...>
...
<RadioGroup ...>
<RadioButton ...
android:checked="@{book.mediaTypeValue == MediaType.EBOOK}"
android:onCheckedChanged="@{presenter::onMediaTypeChanged}" />
<RadioButton ...
android:checked="@{book.mediaTypeValue == MediaType.PAPER}"
android:onCheckedChanged="@{presenter::onMediaTypeChanged}" />
</RadioGroup>
...
</LinearLayout>
</layout>
public void onMediaTypeChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
if (buttonView == mBinding.radioMediaEbook) {
mBinding.getBook().setMediaTypeValue(MediaType.EBOOK);
} else if (buttonView == mBinding.radioMediaPaper) {
mBinding.getBook().setMediaTypeValue(MediaType.PAPER);
}
}
}
public class Publisher {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
public class Book extends BaseObservable {
private Publisher publisher;
...
@Bindable
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
notifyPropertyChanged(BR.publisher);
}
}
<layout ...>
<data>
<import type="java.util.List"/>
<variable name="book"
type="br.com.nglauber.livrosfirebase.model.Book" />
<variable name="presenter"
type="br.com.nglauber.livrosfirebase.DetailEditActivity" />
<variable name="publishers"
type="java.util.List&lt;br.com.nglauber.livrosfirebase.model.Publisher&gt;" />
</data>

<LinearLayout ...>
<Spinner ...
android:entries="@{publishers}"
android:selection="@{publishers.indexOf(book.publisher)}"
android:onItemSelected="@{(p, v, pos, id)->book.setPublisher(publishers[pos])}" />
</LinearLayout>
</layout>
DetailEditActivity.java
ObservableList<Publisher> publishers = loadListFromSomewhere();
mBinding.setPublishers(publishers);
<layout ...>
<data>
<variable
name="book"
type="br.com.nglauber.livrosfirebase.model.Book"/>
<variable
name="presenter"
type=“br.com.nglauber.livrosfirebase.DetailEditActivity"/>
</data>
<LinearLayout ...>
...
<Button ...
android:onClick="@{()->presenter.saveBook(book)}"/>
</LinearLayout>
</layout>
Pronto!
http://jakewharton.github.io/butterknife/
Binding Adapters #3
https://github.com/lisawray/fontbinding
public class TextViewBinderAdapter {
@BindingAdapter({"font"})
public static void setFont(TextView textView,String font){
AssetManager assets = textView.getContext().getAssets();
Typeface typeface = Typeface.createFromAsset(assets, font);
textView.setTypeface(typeface);
}
}
<TextView app:font="@{`FunnyKid.ttf`}" .../>
Mas se tiver um “set” pode usar!
<android.support.v4.widget.DrawerLayout ...
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
Miscellaneous
Expression Language
<TextView ...
android:text="@{user.displayName ?? user.lastName}"/>
<TextView ...
android:text="@{`User: ` + user.name}”/>
<LinearLayout ...
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"/>
res/values/colors.xml
<color name="red">#F00</color>
<color name="black">#000</color>
<TextView ...
android:id="@+id/text_title"
android:textColor="@{book.available ? @color/black : @color/red}"
android:text="@{book.title}" />
res/values/strings.xml
<string name="price_format">$ %1$.2f</string>
<TextView ...
android:id="@+id/text_price"
android:text="@{@string/price_format(book.price)}" />
getString(R.string.price_format, product.price);
Include
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="book" type=“...Book”/>
<variable name=“presenter" type=“...DetailEditActivity" />
</data>
<LinearLayout ...>
<include
android:id="@+id/content"
layout="@layout/content_detail_edit"
app:presenter="@{presenter}"
app:book="@{book}" />
</LinearLayout>
</layout>
Include
ActivityDetailEditBinding mBinding =
DataBindingUtil.setContentView(this, R.layout.activity_detail_edit);
mBinding.setBook(book);
mBinding.setPresenter(this);
mBinding.content.setPublishers(new ObservableArrayList<Publisher>());
Expression Chain
<layout ...>
<data>
<import type="android.view.View" />
...
</data>
<LinearLayout ...>
<ImageView ...
android:id="@+id/image_cover"
android:visibility="@{book.available ? View.VISIBLE : View.INVISIBLE}" />
<RadioGroup ...
android:id="@+id/radio_group_media_type"
android:visibility="@{imageCover.visibility}" />
<RatingBar
android:visibility="@{radioGroupMediaType.visibility}"
android:id=“@+id/rating_book">
<CheckBox ...
android:id="@+id/check_available"
android:checked="@={book.available}" />
Ouvindo coisas… :)
binding.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
if (i == br.com.nglauber.uidemo.BR.name){
Product p = (Product)observable;
Log.d("NGVL", p.getName());
}
}
});
Ouvindo coisas… :)
Use para
animações
binding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
return super.onPreBind(binding);
}
@Override
public void onCanceled(ViewDataBinding binding) {
super.onCanceled(binding);
}
@Override
public void onBound(ViewDataBinding binding) {
super.onBound(binding);
}
});
Aplicando no seu projeto
1. Remova os findViewById’s :)
2. Faça o Binding dos seus objetos.
3. Use os callbacks (eventos) de UI.
4. Aproveite os objetos observáveis.
5. Two-way Data Binding é vida! ♥
Boas práticas
• Não coloque lógica de negócios na tela. Apenas lógica de tela.
• Os eventos disparados executarão na UI Thread.
• Simplifique a lógica de UI (não coloque expressões complicadas,
crie métodos para isso.
• Considere utilizar um ViewModel.
• Leve os BinderAdapters com você :) Você só vai precisar escrevê-
los uma vez. // TODO: Fazer uma lib para isso :p
Referências
• Data Binding -- Write Apps Faster (Android Dev Summit 2015)

https://www.youtube.com/watch?v=NBbeQMOcnZ0
• Data Binding Documentation (pode melhorar né…?)

http://developer.android.com/tools/data-binding/guide.html
• Nelson Glauber Blog

http://www.nglauber.com.br/2016/05/android-data-binding.html
• Advanced Data Binding (Google I/O 2016)

https://www.youtube.com/watch?v=DAmMN7m3wLU
Dúvidas?
@nglauber
+NelsonGlauber
www.nglauber.com.br
Obrigado!

Dominando o Data Binding no Android

  • 1.
    Dominando o DataBinding
 no Android +Nelson Glauber @nglauber www.nglauber.com.br
  • 2.
  • 3.
    Por que DataBinding?
  • 5.
  • 6.
    public class Book{ private String id; private String title; private String author; private String coverUrl; private int pages; private int year; private Publisher publisher; private boolean available; private MediaType mediaType; private float rating; // Getters and setters... }
  • 7.
    Book book =(Book)getIntent().getSerializableExtra(EXTRA_LIVRO); ImageView imgCover = (ImageView) findViewById(R.id.image_cover); TextView txtTitle = (TextView) findViewById(R.id.text_title); TextView txtAuthor = (TextView) findViewById(R.id.text_author); TextView txtPages = (TextView) findViewById(R.id.text_pages); TextView txtYear = (TextView) findViewById(R.id.text_year); TextView txtPublisher = (TextView) findViewById(R.id.text_publisher); TextView txtAvailable = (TextView) findViewById(R.id.text_available); TextView txtMediaType = (TextView) findViewById(R.id.text_media_type); RatingBar ratingBook = (RatingBar) findViewById(R.id.rating_book); Glide.with(this).load(book.getCoverUrl()).into(imgCover); txtTitle.setText(book.getTitle()); txtAuthor.setText(book.getAuthor()); txtPages.setText(getString(R.string.text_format_book_pages, book.getPages())); txtYear.setText(getString(R.string.text_format_book_year, book.getYear())); txtPublisher.setText(book.getPublisher().getName()); txtAvailable.setText(book.isAvailable() ? R.string.text_book_available : R.string.text_book_unavailable); txtMediaType.setText(book.getMediaType().toString()); ratingBook.setNumStars(book.getRating());
  • 8.
  • 9.
    Book book =(Book)getIntent().getSerializableExtra(EXTRA_BOOK); ActivityDetailViewBinding mBinding =
 DataBindingUtil.setContentView(this, R.layout.activity_detail_view); mBinding.setBook(book);
  • 10.
  • 11.
    Configuração Passo 1 Não tempasso 2 o/ android { ... dataBinding { enabled true } }
  • 12.
  • 13.
    public class MainActivityextends AppCompatActivity { ActivityMainBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); mBinding.textViewName.setText(“DataBinding is cool!!!”); } } MainActivity.java camelCase
  • 15.
    Se não gostardo nome da classe… MainActivity.java res/layout/activity_main.xml <layout ...> <data class="AwsomeBinding"> ... </data> public class MainActivity extends AppCompatActivity { AwsomeBinding binding; ...
  • 16.
  • 17.
    Book book =(Book)getIntent().getSerializableExtra(EXTRA_LIVRO); ImageView imgCover = (ImageView) findViewById(R.id.image_cover); TextView txtTitle = (TextView) findViewById(R.id.text_title); TextView txtAuthor = (TextView) findViewById(R.id.text_author); TextView txtPages = (TextView) findViewById(R.id.text_pages); TextView txtYear = (TextView) findViewById(R.id.text_year); TextView txtPublisher = (TextView) findViewById(R.id.text_publisher); TextView txtAvailable = (TextView) findViewById(R.id.text_available); TextView txtMediaType = (TextView) findViewById(R.id.text_media_type); RatingBar ratingBook = (RatingBar) findViewById(R.id.rating_book); Glide.with(this).load(book.getCoverUrl()).into(imgCover); txtTitle.setText(book.getTitle()); txtAuthor.setText(book.getAuthor()); txtPages.setText(getString(R.string.text_format_book_pages, book.getPages())); txtYear.setText(getString(R.string.text_format_book_year, book.getYear())); txtPublisher.setText(book.getPublisher().getName()); txtAvailable.setText(book.isAvailable() ? R.string.text_book_available : R.string.text_book_unavailable); txtMediaType.setText(book.getMediaType().toString()); ratingBook.setNumStars(book.getRating());
  • 18.
    Book book =(Book)getIntent().getSerializableExtra(EXTRA_BOOK); ActivityDetailViewBinding mBinding =
 DataBindingUtil.setContentView(this, R.layout.activity_detail_view); mBinding.setBook(book);
  • 19.
    <layout xmlns:android="http://schemas.android.com/apk/res/android" ...> <data> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book"/> </data> <LinearLayout ... > <ImageView android:src="@{book.coverUrl}" ... /> <TextView android:text="@{book.title}" ... /> <TextView android:text="@{book.author}" ... /> <TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... /> <TextView android:text="@{@string/text_format_book_year(book.year)}" ... /> <TextView android:text="@{book.publisher.name}" ... /> <TextView android:text="@{book.available ? @string/text_book_available : @string/text_book_unavailable}" ... /> <TextView android:text="@{book.mediaTypeValue}" ... /> <RatingBar android:rating="@{book.rating}" ... /> </LinearLayout> </layout>
  • 20.
    <layout xmlns:android="http://schemas.android.com/apk/res/android" ...> <data> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book"/> </data> <LinearLayout ... > <ImageView android:src="@{book.coverUrl}" ... /> <TextView android:text="@{book.title}" ... /> <TextView android:text="@{book.author}" ... /> <TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... /> <TextView android:text="@{@string/text_format_book_year(book.year)}" ... /> <TextView android:text="@{book.publisher.name}" ... /> <TextView android:text="@{book.available ? @string/text_book_available : @string/text_book_unavailable}" ... /> <TextView android:text="@{book.mediaTypeValue}" ... /> <RatingBar android:progress="@{book.rating}" ... /> </LinearLayout> </layout> <!-- strings.xml --> <string name="text_format_book_pages">Número de páginas: %1$d</string> <string name="text_format_book_year">Ano de publicação: %1$d</string>
  • 21.
    <layout xmlns:android="http://schemas.android.com/apk/res/android" ...> <data> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book"/> </data> <LinearLayout ... > <ImageView android:src="@{book.coverUrl}" ... /> <TextView android:text="@{book.title}" ... /> <TextView android:text="@{book.author}" ... /> <TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... /> <TextView android:text="@{@string/text_format_book_year(book.year)}" ... /> <TextView android:text="@{book.publisher.name}" ... /> <TextView android:text="@{book.available ? @string/text_book_available : @string/text_book_unavailable}" ... /> <TextView android:text="@{book.mediaType}" ... /> <RatingBar android:progress="@{book.rating}" ... /> </LinearLayout> </layout>
  • 22.
  • 23.
  • 24.
    <layout xmlns:android="http://schemas.android.com/apk/res/android" ...> <data> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book"/> </data> <LinearLayout ... > <ImageView android:src="@{book.coverUrl}" ... /> <TextView android:text="@{book.title}" ... /> <TextView android:text="@{book.author}" ... /> <TextView android:text="@{@string/text_format_book_pages(book.pages)}" ... /> <TextView android:text="@{@string/text_format_book_year(book.year)}" ... /> <TextView android:text="@{book.publisher.name}" ... /> <TextView android:text="@{book.available ? @string/text_book_available : @string/text_book_unavailable}" ... /> <TextView android:text="@{book.mediaTypeValue}" ... /> <RatingBar android:progress="@{book.rating}" ... /> </LinearLayout> </layout>
  • 25.
  • 26.
    public class TextBinding{ @BindingAdapter({"android:text"}) public static void setMediaTypeText(TextView textView, MediaType mediaType){ Context context = textView.getContext(); switch (mediaType) { case EBOOK: textView.setText(context.getString(R.string.text_book_media_ebook)); break; case PAPER: textView.setText(context.getString(R.string.text_book_media_paper)); break; default: textView.setText(null); } } } TextBinding.java
  • 27.
  • 28.
    public class ImageBinding{ @BindingAdapter({"android:src"}) public static void setImageUrl(ImageView imageView, String url){ Glide.with(imageView.getContext()) .load(url) .into(imageView); } @BindingAdapter({"android:src", "placeHolder"}) public static void setImageUrl(ImageView imageView, String url, Drawable placeholder){ Glide.with(imageView.getContext()) .load(url) .placeholder(placeholder) .into(imageView); } } ImageBinding.java
  • 30.
    E se osdados mudarem?
  • 31.
    public class Bookextends BaseObservable { // Atributos... @Bindable public String getTitle() { return title; } public void setTitle(String title) { this.title = title; notifyPropertyChanged(BR.title); } @Bindable public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; notifyPropertyChanged(BR.author); } // Demais getters/setters Book.java
  • 32.
    public class Bookimplements Observable { private PropertyChangeRegistry mCallbacks; // Copia as coisas do BaseObservable :) }
  • 33.
    public class BaseObservableimplements Observable { private transient PropertyChangeRegistry mCallbacks; public BaseObservable() { } @Override public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) { if (mCallbacks == null) { mCallbacks = new PropertyChangeRegistry(); } mCallbacks.add(callback); } @Override public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) { if (mCallbacks != null) { mCallbacks.remove(callback); } } public synchronized void notifyChange() { if (mCallbacks != null) { mCallbacks.notifyCallbacks(this, 0, null); } } public void notifyPropertyChanged(int fieldId) { if (mCallbacks != null) { mCallbacks.notifyCallbacks(this, fieldId, null); } } }
  • 34.
    import android.databinding.ObservableBoolean; import android.databinding.ObservableField; importandroid.databinding.ObservableInt; public class Book { //... private final ObservableField<String> title; private final ObservableInt year; private final ObservableBoolean available; public Book() { //... this.title = new ObservableField<>(); this.year = new ObservableInt(2016); this.available = new ObservableBoolean(false); } public ObservableField<String> getTitle() { return title; } public void setTitle(String title) { this.title.set(title); } public ObservableInt getYear() { return year; } public void setYear(double year) { this.year.set(year); } public ObservableBoolean isAvailable() { return available; } public void setAvailable(boolean available) { this.available.set(available); } }
  • 35.
  • 36.
    BookFragment.java private FragmentBookBinding mBinding; @Override publicView onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_book, container, false); ... return mBinding.getRoot(); }
  • 37.
    res/layout/item_book.xml <layout ...> <data> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book"/> </data> <android.support.v7.widget.CardView ...> <RelativeLayout...> <ImageView android:src="@{book.coverUrl}" ... /> <TextView android:text="@{book.title}" ... /> <TextView android:text="@{book.author}" ... /> </RelativeLayout> </android.support.v7.widget.CardView> </layout>
  • 38.
    BookViewHolder.java public static classBookViewHolder extends RecyclerView.ViewHolder { ItemBookBinding binding; public BookViewHolder(ItemBookBinding binding) { super(binding.getRoot()); this.binding = binding; } }
  • 39.
    @Override public BookViewHolder onCreateViewHolder(ViewGroupparent, int viewType) { ItemBookBinding binding = DataBindingUtil.inflate( LayoutInflater.from(parent.getContext()), R.layout.item_book, parent, false); final BookViewHolder vh = new BookViewHolder(binding); vh.itemView.setOnClickListener(...); return vh; } @Override public void onBindViewHolder(BookViewHolder holder, int pos) { Book book = mBooksList.get(pos); holder.binding.setBook(book); holder.binding.executePendingBindings(); } BookAdapter.java
  • 41.
  • 42.
    <layout ...> <data> <variable name="texto"type="String"/> </data> <LinearLayout ... > <TextView ... android:id="@+id/textViewNome" android:text="@{texto}" /> <EditText ... android:id="@+id/editTextNome" android:text="@={texto}"/> </LinearLayout> </layout>
  • 44.
    <layout ...> <data> ... <import type="br.com.nglauber.livrosfirebase.model.MediaType"/> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book" /> </data> <LinearLayout ...> <ImageView android:src="@{book.coverUrl}" ... /> <EditText android:text="@={book.title}" ... /> <EditText android:text="@={book.author}" ... /> <EditText android:text="@={book.pages}" ... /> <EditText android:text="@={book.year}" ... /> <Spinner ...> <!-- trataremos desse em breve. --> <CheckBox android:checked="@={book.available}" ... /> <RadioGroup ...> <RadioButton ... android:checked="@{book.mediaTypeValue == MediaType.EBOOK}" ... /> <RadioButton ... android:checked="@{book.mediaTypeValue == MediaType.PAPER}" ... /> </RadioGroup> <RatingBar android:rating="@={book.rating}" ... /> ... </LinearLayout> </layout>
  • 45.
    <layout ...> <data> ... <import type="br.com.nglauber.livrosfirebase.model.MediaType"/> <variable name="book" type="br.com.nglauber.livrosfirebase.model.Book" /> </data> <LinearLayout ...> <ImageView android:src="@{book.coverUrl}" ... /> <EditText android:text="@={book.title}" ... /> <EditText android:text="@={book.author}" ... /> <EditText android:text="@={book.pages}" ... /> <EditText android:text="@={book.year}" ... /> <Spinner ...> <!-- trataremos desse em breve. --> <CheckBox android:checked="@={book.available}" ... /> <RadioGroup ...> <RadioButton ... android:checked="@{book.mediaTypeValue == MediaType.EBOOK}" ... /> <RadioButton ... android:checked="@{book.mediaTypeValue == MediaType.PAPER}" ... /> </RadioGroup> <RatingBar android:progress="@={book.rating}" ... /> ... </LinearLayout> </layout>
  • 46.
  • 47.
    Cuidado com loops! EditText
 android:text="@={book.title}" publicclass Book extends BaseObservable { // Atributos... @Bindable public String getTitle() { return title; } public void setTitle(String title) { this.title = title; notifyPropertyChanged(BR.title); } // Demais getters/setters
  • 48.
    public class EditTextBindingAdapters{ @InverseBindingAdapter(attribute = "android:text") public static int getTextAsInt(EditText editText) { try { return Integer.parseInt(editText.getText().toString()); } catch (Exception e){ return 0; } } @BindingAdapter({"android:text"}) public static void setTextFromInt(EditText editText, int value){ if (getTextAsInt(editText) != value) { editText.setText(String.valueOf(value)); } } } EditTextBindingAdapters.java
  • 49.
  • 50.
    <layout ...> <data> ... <variable name="presenter" type=“br.com.nglauber.livrosfirebase.DetailEditActivity"/> </data> <LinearLayout ...> ... <TextView... android:onLongClick="@{presenter::longClick}" /> <Button ... android:onClick=“@{presenter::clickSaveBook}”/> <EditText ... android:onFocusChange="@{presenter::focusChanged}"/> </LinearLayout> </layout> res/layout/activity_detail_edit.xml
  • 51.
    @Override protected void onCreate(BundlesavedInstanceState) { ... binding.setBook(book); binding.setPresenter(this); } public void clickSaveBook(View view) { Toast.makeText(this, book.toString(), Toast.LENGTH_SHORT).show(); } public boolean longClick(View view){ book.setRating(book.getRating() + 1); return true; } public void focusChanged(View v, boolean focus){ if (!focus) showProduct(v); } DetailEditActivity.java Respeite a assinatura do método
  • 52.
    <layout ...> <data> <import type="br.com.nglauber.livrosfirebase.model.MediaType"/> ... <variable name="presenter" type="br.com.nglauber.livrosfirebase.DetailEditActivity" /> </data> <LinearLayout ...> ... <RadioGroup ...> <RadioButton ... android:checked="@{book.mediaTypeValue == MediaType.EBOOK}" android:onCheckedChanged="@{presenter::onMediaTypeChanged}" /> <RadioButton ... android:checked="@{book.mediaTypeValue == MediaType.PAPER}" android:onCheckedChanged="@{presenter::onMediaTypeChanged}" /> </RadioGroup> ... </LinearLayout> </layout>
  • 53.
    public void onMediaTypeChanged(CompoundButtonbuttonView, boolean isChecked) { if (isChecked) { if (buttonView == mBinding.radioMediaEbook) { mBinding.getBook().setMediaTypeValue(MediaType.EBOOK); } else if (buttonView == mBinding.radioMediaPaper) { mBinding.getBook().setMediaTypeValue(MediaType.PAPER); } } }
  • 54.
    public class Publisher{ private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } } public class Book extends BaseObservable { private Publisher publisher; ... @Bindable public Publisher getPublisher() { return publisher; } public void setPublisher(Publisher publisher) { this.publisher = publisher; notifyPropertyChanged(BR.publisher); } }
  • 55.
    <layout ...> <data> <import type="java.util.List"/> <variablename="book" type="br.com.nglauber.livrosfirebase.model.Book" /> <variable name="presenter" type="br.com.nglauber.livrosfirebase.DetailEditActivity" /> <variable name="publishers" type="java.util.List&lt;br.com.nglauber.livrosfirebase.model.Publisher&gt;" /> </data>
 <LinearLayout ...> <Spinner ... android:entries="@{publishers}" android:selection="@{publishers.indexOf(book.publisher)}" android:onItemSelected="@{(p, v, pos, id)->book.setPublisher(publishers[pos])}" /> </LinearLayout> </layout>
  • 56.
    DetailEditActivity.java ObservableList<Publisher> publishers =loadListFromSomewhere(); mBinding.setPublishers(publishers);
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
    https://github.com/lisawray/fontbinding public class TextViewBinderAdapter{ @BindingAdapter({"font"}) public static void setFont(TextView textView,String font){ AssetManager assets = textView.getContext().getAssets(); Typeface typeface = Typeface.createFromAsset(assets, font); textView.setTypeface(typeface); } } <TextView app:font="@{`FunnyKid.ttf`}" .../>
  • 62.
    Mas se tiverum “set” pode usar! <android.support.v4.widget.DrawerLayout ... app:scrimColor="@{@color/scrim}" app:drawerListener="@{fragment.drawerListener}"/>
  • 63.
  • 64.
    Expression Language <TextView ... android:text="@{user.displayName?? user.lastName}"/> <TextView ... android:text="@{`User: ` + user.name}”/> <LinearLayout ... android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"/>
  • 65.
    res/values/colors.xml <color name="red">#F00</color> <color name="black">#000</color> <TextView... android:id="@+id/text_title" android:textColor="@{book.available ? @color/black : @color/red}" android:text="@{book.title}" />
  • 66.
    res/values/strings.xml <string name="price_format">$ %1$.2f</string> <TextView... android:id="@+id/text_price" android:text="@{@string/price_format(book.price)}" /> getString(R.string.price_format, product.price);
  • 67.
    Include <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="book"type=“...Book”/> <variable name=“presenter" type=“...DetailEditActivity" /> </data> <LinearLayout ...> <include android:id="@+id/content" layout="@layout/content_detail_edit" app:presenter="@{presenter}" app:book="@{book}" /> </LinearLayout> </layout>
  • 68.
    Include ActivityDetailEditBinding mBinding = DataBindingUtil.setContentView(this,R.layout.activity_detail_edit); mBinding.setBook(book); mBinding.setPresenter(this); mBinding.content.setPublishers(new ObservableArrayList<Publisher>());
  • 69.
    Expression Chain <layout ...> <data> <importtype="android.view.View" /> ... </data> <LinearLayout ...> <ImageView ... android:id="@+id/image_cover" android:visibility="@{book.available ? View.VISIBLE : View.INVISIBLE}" /> <RadioGroup ... android:id="@+id/radio_group_media_type" android:visibility="@{imageCover.visibility}" /> <RatingBar android:visibility="@{radioGroupMediaType.visibility}" android:id=“@+id/rating_book"> <CheckBox ... android:id="@+id/check_available" android:checked="@={book.available}" />
  • 70.
    Ouvindo coisas… :) binding.addOnPropertyChangedCallback(newObservable.OnPropertyChangedCallback() { @Override public void onPropertyChanged(Observable observable, int i) { if (i == br.com.nglauber.uidemo.BR.name){ Product p = (Product)observable; Log.d("NGVL", p.getName()); } } });
  • 71.
    Ouvindo coisas… :) Usepara animações binding.addOnRebindCallback(new OnRebindCallback() { @Override public boolean onPreBind(ViewDataBinding binding) { return super.onPreBind(binding); } @Override public void onCanceled(ViewDataBinding binding) { super.onCanceled(binding); } @Override public void onBound(ViewDataBinding binding) { super.onBound(binding); } });
  • 72.
    Aplicando no seuprojeto 1. Remova os findViewById’s :) 2. Faça o Binding dos seus objetos. 3. Use os callbacks (eventos) de UI. 4. Aproveite os objetos observáveis. 5. Two-way Data Binding é vida! ♥
  • 73.
    Boas práticas • Nãocoloque lógica de negócios na tela. Apenas lógica de tela. • Os eventos disparados executarão na UI Thread. • Simplifique a lógica de UI (não coloque expressões complicadas, crie métodos para isso. • Considere utilizar um ViewModel. • Leve os BinderAdapters com você :) Você só vai precisar escrevê- los uma vez. // TODO: Fazer uma lib para isso :p
  • 74.
    Referências • Data Binding-- Write Apps Faster (Android Dev Summit 2015)
 https://www.youtube.com/watch?v=NBbeQMOcnZ0 • Data Binding Documentation (pode melhorar né…?)
 http://developer.android.com/tools/data-binding/guide.html • Nelson Glauber Blog
 http://www.nglauber.com.br/2016/05/android-data-binding.html • Advanced Data Binding (Google I/O 2016)
 https://www.youtube.com/watch?v=DAmMN7m3wLU
  • 75.
  • 76.