android kotlin

Fragment và Activity trong android

1. Giới thiệu:

Như các bạn đã thấy hiện nay kích thước màn hình điện thoại và tablet ngày càng lớn. Việc tăng kích thước màn hình trên các thiết bị Android như thế ít nhiều làm tăng thêm "gánh nặng" cho Activity, khiến việc thiết kế UI sao cho vừa có thể chạy tốt trên phone và tablet lẫn phaplet bổng trở nên khó khăn hơn bao giờ hết cho các lập trình viên của chúng ta. Việc khoảng trống mênh mông được tạo ra khiến ảnh hưởng xấu đến trải nghiệm của người dùng.

Chính vì vậy mà Fragment đã được Google giới thiệu ra nhằm "giảm tải" cái gánh nặng đó.

2. Fragment là gì?

Từ phần giới thiệu trên, bạn có thể hiểu Fragment đóng vai trò quản lý một giao diện của màn hình y như Activity vậy. Nhưng Fragment lại không phải là một thành phần quản lý giao diện động laaoj như Activity, Fragment thuộc về quản lý của Activity.

Như vậy bạn có thể hình dùng Fragment chịu trách nghiệm quản lý một không gian màn hình, nhiều khi không gian này cũng chính à toàn màn hình. Và cái không gian màn hình đó của Fragment, có khi nhiều Fragment của Activity đó cùng nhau hiển thị lên một màn hình, cũng có khi chúng luân phiên hiển thị nếu như mỗi chúng đều chiếm cả không gian màn hình. Và một cái hay nữa là một Fragment nào đó cũng có thể được khai báo và sử dụng bởi nhiều Activity khác nhau.

Vì Fragment chịu trách nhiệm quản lý một phần giao diện, nên nó cũng có vòng đời tương tự như với Activity vậy. Tuy nhiên, vì Fragment nằm trong quản lý của Activity, nên vòng đời của Fragment sẽ phụ thuộc rất nhiều vào vòng đời của Activity. Phần sau chúng ta sẽ tìm hiểu kĩ mối quan hệ giữa chúng.

3. Cách đính Fragment lên Activity

Có hai cách để thực hiện việc đó:

  1. Add():

    • Khi bạn muốn hiển thị 1 fragment bằng hàm add() thì bạn đang thêm 1 fragment mới vào View và fragment cũ vẫn còn trong View. Thao tác này thực chất là bạn đang phủ (đè) 1 View với mới lên trên View cũ.
    FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
    transaction.add(R.id.main, FragmentA.newInstance());
    transaction.addToBackStack(null);
    transaction.commit();
    

    Giải thích một chút nhé. Đầu tiên chúng ta tạo một Transaction., thực hiện phương thức add() trong đó có 2 tham số : một là layout mà chúng ta sẽ đính fragment vào, thứ hai là Fragment chúng ta muốn add vào activity. tiếp theo là đặt nó vào BackTask. Cuối cùng dùng phương thức commit() để hoàn thành

    Tuy nhiên, khi bạn ấn nút "Back" cứng trên bàn phím hay là gọi sự kiện popBackStack() để remove fragment hiện tại thì fragment cũ sẽ không được khởi tạo lại (các hàm onResume(), onCreate(), onCreateView() không được gọi vì thực chất nó chưa bị kill hay là lưu vào trạng thái nghỉ) mà vẫn tiếp chạy những tiến trình còn đang dang dở. Làm như vậy sẽ rất tốn bộ nhớ.

  2. Replace()

    • Khi mà bạn sử dụng phương thức này đồng nghĩa với việc bạn đã thay thế Fragment cũ bằng một Fragment mới.
    FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.main, FragmentA.newInstance());
    transaction.addToBackStack(null);
    transaction.commit();
    

    Như vậy khi bạn gọi hàm replace() thì frament cũ sẽ gọi vào hàm onPause() và savedInstanceState lại trạng thái hiện tại của nó. Khi bạn gọi sự kiện popBackStack() ở fragment hiện tại để trở về fragment cũ thì fragment sẽ được khởi tạo lại với trạng thái được lưu trong savedInstanceState.

    Vậy các bạn thấy có chút bối rối rồi phải không! Tùy thuộc vào trường hợp mà chúng ta có cách sử lý hợp lý.

    • Bạn nên sử dụng add() khi bạn vẫn muốn fragment hiện tại tiếp tục chạy. Chẳng hạn như bạn có một danh sách đối tượng khi kích vào mỗi đối tượng sẽ xem chi tiết khi back lại thì vẫn có thể xem lại danh sách đó... Và còn nhiều trường hợp khác nữa. Khi vào làm thực tế các bạn sẽ vấp phải thôi. Đừng lo lắng nhé.
    • Khi bạn khi thực hiện các thao tác hoàn toàn mới và độc lập với fragment cũ thì bạn nên sử dụng fragment để tiết kiệm bộ nhớ cho ứng dụng, ngoài ra khi sử dụng replace khi back trở về fragment trước thì hàm onResume() sẽ được gọi, tại đây bạn có thể refresh lại dữ liệu hiển thị trên View.

4. Sơ đồ vòng đời giữa Activity và Fragment:

Ở đây mình lấy ví dụ chúng ta có một Activity và 2 Fragment gồm: FragmentA có một button để thêm Fragment khác vào và Fragment B.

Trường hợp 1: khi chạy app thì lần lượt các hàm sẽ là

MainActivity: onCreate
FragmentA: onAttach
MainActivity: onAttachFragment
FragmentA: onCreate
FragmentA: onCreateView
FragmentA: onViewCreated
FragmentA: onActivityCreated
FragmentA: onStart
MainActivity: onStart
MainActivity: onResume
FragmentA: onResume
MainActivity: onResumeFragments

Kết luận: Chạy onCreate sau đó tạo xong fragment FragmentA thì chạy tiếp Activity. Song song với điều này nếu muốn đếm có bao nhiêu fragment trong stack thì cần phải kiểm tra ở hàm onResume của Activity nếu không sẽ không đếm được vì lúc ấy chưa tạo xong fragment.

Trường hợp 2: Khi click vào nút và thêm FragmentB vào sẽ là:

FragmentB: onAttach
MainActivity: onAttachFragment
FragmentB: onCreate
FragmentB: onCreateView
FragmentB: onViewCreated
FragmentB: onActivityCreated
FragmentB: onStart
FragmentB: onResume

Kết luận: Khi click thì Activity vẫn đang ở trạng thái onResume, nếu click thì chỉ chạy vòng đời của fragment này FragmentB thôi. Và một đều đặc biệt nếu có bất kỳ một fragment được thêm hay thay thế thì sự kiện onAttachFragment của Activity luôn chạy.

Trường hợp 3: Ấn back trở lại thì fragment FragmentB sẽ bị remove.

FragmentB: onPause
FragmentB: onDestroyView
FragmentB: onDestroy
FragmentB: onDetach

Trường hợp 4: Ra màn hình Home thì

FragmentA: onPause
MainActivity: onPause
FragmentA: onStop
MainActivity: onStop

Trường hợp 5: Trở lại app

FragmentA: onStart
MainActivity: onStart
MainActivity: onResume
FragmentA: onResume
MainActivity: onResumeFragments

Trường hợp 6: Khi chưa remove FragmentB bằng Back và ra màn hình Home

FragmentA: onPause
FragmentB: onPause
MainActivity: onPause
FragmentA: onStop
FragmentB: onStop
MainActivity: onStop

Kết luận: Fragment nào được thêm vào trước thì sẽ dừng trước, lần lượt.

Trường hợp 7: Mở lại app từ bước 6

FragmentA: onStart
FragmentB: onStart
MainActivity: onStart
MainActivity: onResume
FragmentA: onResume
FragmentB: onResume
MainActivity: onResumeFragments

Trường hợp 8: Gắn đồng thời 2 fragment

MainActivity: onCreate
FragmentA: onAttach
MainActivity: onAttachFragment
FragmentA: onCreate
FragmentA: onCreateView
FragmentA: onViewCreated
FragmentA: onActivityCreated
FragmentB: onAttach
MainActivity: onAttachFragment
FragmentB: onCreate
FragmentB: onCreateView
FragmentB: onViewCreated
FragmentB: onActivityCreated
FragmentA: onStart
FragmentB: onStart
MainActivity: onStart
MainActivity: onResume
FragmentA: onResume
FragmentB: onResume
MainActivity: onResumeFragments

Một số lưu ý

  • Nếu bạn sử dụng add() FragmentB và dùng addToBackStack() thì FragmentB sẽ hiện lên và đề lên Fragment cũ. Nếu FragmentB trong suốt có thể nhìn thấy FragmentA. Khi remove sẽ hủy fragmentB để hiện ra FragmentA.(Vì Fragment này còn nguyên trên View và trong BackStack chưa bị hủy).
  • Nếu bạn sử dụng add() FragmentB và không dùng addToBackStack() thì FragmentB sẽ hiện lên và đề lên Fragment cũ. Nếu FragmentB trong suốt có thể nhìn thấy FragmentA. Khi remove sẽ remove luôn cả Activity (Lúc gán A lên Activity ở onCreate() cũng không dùng addToBackStack)
  • Nếu bạn sử dụng replace() FragmentB và dùng addToBackStack() thì A sẽ bị xóa khỏi View, B hiện lên và thêm A vào BackStack. Do đó khi ấn nút Back thì hủy Fragment B sau đó lấy từ BackStack ra A và hiện A lên View.
  • Nếu bạn sử dụng replace() FragmentB và không dùng addToBackStack() thì A sẽ bị xóa khỏi View và bị destroy, ấn nút Back sẽ detroy luôn cả Activity vì bây giờ BackStack không có fragment nào (Lúc gán A lên Activity ở onCreate() cũng không dùng addToBackStack).
  • Chú ý là BackStack để lưu trạng thái trước nó chứ không phải nó. Ví dụ đoạn code dưới đây sẽ lưu trạng thái của cái main chứ không phải lưu trạng thái của FragmentA. Muốn lưu trạng thái của FragmentA thì ta phải thực hiện ở câu lệch tiếp theo.
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.main, FragmentA.getInstanse(), "FragmentA").addToBackStack(null);
        transaction.commit();

Trên đây mình đã trình bày những kiến thức cơ bản nhất về Fragment mà mình học được từ nhiều nơi. Nếu như có sai xót gì. Mong các bạn để lại bình luận bên dưới. Để mình có thể cải thiện bài viết. Cảm ơn bạn đọc!

Registration Login
Sign in with social account
or
Lost your Password?
Registration Login
Sign in with social account
or
A password will be send on your post
Registration Login
Registration