+7

Data Binding trong Android (phần 3)

1. Giới thiệu

phần 1phần 2 mình đã giới thiệu với các bạn những kiến thức cơ bản về Databinding trong Android. Nhưng trong phần 2 các bạn có thấy mình có viết setAdapter cho RecyclerView như sau.

binding.layoutRecyclerView.setLayoutManager(new LinearLayoutManager(this));
binding.layoutRecyclerView.setAdapter(listUserAdapter);

nếu viết như này thì ko khác gì các bạn viết

 RecyclerView recyclerView = (RecyclerView) findViewById(R.id.layout_recycler_view);
 recyclerView.setLayoutManager(new LinearLayoutManager(this));
 recyclerView.setAdapter(listUserAdapter);

và ko tận dụng dc sức mạnh của Binding Data. Các bạn có xem ở phần 1 thì thấy mình có viết hàm sự kiện onClick() cho 1 Button hay bất kỳ 1 view nào đấy như sau

// xml
android:onClick="@{ (v) -> viewModel.clickButton(v) }"
// Java
public void clickButton(View view){
// Todo click
}

mà ko viết là

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(...)

đó là mình đã sử dụng @BindingAdapter trong Android.

2. BindingAdapter

Các bạn có thể tham khảo BindingAdapter theo tài liệu của Google ở đây. Trong Android có cung cấp 1 số hàm BindingAdapter cơ bản như * onClick, onLongClick ... * để giúp người dùng không cần phải định nghĩa lại những hàm như thế, nhưng chúng ta có custom thêm những hàm sử dụng BindingAdapter để nhằm tiện sử dụng trong * project * của mình. Mình lấy ví dụ trong project của bạn có sử dụng nhiều RecyclerView thì bạn có thế custom 1 số hàm @BindingAdapter để dụng lại trong project của bạn.

Sử dụng BindingAdapter set dữ liệu lên View

Như ở đây mình có tạo ra 1 file BingdingUtils.java để định nghĩa các @BindingAdapter trong toàn project của mình.

public class BingdingUtils {
        @BindingAdapter({ "setAdapter"})
        public static void setAdapter(RecyclerView view, ListUserAdapter adapter) {
            view.setAdapter(adapter);
        }
}

Hiện tại ở đây mình định nghĩa 1 hàm để setAdapter cho RecyclerView. Để sử dụng hàm này thì các bạn cần thay đổi 1 chút trong file Xml như sau:

 // xml
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
bind:setAdapter="@{ main.listUserAdapter}"
/>

// Java 
public class MainActivity extends AppCompatActivity {
      public ObservableField<String> title = new ObservableField<>();
      public ObservableField<ListUserAdapter> listUserAdapter = new ObservableField<>();

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);

           ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
           title.set("Example DataBing RecyclerView");
           binding.setMain(this);
           setData();
       }

      private void setData() {
           List<User> users = new ArrayList<>();
           for (int i = 0; i < 20; i++) {
           User user = new User();
           user.setFirstName("first name " + i);
           user.setLastName("last name " + i);
           users.add(user);
          }
          listUserAdapter.set(new ListUserAdapter(users));
     }
}

Mình sẽ giới thiệu sơ qua về File BingdingUtils.java

  • @BindingAdapter({ "setAdapter"}): là dòng khai báo Java Annotation với tham số setAdapter sẽ được sử dụng trong file xml
  • public static void nameMethod(RecyclerView view, ListUserAdapter adapter) {}: đây là 1 hàm với tham số đầu tiên RecyclerView view là view sẽ chịu tác động của Annotation. ListUserAdapter adapter tham số thứ 2 là kiểu dữ liệu mà view sẽ nhận.
  • view.setAdapter(adapter); : dùng để đĩnh nghĩa, set các thuộc tính cho cho View.

Khi sử dụng @BindingAdapter thì các bạn chỉ cần khai báo 1 biến có kiểu là ListUserAdapter trong Class binding dữ liệu và phần còn lại thì cơ chế tự động của Android sẽ giúp các bạn làm nốt phần việc còn lại. Ở đây bạn có thể tận dụng được sức mạnh của Binding Data khi bạn thay đổi dữ liệu trong Adapter thì adapter sẽ tự động update dữ liệu lên RecyclerView cho các bạn.

Các bạn lưu ý trong file xml

bind:setAdapter="@{ main.listUserAdapter}"

thì setAdapter là tên tham số các bạn định nghĩ trong Annotation @BindingAdapte (@BindingAdapter({ "setAdapter"}) còn phần nameMethod thì là tùy ý các bạn đặt nhưng mà nên đặt tên cho dễ gợi nhớ.

Trên đây là 1 ví dụ cơ bản về cách sử dụng @BindingAdapter trong Data Binding. Ngoài ra các bạn có thể định nghĩa rất nhiều các @BindingAdapter để sử dụng trong Project của mình.

@BindingAdapter({ "src" })
public static void setImageSrc(ImageView view, @DrawableRes int src) {
    if (src != -1) view.setImageResource(src);
}

@BindingAdapter("viewPagerAdapter")
public static void setPagerAdapter(ViewPager viewPager, PagerAdapter adapter) {
   viewPager.setAdapter(adapter);
}

@BindingAdapter("currentItem")
public static void setCurrentItem(ViewPager viewPager, int position) {
   viewPager.setCurrentItem(position);
}

@BindingAdapter({ "loadUrl" })
public static void loadWebUrl(WebView webView, String url) {
    webView.loadUrl(url);
}

@BindingAdapter("checked")
public static void setChecked(ListView listView, List<Integer> list) {
    for (Integer i : list) {
      listView.setItemChecked(i, true);
    }
}
...

Sử dụng BindingAdapter để set các sự kiện lên View

Ngoài sử dụng @BindingAdapter để set dữ liệu cho View thì các bạn cũng có thể sử dụng @BindingAdapter để set các sự kiện trong Android Thường thì các View mặc định có sẵn của Android thì đều đã được cung cấp sẵn các @BindingAdapter nhằm phục vụ việc sử dụng được thuận tiện. Nhưng với 1 View mà bạn Custom thì sao, phần dưới đây mình sẽ hướng dẫn các bạn sử dụng @BindingAdapter để có thể binding được sự kiện của những Custom View.

@BindingAdapter({ "setOnScroll" })
public static void setScroll(ScrollWebView scrollWebView,
  ScrollWebView.OnScrollChangedCallback onScrollChangedCallback) {
     scrollWebView.setOnScrollChangedCallback(onScrollChangedCallback);
}

Như ở trên các bạn thấy mình có set sự kiện Callback cho ScrollWebView: File ScrollWebView.java

public class ScrollWebView extends WebView {
    private OnScrollChangedCallback mOnScrollChangedCallback;

    public ScrollWebView(final Context context) {
        super(context);
    }

    public ScrollWebView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollWebView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (mOnScrollChangedCallback != null) {
            mOnScrollChangedCallback.onScroll(this);
        }
    }

    public OnScrollChangedCallback getOnScrollChangedCallback() {
        return mOnScrollChangedCallback;
    }

    public void setOnScrollChangedCallback(final OnScrollChangedCallback onScrollChangedCallback) {
        mOnScrollChangedCallback = onScrollChangedCallback;
    }

    /**
     * Impliment in the activity/fragment/view that you want to listen to the webview
     */
    public static interface OnScrollChangedCallback {
        public void onScroll(View view);
    }
}

Trong file Xml

<....ScrollWebView
bind:setOnScroll="@{ (v) -> viewMoldel.scrollWebView(v) }"
/>

Trong class ViewModel các bạn khai báo 1 hàm với danh sách đối số giống với interface mà các bạn đã đĩnh nghĩa để nhận sự kiện onScroll

public void scrollWebView(View view){
// Todo something
}

Các bạn có thể định nghĩa các hàm interface với nhiều đố số và khi khai báo @BindingAdapter và khai báo các hàm trong ViewModel thì các bạn định nghĩa hàm với đầy đủ các đối số giống với các đối số đã khai báo trong interface là được. Sau đây mình sẽ thiệu các bạn về 1 số @BindingAdapter với nhiều tham số

@BindingAdapter("onGroupClickListener")
    public static void setOnGroupClick(ExpandableListView listView,
            ExpandableListView.OnGroupClickListener listener) {
        listView.setOnGroupClickListener(listener);
    }

    @BindingAdapter("onChildClickListener")
    public static void setOnChildClick(ExpandableListView listView,
            ExpandableListView.OnChildClickListener listener) {
        listView.setOnChildClickListener(listener);
    }

file xml

<ExpandableListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            bind:expandableAdapter="@{ viewModel.cityExpandableAdapter }"
            bind:onChildClickListener="@{ (listView, v, i, j, l) -> viewModel.onChildCityClick(listView, v, i, j, l) }"
            bind:onGroupClickListener="@{ (listView, v, i, l) -> viewModel.onGroupCityClick(listView, v, i, l) }"
            />

trong file ViewModel

public void onGroupCityClick(ExpandableListView parent, View v, int groupPosition, long id) {
        // Todo something
    }

public void onChildCityClick(ExpandableListView parent, View v, int groupPosition,
            int childPosition, long id) {
        // Todo something
    }

Trong bài viết trên mình đã giới thiệu với các bạn cơ bản về @BindingAdapter trong Android. Nếu các bạn kết hợp sử dụng @BindingAdapter với các phương thức trong BindingData thì sẽ giúp code được tái sử dụng tốt hơn và đạt được hiệu quả cao nhất trong công việc.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.