1. Giới thiệu
View binding được giới thiệu từ Android Studio 3.6. View binding là tính năng mới cho phép bạn thay thế câu lệnh nhàm chán findViewById với việc tự động generate các class binding cho mỗi file layout XML để đơn giản code, loại bỏ việc gặp bugs, và tránh được boilerplate so với việc sử dụng findViewById.
2. Sử dụng
Lưu ý: View binding có sẵn ở phiên bản Android Studio 3.6
Để sử dụng View binding trong module, thêm viewBinding vào file build.gradle:
1 2 3 4 5 6 7 | android <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> viewBinding <span class="token punctuation">{</span> enabled <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Nếu bạn muốn bỏ qua file nào mà không cần gen view binding tự động, thêm thuộc tính sau vào mỗi file xml:
1 2 3 4 5 6 | <span class="token operator"><</span>LinearLayout <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> tools<span class="token operator">:</span>viewBindingIgnore<span class="token operator">=</span><span class="token string">"true"</span> <span class="token operator">></span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token operator"><</span><span class="token operator">/</span>LinearLayout<span class="token operator">></span> |
Một khi viewbinding được bật, class binding sẽ tự động generated cho mỗi XML layout file. Tên của class binding được generated bằng cách convert tên file layout XML thành dạng camel case và thêm hậu tố Binding phía sau cùng.
Ví dụ file xml activityawesome.xml -> ActivityAwesomeBinding
2.1. Sử dụng viewbinding trong Activity:
Để cài đặt instance của binding class sử dụng trong activity, trong method onCreate() thực hiện các bước sau:
- Sử dụng method inflate(), instance của class binding sẽ được tạo để sử dụng trong activity.
- Gọi đến tham chiếu của root view bằng getRoot()
- Truyền root view vào trong setContentView() để hiển thị view
1 2 3 4 5 6 7 8 9 | <span class="token keyword">private</span> lateinit var binding<span class="token operator">:</span> ResultProfileBinding override fun <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span> binding <span class="token operator">=</span> ResultProfileBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">)</span> val view <span class="token operator">=</span> binding<span class="token punctuation">.</span>root <span class="token function">setContentView</span><span class="token punctuation">(</span>view<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Bây giờ bạn có thể sử dụng instance của binding class để tham chiếu tới các phần tử của view rồi đó
1 2 3 | binding<span class="token punctuation">.</span>name<span class="token punctuation">.</span>text <span class="token operator">=</span> viewModel<span class="token punctuation">.</span>name binding<span class="token punctuation">.</span>button<span class="token punctuation">.</span>setOnClickListener <span class="token punctuation">{</span> viewModel<span class="token punctuation">.</span><span class="token function">userClicked</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
2.2. Sử dụng view binding trong Fragment:
Để sử dụng instance binding class trong fragment, trong method onCreateView():
- Sử dụng method inflate(), instance của class binding sẽ được tạo để sử dụng trong fragment.
- Gọi đến tham chiếu của root view bằng getRoot()
- Return root view từ method onCreateView() để hiển thị view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">private</span> var _binding<span class="token operator">:</span> ResultProfileBinding<span class="token operator">?</span> <span class="token operator">=</span> null <span class="token comment">// This property is only valid between onCreateView and</span> <span class="token comment">// onDestroyView.</span> <span class="token keyword">private</span> val binding <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> _binding<span class="token operator">!</span><span class="token operator">!</span> override fun <span class="token function">onCreateView</span><span class="token punctuation">(</span> inflater<span class="token operator">:</span> LayoutInflater<span class="token punctuation">,</span> container<span class="token operator">:</span> ViewGroup<span class="token operator">?</span><span class="token punctuation">,</span> savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span> <span class="token punctuation">)</span><span class="token operator">:</span> View<span class="token operator">?</span> <span class="token punctuation">{</span> _binding <span class="token operator">=</span> ResultProfileBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>inflater<span class="token punctuation">,</span> container<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span> val view <span class="token operator">=</span> binding<span class="token punctuation">.</span>root <span class="token keyword">return</span> view <span class="token punctuation">}</span> override fun <span class="token function">onDestroyView</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _binding <span class="token operator">=</span> null <span class="token punctuation">}</span> |
Bây giờ bạn có thể sử dụng các biến tham chiếu của view rồi:
1 2 3 | binding<span class="token punctuation">.</span>name<span class="token punctuation">.</span>text <span class="token operator">=</span> viewModel<span class="token punctuation">.</span>name binding<span class="token punctuation">.</span>button<span class="token punctuation">.</span>setOnClickListener <span class="token punctuation">{</span> viewModel<span class="token punctuation">.</span><span class="token function">userClicked</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
2.3. Code được generate như thế nào:
View binding sẽ tự động generate 1 class binding cho mỗi file xml. Khi bạn chỉnh sửa file layout xml, code gen tự động sẽ được tối ưu chỉ cho class binding liên quan đến file xml đó và nó sẽ được thực hiện ở memory để đảm bảo mọi thứ nhanh nhất. Hãy cùng xem 1 file gen tự động từ file layout xml:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">ActivityAwesomeBinding</span> <span class="token keyword">implements</span> <span class="token class-name">ViewBinding</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@NonNull</span> <span class="token keyword">private</span> <span class="token keyword">final</span> ConstraintLayout rootView<span class="token punctuation">;</span> <span class="token annotation punctuation">@NonNull</span> <span class="token keyword">public</span> <span class="token keyword">final</span> Button button<span class="token punctuation">;</span> <span class="token annotation punctuation">@NonNull</span> <span class="token keyword">public</span> <span class="token keyword">final</span> TextView subtext<span class="token punctuation">;</span> <span class="token annotation punctuation">@NonNull</span> <span class="token keyword">public</span> <span class="token keyword">final</span> TextView title<span class="token punctuation">;</span> |
View binding sẽ tự gen đúng type cho mỗi view xác định bởi id chỉ định cho view đó.
Layout include:
View binding cũng được sử dụng trong include file layout xml
1 2 3 4 5 6 7 8 9 10 | <span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> activity_awesome<span class="token punctuation">.</span>xml <span class="token operator">--</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout<span class="token operator">></span> <span class="token operator"><</span>include android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/includes"</span> layout<span class="token operator">=</span><span class="token string">"@layout/included_buttons"</span> <span class="token operator"><</span><span class="token operator">/</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> included_buttons<span class="token punctuation">.</span>xml <span class="token operator">--</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout<span class="token operator">></span> <span class="token operator"><</span>Button android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/include_me"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout<span class="token operator">></span> |
Lưu ý: tag <include> phải có id android:id=”@+id/includes”
1 2 3 4 5 | public final class ActivityAwesomeBinding implements ViewBinding { ...java @NonNull public final IncludedButtonsBinding includes; |
View binding sẽ tự gen tham chiếu IncludedButtonsBinding trong ActivityAwesomeBinding
3. View binding vs findViewById
View binding có 1 vài điểm hay hơn so với findViewById:
- Null safety: Vì view binding tạo tham chiếu trực tiếp tới view, do đó không có rủi ro như Null Pointer Exception khi invalid view ID.
- Type safety: Fileds trong mỗi class binding có những type matching view với tham chiếu trong file xml. Do đó, không có rủi ro với class cast exception.
4. View binding vs Data Binding
View binding và Data binding cả 2 đều generate tự động class binding để bạn sử dụng tham chiếu view trực tiếp. Tuy nhiên, view binding được chú ý hơn trong các use case đơn giản và cung cấp 1 số tính năng:
- Compile nhanh hơn: View binding không yêu cầu annotation, nên thời gian compile nhanh hơn
- Dễ dàng sử dụng: View binding không yêu cầu tag cho file xml
Bên cạnh đó, view binding cũng có 1 số nhược điểm so với data binding:
- View binding không hỗ trợ layout variables or layout expression
- View binding không hỗ trợ two-way data binding
Lưu ý: View binding chỉ thay thế cho findViewById, nếu bạn muốn tự động bind view trong file layout xml, bạn có thể sử dụng view binding cùng với data binding trong cùng module.
5. View binding vs Kotlin synthetics vs ButterKnife
Trên đây đều là 3 cách để sử dụng view trong file layout xml và được mọi người sử dụng.
ButterKnife validate nullable/not-null tại runtime, compiler không check rằng bạn đã match đúng trong layout hay chưa
Bạn nên cân nhắc sử dụng view binding nhé!
Tham khảo:
https://developer.android.com/topic/libraries/view-binding
https://medium.com/androiddevelopers/use-view-binding-to-replace-findviewbyid-c83942471fc