Android

Quản lí Fragment và một số điểm mạnh của Fragment

Như tôi đã nói trong bài trước, ngoài việc được dùng để tối ưu hóa giao diện cho máy tính bảng, Fragment trong Android còn được sử dụng cho nhiều mục đích khác. Bài viết này sẽ giới thiệu cách quản lí các Fragments trong Activity cũng như một vài trường hợp từ mức “nên” tới “buộc” phải dùng Fragment, bất kể thiết bị đích là handsets hay tablets.

1. Quản lí Fragment

Khác với bài viết trước, lần này khi viết code thì tôi sẽ dùng thư viện support và tôi cũng khuyến nghị các bạn nên sử dụng thư viện này, vì hiện tại phiên bản mới nhất của v4-fragment libs là 25.4.0, tức nó đang resemble các Fragment classes của API25, bạn có thể tích hợp các tính năng mới nhất trên API25 xuống tới tận API9. Cũng xin cung cấp thêm thông tin là sang “vơ-sần” 26 thì toàn bộ các support libs đều yêu cầu minSdk là 14 chứ không còn là 9 nữa.

Để tạo một Fragment thì bạn sẽ cần extends class android.support.v4.app.Fragment (hay android.app.Fragment nếu dùng framework libs). Chẳng hạn, tôi muốn tạo một FragmentOne thì tôi có code sau:

Bạn lưu ý: Bao giờ cũng phải có một public constructor không có một param nào, và bạn không nên tạo thêm constructor nào có tham số, trừ khi điều đó là vô cùng cần thiết, không làm khác được. Thông thường, theo bài bản thì nếu bạn muốn truyền biến nào vào Fragment, thì bạn nên dùng hàm Fragment#setArguments(Bundle args) với cái Bundle args kia sẽ chứa các biến bạn muốn truyền vào. Và cũng rất phổ biến là các coder chuyên nghiệp về Android hay làm một hàm static newInstance(…) để truyền tham số vào cho gọn. Chẳng hạn:

Bấy giờ, từ Activity chứa Fragment, ví dụ ở đây là MainActivity, bạn sẽ gọi hàm newInstance đó để khởi tạo biến Fragment. Ví dụ:

Tiếp theo, nếu bạn không tiến hành add Fragment vào Activity thì… hơi kì cục. Để tiến hành add, remove, replace… các Fragments, bạn sẽ cần một instance của FragmentTransaction. Lưu ý là mỗi FragmentTransaction chỉ được commit một lần duy nhất nên đừng bao giờ đặt biến này làm field member để dùng lại nhiều lần. Bạn commit lần nhì là sẽ gặp Exception ngay. Để gọi instance này, thì bạn không bao giờ được phép dùng constructor, chẳng hạn:

Bạn gọi như vậy thì sẽ toi ngay lập tức. Thay vào đó, bạn gọi thông qua FragmentManager. Đối với thư viện android.support.v4.app.FragmentManager thì từ AppCompatActivity, bạn gọi method getSupportFragmentManager(). Còn nếu bạn dùng android.app.FragmentManager thì gọi getFragmentManager(). Tại đây tôi chỉ trình bày hàm FragmentTransaction#add(…), các hàm khác như remove(…) hay replace(…) thì làm tương tự và bạn tự khám phá.

Có ba kiểu add, gồm add(Fragment fragment, String tag)add(int containerViewId, Fragment fragment) và add(int containerViewId, Fragment fragment, String tag). Với param fragment và tag thì bạn đã hiểu rồi, còn containerViewId chính là Id của cái ViewGroup – cụ thể hơn là cái Layout – mà bạn muốn fragment “nhập” vào trên contentView của Activity. Nếu trên contentView của Activity không có ViewGroup nào có Id được truyền thì một RuntimeException sẽ được thrown. Hàm đầu tiên là bạn add một Fragment “ẩn”, không “visible” trong Activity, dành cho những lúc bạn code các Fragment xử lí các tình huống mang tính “background”. Lưu ý là trong những trường hợp đó, muốn truyền kết quả về Activity (tất nhiên là bạn có truyền thông tin từ Activity sang Fragment rồi thì nó mới có cái để xử lí), thì bạn vẫn buộc phải add Fragment vào với hàm trên chứ không phải là cứ new hay newInstance là có thể lấy biến Fragment ra xử lí thông tin ngay.

Để remove hay replace hay các thao tác khác thì bạn làm tương tự, cũng từ FragmentManager gọi beginTransaction() và cuối cùng phải commit(). Nếu quên không commit thì cũng giống như bạn (nam) đem 16tỉ rưỡi ra gặp một cô người mẫu rồi cầm nguyên số tiền đó không thiếu một đồng quay về vậy.

Bây giờ, quay lại Fragment, giả sử tôi có 3 TextView để hiển thị 3 giá trị được truyền vào trong Bundle args thì bạn chỉ làm những hàm giản dị như bên dưới. Lưu ý: Bạn phải bắt đầu code từ giai đoạn onCreateView, nếu trước đó thì do chưa định contentView nên các TextView instances sẽ bị null chắc. Còn việc lấy args ra thì trong onCreate hay onAttach cũng không ảnh hưởng đến hòa bình thế giới. Với bài giới thiệu này thì tôi làm trong onCreateView.

Và như vậy là xong. Đối với các trường hợp phức tạp trong thực tế thì việc sáng tạo là không thể thiếu.

2. Một số trường hợp nên sử dụng Fragment

Trường hợp đầu tiên, cũng là trường hợp mang tính “original” nhất là việc tạo giao diện đa cột để tối ưu hóa giao diện ứng dụng cho máy tính bảng, cũng như Chromebooks hay Samsung DEX ở thời điểm hiện tại.

android-fragments

Bên trên là tham chiếu “chính chủ” của Google về việc làm giao diện đa cột. Đối với handset thì Activity A sẽ chỉ chứa một Fragment A chứa một ListView, khi người dùng chọn một row trên ListView thì Fragment A sẽ gọi ngược lên Activity A, chuyển cảnh sang Activity B chứa Fragment B để hiển thị dữ liệu tương ứng. Tuy nhiên, trên tablet thì cả hai Fragment A và Fragment B đều nằm trong Activity A và khi người dùng tương tác với Fragment A thì Fragment A sẽ gọi ngược lên Activity A và Activity A sẽ yêu cầu Fragment B hiển thị/cập nhật dữ liệu tương ứng.

Đến đây, có thể bạn sẽ thắc mắc vậy Activity B ở đâu trên tablet? Câu trả lời là nó không ở đâu cả, vì với giao diện hai cột như trên, cả hai Fragment đều nằm trên Activity A rồi thì Activity B sẽ “không có đất đâu mà dựng võ”. Nó sẽ không được gọi.

Tôi tin rằng sẽ có bạn tiếp tục nghĩ “Vậy trên handset tôi replace Fragment A bằng Fragment B và như vậy tôi không cần làm Activity B cho tốn công, được không?”, thì tôi trả lời là Thích thì nhích thôi, bạn làm như vậy chứng tỏ bạn chuyên nghiệp rồi đó.

Một trường hợp khác là khi bạn làm SettingsActivity, tức Activity chứa các thiết lập cho ứng dụng của bạn. Trên tablet thì không cần nói rồi, nhưng nếu dù ứng dụng của bạn chỉ chạy trên handsets thì cũng nên dùng PreferenceFragment rồi cho add nó vào Activity chứ không nên dùng PreferenceActivity – class này đã bị deprecated với hàm addPrefereneFromResource(int) và Google khuyến khích dùng PreferenceFragment#addPereferenceFromResource(int) thì hơn.

Và trường hợp cuối cùng: Activity để đăng nhập và đăng kí tài khoản. Nếu ứng dụng của bạn có yêu cầu đăng nhập/đăng kí tài khoản, thì thay vì bạn làm 2 Activity, 1 cái về đăng nhập, 1 cái về đăng kí thì bạn nên làm 1 cái Activity làm container và 2 cái Fragment tương ứng đăng nhập và đăng kí, trước là bạn tận dụng được code, sau là đơn giản hóa quá trình chuyển đổi sang các Activity sau, tránh lòng vòng lẩn quẩn giữa Activity đăng nhập và Activity đăng kí.

3. Một số trường hợp phải sử dụng Fragment

Đầu tiên, và cũng là phổ biến nhất là ViewPager hay BottomNavigationView như hình bên dưới. Nếu bạn quá “amateur” mà làm nhiều Activity thì việc handle các event của người dùng rất mệt và sẽ làm rối tung rối mù hệ thống backstack của ứng dụng.

bottomnavigationview

Như bên trên thì mỗi tab Favourites, Schedules và Music sẽ có 3 Fragments tương ứng với nó, và khi người dùng click vào các item trên đó thì Activity sẽ chạy Fragment tương ứng. Bạn thử nghĩ nếu với ý tưởng đó, bạn không làm Fragment mà dùng Activity thì sẽ ra sao?

Một trường hợp khác là bạn làm NavigationDrawer. Nếu chưa nghe qua thì bạn hãy bật ứng dụng Gmail hay CH Play lên và nhấn nút 3 gạch ngang ở góc trên bên trái,. Cũng có trường hợp click vào 1 item trên NavigationDrawer thì sẽ chuyển sang Activity khác như Cài đặt trong CH Play, nhưng trong các item về Ứng dụng và Trò chơi hay Phim và Sách thì dùng Fragment, để có thể truy cập được NavigationDrawer mọi lúc.

Một ví dụ khác là nếu bạn làm một ứng dụng về trình duyệt, thì nếu làm nhiều Activity, người dùng bảo đảm sẽ rate 1 sao cho bạn vì mọi thứ sẽ rối tung rối mù.

Sang bài tiếp theo thì tôi sẽ hướng dẫn các bạn thực hiện giao tiếp giữa Fragment-Activity và Fragment-Fragment trên cùng một Activity. I’ll see you in the very next episode.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.