More Related Content Similar to Dominando o Data Binding no Android (20) More from Nelson Glauber Leal (20) Dominando o Data Binding no Android1. Dominando o Data Binding
no Android
+Nelson Glauber
@nglauber
www.nglauber.com.br
5. Data Binding
• Facilita a ligação entre Model e View
• Estende os arquivos de layout com micro-expressões
• Binders customizados reduzem a repetição de código
• Tratamento de NPE
7. 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...
}
8. 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());
10. Book book = (Book)getIntent().getSerializableExtra(EXTRA_BOOK);
ActivityDetailViewBinding mBinding =
DataBindingUtil.setContentView(this, R.layout.activity_detail_view);
mBinding.setBook(book);
15. 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
17. Se não gostar do nome da classe…
MainActivity.java
res/layout/activity_main.xml
<layout ...>
<data class="VaiFilhaoBinding">
...
</data>
public class MainActivity extends AppCompatActivity {
VaiFilhaoBinding binding;
...
20. 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());
21. Book book = (Book)getIntent().getSerializableExtra(EXTRA_BOOK);
ActivityDetailViewBinding mBinding =
DataBindingUtil.setContentView(this, R.layout.activity_detail_view);
mBinding.setBook(book);
22. <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:rating="@{book.rating}" ... />
</LinearLayout>
</layout>
23. <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>
<!-- 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>
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.mediaType}" ... />
<RatingBar android:progress="@{book.rating}" ... />
</LinearLayout>
</layout>
27. <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>
public class Book {
...
private String coverUrl;
private MediaType mediaType;
}
29. 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
30. 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
37. BookViewHolder.java
public static class BookViewHolder extends RecyclerView.ViewHolder {
ItemBookBinding binding;
public BookViewHolder(ItemBookBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
38. @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
41. 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
42. public class Book implements Observable {
// Copie as coisas do BaseObservable :)
}
43. 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);
}
}
}
44. 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); }
}
46. <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>
48. <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>
49. <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>
52. 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
55. @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
56. <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>
57. 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);
}
}
}
58. 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);
}
}
59. <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<br.com.nglauber.livrosfirebase.model.Publisher>" />
</data>
<LinearLayout ...>
<Spinner ...
android:entries="@{publishers}"
android:selection="@{publishers.indexOf(book.publisher)}"
android:onItemSelected="@{(p, v, pos, id)->book.setPublisher(publishers[pos])}" />
</LinearLayout>
</layout>
66. Mas se tiver um “set” pode usar!
<android.support.v4.widget.DrawerLayout ...
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
73. 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}" />
75. 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);
}
});
76. 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! ♥
77. 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
78. 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