Fragment la gi

Dẫn nhập

Ở bài học trước chúng ta đã làm quen với ACTIVITY & VÒNG ĐỜI CỦA ỨNG DỤNG

Với việc càng ngày càng nhiều mẫu mã thiết bị Android ra đời thì người ta đã nghĩ ra những cách khác nhau để bố trí giao diện ứng dụng sao cho phù hợp để tận dụng diện tích màn hình. Một trong số đó là Fragment. Bài viết này chúng ta sẽ cùng tìm hiểu về nó.


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về các phần:

  • ACTIVITY & VÒNG ĐỜI CỦA ỨNG DỤNG
  • CÁCH TẠO MỘT PROJECT ANDROID CƠ BẢN

Trong bài học này, chúng ta sẽ cùng tìm hiểu các vấn đề:

  • Fragment là gì?
  • Vòng đời của Fragment.
  • Cách tạo một Activity với Fragment cơ bản.
  • Tích hợp Backstack cho Fragment trong Activity đã tạo.

Fragment

Fragment là gì?

Trước tiên chúng ta xem 2 hình phía dưới.      

Fragment la gi
    
Fragment la gi

OK, bạn nhận xét thế nào?

Rõ ràng ở hình thứ 2, cách phân bố các item được tận dụng tốt hơn. Mỗi mục bên trái tương ứng với một nội dung bên phải. So với việc chỉ hiển thị đầu mục Setting như hình thứ nhất.

Đó cũng là ưu điểm khi sử dụng Fragment!

Mặt khác, chúng ta có thể hiểu: Fragment là một dạng nhiều Activity con, lồng trong Activity mẹ. Các Fragment cũng sở hữu vòng đời (Lifecycle) của mình, vòng đời của Fragment cũng gần gần tương tự như Activity (giống đến 89%).


Tạo một Fragment

Các bạn có thể tưởng tượng rằng một Fragment là một vùng chứa trong Activity, một module riêng. Và nó có vòng đời của mình.

Bài viết này chủ yếu là thực hành, và chúng ta sẽ làm một ứng dụng nhỏ có tên FragmentExample, có sử dụng Fragment trong thư viện Android Support Library (hỗ trợ đến Android đời cũ tận 1.6).

Bước 1:Chúng ta vẫn tạo một project bình thường như mọi khi, với Activity là Empty Activity:

Fragment la gi

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

activity_main.xml







    

Bước 2:Thêm Fragment vào XML

Mặc dù Fragment có tính module hóa cao, có thể được tái sử dụng. Nhưng mỗi Fragment muốn sử dụng đều phải gắn với một Activity mẹ. Do đó, chúng ta sẽ thêm vào trong Activity chính như sau, đồng thời chuyển RelativeLayout sang dạng LinearLayout cho dễ hình dung:




    

    

    

Cùng nhìn lại thiết kế của XML, chúng ta sẽ thấy fragment đã được tạm dựng (“tạm” nhé, vì chưa có nội dung, nhưng đã thấy được chiều cao của nó):

Fragment la gi

Bước 3: Tạo các fragment tương ứng:

Như các bạn đã thấy, ở trên chúng ta đã định nghĩa 2 fragment trong Activity. Fragment thực chất là 1 dạng Activity con, vì vậy chúng ta cần dựng một class riêng cho nó để dễ quản lý.

Chúng ta sẽ tạo một class tên là Fragment1 và kế thừa từ lớp android.support.v4.app.Fragment:

Fragment la gi

  • Đặt tên class là Fragment1.java và code sẽ có nội dung như sau:
package com.howkteam.fragmentexample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment1 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }
}
  • Tiếp đến là Fragment2.java cũng tương tự:
package com.howkteam.fragmentexample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment2 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }
}

Khoan! Tại sao lại là onCreateView mà không phải là onCreate?

Như đã đề cập nhiều lần ở trên, Fragment không phải Activity mà chứa trong Activity. Nó có vòng đời riêng, layout riêng và vì thế sẽ cần một số cơ chế khác để đổ dữ liệu và các View vào bên trong. Hãy nhìn qua sơ đồ vòng đời của Fragment:

Fragment la gi

Đấy, các thao tác rẽ nhánh giờ đây đã được chuyển sang 2 bước mới là onCreateViewonDestroyView.

Bước 4:Tạo file layout cho Fragment.

Một Fragment cũng có layout như Activity mà thôi, và vì thế chúng ta cần tạo layout cho nó. Chuột phải vào thư mục res/layout > New > Layout resource file:

  • Chúng ta đặt tên cho layout là fragment_1, trong Fragment này ta sẽ đặt một TextView với nội dung tùy ý, đại khái code XML có nội dung như sau:


   
    

  • Tương tự, chúng ta tạo thêm một layout nữa có tên fragment_2:



    

Bước 5: Liên kết class java với layout fragment:

Class java đã có, XML cũng đã có, giờ chúng ta bắt đầu liên kết fragment với layout tương ứng. Nếu như trong Activity, chúng ta sử dụng setContentView thì ở Fragment, chúng ta dùng tham số đầu vào Inflater để nhét layout, việc thực hiện cũng khác:

Fragment_1.java

package com.howkteam.fragmentexample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment1 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_1, container, false);
    }
}

Fragment_2.java

package com.howkteam.fragmentexample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment2 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_2, container, false);
    }
}

Bước 6: Sau khi chuẩn bị xong xuôi. Activity đã có, 2 Fragment cũng đã có. Giờ chúng ta sẽ liên kết chúng lại. Rất đơn giản, chỉ việc sửa đổi một chút ở file activity_main.xml như sau:




    

    


Việc khai báo rất đơn giản, phải không nào? Chạy thử thôi:

Fragment la gi


Cơ chế Backstack

Với những giải thích và ví dụ ở phần trên, chúng ta đã biết cách để gán 2 Fragment vào một Activity. Việc gán nhiều Fragment vào Activity cũng vậy.

Vậy giả sử trong trường hợp bạn chỉ có một khung Fragment, và muốn hoán đổi nhiều Fragment khác lần lượt vào cái khung đó thì sao? Và việc gì xảy ra nếu chúng ta nhấn nút Back (quay lại) trên máy?

Các bạn hãy nhìn hình mô tả sau:

Fragment la gi

Chúng ta đều mong muốn cách mọi thứ diễn ra sẽ như trên. Khi chuyển fragment và nhấn back thì fragment cũ sẽ được đưa về. Nhưng thực tế sẽ là…

Fragment la gi

Tại sao? Vì nút Back trên thiết bị Android mặc định sẽ chỉ có tác dụng với Activity đang hiện hữu chứ không phải Fragment. Và vì thế chúng ta sẽ cần sử dụng Fragment Backstack: Đưa các Fragment vào một ngăn xếp (giống như khi bạn duyệt trang web vậy, nhấn back thì trình duyệt sẽ quay lại trang trước đó).

Bước 1:Cách thực hiện cũng không khó. Vẫn sử dụng project mà chúng ta đang code, thêm vào một Activity khác tạm gọi là BackstackActivity:

Fragment la gi

BackstackActivity.java

package com.howkteam.fragmentexample;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

public class BackstackActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_backstack);
    }
}
  • Và cùng với đó là một file layout tương ứng:

activity_backstack.xml







    

Các bạn chú ý tag FrameLayout ở trên, đây sẽ là cái khung mà lát nữa chúng ta sẽ đổ fragment vào. Được ư? Quá được chứ, Fragment có thể được đổ vào layout bất kỳ. Nút Click me sẽ là nút mà chúng ta sử dụng để chuyển Fragment trong ví dụ này.

Bước 2: Đây mới là phần hay.

Để hiểu Fragment Backstack thì đương nhiên là chúng ta cần… Fragment. Sau khi làm ví dụ ở phần trên thì lúc này chúng ta đã có 2 cái là Fragment_1Fragment_2, và 2 Fragment này hoàn toàn có thể được sử dụng lại.

Và Android cung cấp một công cụ cực kỳ tuyệt vời để quản lý Fragment. Đó là class FragmentManager. Nó sẽ giúp các bạn thay đổi / thêm / bỏ Fragment tùy ý. Để xem chúng có gì khác nhau, chúng ta sẽ viết từng hàm riêng lẻ, sau đó gọi trong onCreate để biết tính năng của từng cái:

Kiểu 1: Fragment_2 thay thế hoàn toàn Fragment_1, Fragment_1 bị hủy. Đơn giản là chỉ cần Lấy Fragment_2 chiếm vị trí của Fragment_1 mà thôi:

  • Thêm 2 dòng sau vào fragment_1.xml:



    

  • Để cho chữ sang bên phải, lát nữa chúng ta sẽ biết tác dụng ^^
    • Sửa đổi file AndroidManifest.xml để chỉ định BackstackActivity là Activity khởi chạy:






    

        

            

                



                

            

        

    



Bước 3: Cuối cùng sửa file BackstackActivity như sau. Hơi dài chút nhưng các bạn chú ý mấy chỗ tô vàngtô xanh là đủ:

package com.howkteam.fragmentexample;



import android.os.Bundle;

import android.support.annotation.Nullable;

import android.support.v4.app.Fragment;

import android.support.v4.app.FragmentManager;

import android.support.v4.app.FragmentTransaction;

import android.support.v7.app.AppCompatActivity;

import android.util.Log;

import android.view.View;

import android.widget.Button;



public class BackstackActivity extends AppCompatActivity {



    private Button btnFrag;



    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);



        setContentView(R.layout.activity_backstack);

        btnFrag = (Button) findViewById(R.id.btn_replace_fragment);

        btnFrag.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                replaceFragmentContent(new Fragment2());

                Log.e("Replaced fragment", "2");

            }

        });



        initFragment();

    }



    private void initFragment() {

        Fragment1 firstFragment = new Fragment1();

        FragmentManager fragmentManager = getSupportFragmentManager();

        FragmentTransaction ft = fragmentManager.beginTransaction();

        ft.replace(R.id.container_body, firstFragment);

        ft.commit();

    }



    protected void replaceFragmentContent(Fragment fragment) {

        if (fragment != null) {

            FragmentManager fmgr = getSupportFragmentManager();

            FragmentTransaction ft = fmgr.beginTransaction();

            ft.replace(R.id.container_body, fragment);

            ft.commit();

        }

    }

}
  • Ta khởi tạo và nhét Fragment_1 vào Activity ngay lúc Activity hiện hình bằng cách gọi hàm initFragment() trong onCreate().
  • Mình viết hàm replaceFragmentContent để thay thế cái container_body (vốn chính là cái FrameLayout trong activity_backstack.xml đó. Sau đó đặt event cho cái nút: Khi bấm nút thì Fragment_2 sẽ nhảy vào.

Tức là khi mở lên, chưa bấm nút, nó sẽ thế này:

Fragment la gi

Và sau khi bấm nút, nó sẽ thế này:

Fragment la gi

Mình cố tình để hiện Android Monitor để các bạn có thể xem log đó. Nhớ tận dụng log để test nhé.

Ấy thế nhưng khi chúng ta nhấn nút Back thì…

Fragment la gi

Bùm! Activity đã bị hủy. Đó là vì nút back chỉ tác dụng lên Activity, và không có Fragment nào khác trong danh sách Backstack (giống việc bàn cờ có 2 quân, quân này ăn quân kia thì chỉ còn 1).

Và để làm cho nút Back sau khi nhấn sẽ quay lại về Fragment_1 thì chúng ta viết 1 hàm khác và gọi ra trong Activity như sau:

package com.howkteam.fragmentexample;



import android.os.Bundle;

import android.support.annotation.Nullable;

import android.support.v4.app.Fragment;

import android.support.v4.app.FragmentManager;

import android.support.v4.app.FragmentTransaction;

import android.support.v7.app.AppCompatActivity;

import android.util.Log;

import android.view.View;

import android.widget.Button;



public class BackstackActivity extends AppCompatActivity {



    private Button btnFrag;



    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);



        setContentView(R.layout.activity_backstack);

        btnFrag = (Button) findViewById(R.id.btn_replace_fragment);

        btnFrag.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                addFragment(new Fragment2());

                Log.e("Replaced fragment", "2");

            }

        });



        initFragment();

    }



    private void initFragment() {

        Fragment1 firstFragment = new Fragment1();

        FragmentManager fragmentManager = getSupportFragmentManager();

        FragmentTransaction ft = fragmentManager.beginTransaction();

        ft.replace(R.id.container_body, firstFragment);

        ft.commit();

    }



    protected void replaceFragmentContent(Fragment fragment) {

        if (fragment != null) {

            FragmentManager fmgr = getSupportFragmentManager();

            FragmentTransaction ft = fmgr.beginTransaction();

            ft.replace(R.id.container_body, fragment);

            ft.commit();

        }

    }



    protected void addFragment(Fragment fragment) {

        FragmentManager fmgr = getSupportFragmentManager();

        FragmentTransaction ft = fmgr.beginTransaction();

        ft.add(R.id.container_body, fragment);

        ft.addToBackStack(fragment.getClass().getSimpleName());

        ft.commit();

    }

}

Chạy thử nào, khi chúng ta nhấn nút thì màn hình sẽ chuyển sang Fragment_2:

Fragment la gi

Và khi nhấn Back thì…

Fragment la gi

Không có gì khó hiểu, phải không nào?


Kết luận

Qua bài này chúng ta đã nắm được Fragment là gì, mối quan hệ của nó với Activity, và cách tạo một Fragment đơn giản. Và cơ chế Backstack.

Chúng ta sẽ tìm hiểu về kiểu thiết kế Material Design của Google, cũng như quy chuẩn code Android (Coding Convention) hiệu quả qua bài tiếp theo: MATERIAL DESIGN & CODING CONVENTION

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng.