Hướng dẫn create và add fragment vào Activity trong Android

0
50
Bài này thuộc phần 3 của 4 phần trong series Hướng dẫn toàn tập Fragment trong Android

Add fragment to Activity

Hướng dẫn toàn tập về cách sử dụng Fragment trong Android

Phần 3: Tạo và thêm Fragment vào Activity trong Android

Bài viết trước chúng ta đã cùng nhau tìm hiểu về vòng đời của Fragment – điều mà tất cả các bạn cần phải nắm chắc trước khi bắt tay vào code.

Sau khi đã thấm nhuần lý thuyết, chúng ta bắt tay vào việc đầu tiên đó là tạo một Fragment. Khi có được một fragment thì làm sao “nhồi” nó vào được Activity để hiển thị ra màn hình?

Let’s go…

1. Cách tạo một fragment trong Android

Quay lại ứng dụng Rage Comics,  khi khởi động ứng dụng xong sẽ hiển thị một danh sách các Rages. Khi touch vào bất kì mục nào sẽ hiển thị thông tin (tạm gọi là trang detail) về comic đó. Để bắt đầu, bạn cần làm ngược lại tức là tạo trang detail đã.

Đầu tiên mở thư mục app trong Android Studio và tìm file layout cho trang detail fragment_rage_comic_details.xml theo đường dẫn  app -> res -> layout. Chúng ta thiết kế màn hình theo bố cục gồm: một Image và bên dưới là text mô tả. Các bạn tham khảo bố cục như hình bên dưới.

android_fragments_008_fragment_details_layout_preview-626x500

Sau khi thiết kế layout xong thì chuyển sang code  logic. Các bạn vào Android Studio’s Project tab  chọn file RageComicDetailsFragment.kt. Đây chính là file Fragment để chúng ta code phần hiển thị cho trang detail.

Trong RageComicDetailsFragment.kt đoạn code sẽ như sau:

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
//1
class RageComicDetailsFragment : Fragment() {
  //2
  companion object {
    fun newInstance(): RageComicDetailsFragment {
      return RageComicDetailsFragment()
    }
  }
  //3
  override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                            savedInstanceState: Bundle?): View? {
    return inflater?.inflate(R.layout.fragment_rage_comic_details, container, false)
  }
}

Mình sẽ giải thích một chút về đoạn code trên:

  1. Khai báo RageComicDetailsFragment là một class con của Fragment (Nếu bạn chưa hiểu khái niệm class con, class cha thì bạn cần tìm hiểu thêm về lập trình hướng đối tượng, cũng nhanh thôi)
  2. Cung cấp hàm khởi tạo instance cho fragment. Ở đây chính là một dạng factory method
  3. Cuối cùng là override lại hàm onCreateView để có thể nhồi file layout xml vào fragment class.

Activity  sử dụng setContentView() để nhồi xml layout thì fragments thực hiện trong hàm onCreateView()

Tham số thứ 3 của inflate xác định liệu có thêm fragment vào container hay không? (false là không thêm). Container  là view cha và nó sẽ chứa các view của fragment. Bạn nên đặt giá trị là false vì nên để việc thêm fragment vào containner cho FragmentManager đảm nhiệm

Mỗi Activity sẽ có một FragmentManager. Như tên gọi của nó thì FragmentManagercó nhiệm vụ quản lý các fragment được đính trên Activity. Nó có những hàm để truy cập, thêm và xóa fragments.

Tại sao lại sử dụng factory method thay vì hàm contructor để tạo fragment?

Trong hướng dẫn này, mình sử dụng factory method để khởi tạo fragment thay vì sử dụng constructor. Nhiều bạn sẽ thắc mắc tại sao lại vậy đúng không?

  • Thứ nhất: Do bạn không định nghĩa bất kỳ constructor nào, trình biên dịch tự động tạo ra một constructor mặc định, không có đối số đủ để tạo được i Đây là tất cả những gì bạn cần có cho một fragment.
  • Thứ hai: Khi ứng dụng chuyển sang chạy nền(background) thì Android sẽ destroy Activities cùng tất cả các fragment liên quan. Khi Activity được visibile trở lại, FragmentManager sẽ tạo lại các fragment bằng cách sử dụng constructor rỗng mặc định. Nếu nó không tìm thấy thì sẽ bị exception.

Do đó, cách code thông minh nhất không nên tạo class với constructor có tham số. Nếu có tạo constructor có tham số thì bắt buộc bạn phải viết thêm hàm constructor không tham số. Cách đơn giản nhất là dùng factory method như mình làm ở trên.

Đừng bỏ qua:  Cách tạo Chat Head như Facebook Messenger

2. Add fragment vào Activity

Có 2 cách để thêm một fragment:

  • Cách đơn giản nhất là add fragment trực tiếp vào XML layout. Cách này làm thì nhanh nhưng có nhược điểm là không linh động và khó khăn trong việc trao đổi dữ liệu giữa các fragment
  • Cách thứ 2 là sử dụng code để add fragment

Mình sẽ giới thiệu lần lượt cả 2 cách thêm fragment này.

2.1 Thêm trực tiếp Fragment bằng XML

Để làm việc này, trước tiên mở activity_main.xml và thêm vào bên trong FrameLayout

<fragment
  android:id="@+id/details_fragment"
  class="com.raywenderlich.alltherages.RageComicDetailsFragment"
  android:layout_width="match_parent"
  android:layout_height="match_parent"/>

Trong bước này bạn đặt <fragment>ở bên trong của activity layout và chỉ định class fragment thông qua thuộc tính class. View ID của <fragment> cần được yêu cầu thông qua FragmentManager.

OK. Bạn thử build thử xem thành quả nhé

android_fragments_012_app_details_fragment_test-281x500

Đừng bỏ qua:  Tại sao sử dụng Kotlin cho phát triển ứng dụng Android?

2.2 Add fragment thông qua code

Đầu tiên, mở activity_main.xml và xóa thẻ <fragment>mà bạn mới thêm vào mà bạn vừa làm lúc trước. Với cách thêm fragment bằng code thì chúng ta không cần thẻ <fragment>trong XML. Bạn sẽ thay thế nó bởi code viết bằng Kotlin

Mở RageComicListFragment.kt để tiến hành chỉnh sửa code.

Bạn imports các packages như bên dưới:

import android.os.Bundle
import android.support.v7.widget.GridLayoutManager

GridLayoutManagergiúp việc định vị các items trong danh sách Rage Comic.

Bên trong của RageComicListFragment.kt, thêm hai phương thức sau để tạo  RageComicAdapter

override fun onAttach(context: Context?) {
    super.onAttach(context)

    // Get rage face names and descriptions.
    val resources = context!!.resources
    names = resources.getStringArray(R.array.names)
    descriptions = resources.getStringArray(R.array.descriptions)
    urls = resources.getStringArray(R.array.urls)

    // Get rage face images.
    val typedArray = resources.obtainTypedArray(R.array.images)
    val imageCount = names.size
    imageResIds = IntArray(imageCount)
    for (i in 0..imageCount - 1) {
      imageResIds[i] = typedArray.getResourceId(i, 0)
    }
    typedArray.recycle()
  }

  override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                            savedInstanceState: Bundle?): View? {

    val view: View = inflater!!.inflate(R.layout.fragment_rage_comic_list, container,
        false)
    val activity = activity
    val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view) as RecyclerView
    recyclerView.layoutManager = GridLayoutManager(activity, 2)
    recyclerView.adapter = RageComicAdapter(activity)
    return view
  }

Hàm onAttach() chứa code có thể truy cập các resource mà bạn cần thông qua Context mà fragment đính kèm. Bởi vì chúng ta viết đoạn code này ở trong onAttach(), bạn có thể yên tâm rằng fragment này chắc chắn có một Context hợp lệ.

Để có thể tạo danh sách Rage Comic, chúng ta sử dụng RecyclerView

Tiếp theo mở MainActivity.kt và thay thế onCreate()với đoạn code dưới đây:

Tại đây, bạn sẽ đặt RageComicListFragment vào trong MainActivity.

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
   if (savedInstanceState == null) {
     supportFragmentManager
             .beginTransaction()
             .add(R.id.root_layout, RageComicListFragment.newInstance(), "rageComicList")
             .commit()
   }
 }

Để ý nhé,  nếu bạn sử dụng thư viện support v4 thì thay vì gọi FragmentManager, bạn gọi hàm supportFragmentManager (Hai class này có vai trò tương đương nhau)

Sau đó, bạn bắt đầu tạo một transaction bằng việc gọi tới beginTransaction.

Hàm add()có 3 tham số mà bạn cần để ý:

  • View ID của một container của fragment trong layout của Activity. Nếu bạn nhìn vào xml, bạn sẽ tìm thấy @+id/root_layout.
  • Instance fragment được thêm vào. Ở đây là instance của RageComicListFragment
  • Một String dùng để định danh cho Fragment. String được FragmentManager coi như là ID để quản lý

Cuối cùng, là hàm commit()để FragmentManager thêm vào Activitiy.

Bạn thử chạy ứng dụng và xem thành quả nhé

android_fragments_013_app_list_build-281x500

Đừng bỏ qua:  24 giờ - Quá đủ để tìm một ý tưởng ứng dụng Android "tỷ đô"

Tại sao phải kiểm tra trạng thái của Fragment trước khi add?

Trong đoạn mã ở trên, hàm onCreate()có khối if chứa mã thêm fragment và kiểm tra xem Activity có được lưu không. Khi một Activity được lưu, tất cả các fragment active của nó cũng được lưu. Nếu bạn không thực hiện kiểm tra này, thì ứng dụng có thể bị lỗi

Bạn tham khảo hình bên dưới để hiểu tại sao nhé

android_fragments_d003_fragments_too_many-296x500

3. Tạm kết

Như vậy chúng ta đã cùng như tìm hiểu chi tiết cách tạo và thêm một Fragment vào trong một Activity. Mình tổng kết lại một chút nhé:

  • Nên sử dụng factory method thay vì viết constructor để tạo fragment
  • Có 2 cách để thêm một fragment vào Activity: một là sử dụng trực tiếp thẻ <fragment> trong XML layout, hai là sử dụng code.

Sau khi đã hiểu cách create và add fragment rồi. Chắc chắn bạn sẽ thắc mắc: làm thế nào để trao đổi dữ liệu giữa các fragment và giữa fragment với Activity? Cứ bình tĩnh, chúng ta sẽ cùng nhau tìm ở phần sau của bài viết nhé.

Xem tiếp các bài trong Series
Phần trước: Android Fragment Lifecycle – Những điều chưa kểPhần kế tiếp: Làm thế nào để Data Binding trong Fragment Android

BÌNH LUẬN

Please enter your comment!
Please enter your name here