[Translation] Các khái niệm cơ bản về Fragment trong Android( Phần 2)

Tiếp theo phần trước, phần này mình sẽ giới thiệu về cách quản lí fragment trong activity

Nội dung bài viết được mình tham khảo ở trang này

Có 1 số subclass dc extend từ lớp base Fragment :

  • Dialog Fragment
  • List Fragment
  • Preference Fragment

Dialog Fragment

Hiển thị 1 floating dialog. Cách tạo này là 1 cách thay thế so với cách sử dụng dialog helper trong Activity , nó lợi hơn ở điểm có thể tích hợp 1 fragment dialog vào backstack fragment (được quản lí bởi Activity) ,cho phép người dùng được trở về fragment đã dismiss trước đó .

List Fragment

HIển thị 1 list item được quản lí bởi adapter (ví dụ như SimpleCursorAdapter) , tương tự như ListActivity. Nó cung cấp vô số phương thức để quản lí 1 listview , ví dụ như onListItemClick() callback để quản lí sự kiện click

Preference Fragment

Hiển thị hệ thống của các đối tượng preference như một list ,tương tự PreferenceActivity. Điều này rất hữu dụng khi tạo 1 "setting" activity cho ứng dụng của bạn. 1 Fragment thường là 1 phần của activity user interface và sẽ "đóng góp " layout của nó tới activity Để set 1 layout cho fragment , bạn phải implement onCreateVIew() , hàm này được gọi bởi Android system và khi đó layout bắt đầu được vẽ .Hàm này trả về 1 View (chính là root view của layout fragment )

Chú ý : Nếu fragment là subclass của ListFragment thì implementation trả về 1 Listview từ onCreateVIew(), vi thế bạn không cần implement nó

Để trả về 1 layout trong onCreateView() , bạn có thể inflate nó từ layout resource (được định nghĩa trong XML). Để giup bạn làm điều đó onCreateView() cung cấp 1 LayoutInflaterObject

Ví dụ:

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

Các parameter được truyền vào là ViewGroup(lớp cha ) , saveInstanceState là 1 Bundle cung cấp dữ liệu instance trước của fragment , nếu mà fragment được resume hàm inflate nhận 3 tham số :

  • Resoure ID của layout mà bạn muốn inflate
  • ViewGroup(lớp cha của layout được inflate ) . Truyền vào container rất quan trọng để hệ thống có thể apply parameter của layout đến root view .
  • Một biến boolean chỉ ra rằng layout có dc gắn vào Viewgroup trong khi đang inflate hay không (Trong trường hợp này set false vì hệ thống đã insert layout vào container rồi , còn set true thì nó sẽ "miễn cưỡng " tạo 1 viewgroup trong layout cuối ) Tiếp đến chúng ta phải add fragment vào activity.

Adding a fragment to an activity

Fragment thường là 1 phần của UI đối với activity chứa nó .Có 2 cách để add fragment vào layout activity : Khai báo 1 fragment trong layout activity

Bạn có thể set thuộc tính của fragment như view .Ví dụ đây là layout của 1 activity chứa 2 fragment :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment android:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    <fragment android:name="com.example.news.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
</LinearLayout>

android:name chính là tên của clas fragment được khởi tạo từ layout Khi hệ thống khởi tạo layout của activity , nó khởi tạo mỗi fragment bên trong layout và gọi onCreateView() với mỗi fragment .Hệ thống insert View vào(View này được trả về bởi fragment thay vì đối tượng <fragment> ) Mỗi fragment cần có cách nhận diện riêng để hệ thống có thể khôi phục lại nó khi mà activity restart Có 3 cách để thiết lập ID cho fragment ;

  • Set thuộc tính android:id với id riêng biệt
  • Set thuộc tính android:tag với string riêng biêt
  • Nếu mà bạn không dùng 2 cách trên , hệ thống sẽ dùng id của container view

Thêm trực tiếp bằng code trong ViewGroup

Bất cứ khi nào activity chạy , bạn có thể thêm fragment vào trong layout activity .Bạn đơn giản chỉ cần chỉ rõ ra ViewGroup nào sẽ đặt fragment Để tạo fragment transaction trong activty(ví dụ delete , remove hay update fragment) , ta phải dùng Api từ Fragment Transaction .Bạn có thể lấy 1 instance của fragment như thế này :

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

và add 1 fragment sử dụng hàm add(). Ví dụ

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

Tham số đầu tiên chính là ViewGroup mà fragment sẽ được đặt , còn tham số thứ 2 là chính fragment . Sau khi tạo thay đổi bạn có thể gọi commit() để các thay đổi được thực thi.

Managing Fragments

Để quản lí fragment ta dùng FragmentManager (có thể gọi bằng hàm getFragmentManager() từ Activity) Bạn có thể làm 1 số điều với nó như :

  • get fragment nằm trong activity bằng findViewById() hoặc findViewByTag()

  • pop fragment ra khỏi back stack,với popBackStack()

  • Đăng kí 1 listener để lắng nghe thay đổi từ back stack với addOnBackStackListener()

Bạn cũng có thể dùng FragmentManager để mở FragmentTransaction

Performing Fragment Transactions

Một trong những điểm rất tuyệt khi sử dụng fragment trong activity là khả năng add , remove , delete và nhiều hành động khác nữa để phản hồi lại các tương tác của người dùng .Mỗi 1 bộ các thay đổi bạn commit đến activity được gọi là 1 transaction và bạn có thể biểu diễn chúng sử dụng API FragmentTrantion .Bạn cũng có thể lưu mỗi transaction vào backstack được quản lí bởi activity ,cho phép người dùng chuyển hướng dựa trên thay đổi của fragment

// Create new fragment and transaction

Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack

transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

Trong ví dụ này , new fragment sẽ thay cho fragment hiện tại (fragment đang được định nghĩa bởi R.id.fragment_container )

Bằng cách gọi addToBackStack() ,transaction dùng để thay thế sẽ được add vào backstack do vậy user có thể đảo ngược lại transaction và gọi lại fragment trước bằng cách nhấn nút Back.

Thứ tự thạy đổi bạn thêm vào Fragment Transaction không ảnh hưởng , ngoại trừ :

  • Phải gọi commit() ở cuối cùng

  • Nếu bạn thêm nhiều fragment vào cùng 1 container , thứ tự bạn thêm sẽ quyết định thứ tự xuất hiện của chúng trong view

Nếu bạn không gọi addToBackStack() khi bạn thực hiện 1 transaction để remove 1 fragment thì fragment sẽ bị destroy ngay khi transaction được commit và user không thể quay lại fragment đó

Tip : Bạn có thể apply animation với mỗi fragment transaction bằng cách gọi setTransition trước khi commit .Trong khi đó ,nếu bạn gọi addToBackStack() thì khi remove 1 fragment , fragment đó sẽ stop và sẽ resume lại nếu người dùng bấm back

Gọi commit() không thực thiện transaction ngay lập tức .Hơn nữa , nó sẽ lập kế hoạch để chạy trên activity UI thread(main thread ) ngay khi thread có khả năng chaỵ.Nếu cần thiết , bạn có thể gọi executePendingTransactions() từ UI thread để thực hiện transaction ngay lập tức thực hiện lênh commit () .Nhưng làm như vậy cũng không thực sự cần thiết trừ khi transaction phụ thuộc vào công việc từ thread khác .

Chú ý : Bạn có thể commit 1 transaction sử dụng commit () ,chỉ ưu tiên với việc lưu lại trạng thái activity (saving activity state )(khi mà user rời khỏi activity).Nếu bạn cố commit sau đó thì sẽ throw exception .Đó là vì state sau khi commit có thể bị mất nếu activity cần được restore .Với trường hợp nào mà state không ảnh hưởng thì bạn có thể dụng lênh commitAllowingStateLoss() .