add 1 fragment hay replace 1 fragment?
chúng khác nhau cái gì? khi nào nên dùng cái này khi nào nên dùng cái kia?
Yeah đó củng là chủ đề của hôm nay mình muốn giới thiệu đến các bạn thông qua 1 demo nho hỏi nhỏ :v

giao diện chương trình và cấu trúc dự án

Lớp MainActivity.java activity duy nhất của chương trình dùng để host các fragment với 2 phương thức addFragment() và replaceFragment(). Ah để ý ở đây khi mình add hay replace đều không add to backstack nhé.

  • 1 button để kích hoạt sự kiện addFragment
  • 1 button để kích hoạt sự kiện replaceFragment
  • 1 button để tạo sự kiện keyback (tương tự nhất nút back trên device)
package com.example.linhphan.fragmentmanager;

import android.support.annotation.IdRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private int number = -1;

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

        Button btnAdd = (Button) findViewById(R.id.btn_add);
        Button btnReplace = (Button) findViewById(R.id.btn_replace);
        Button btnBack = (Button) findViewById(R.id.btn_back);

        btnAdd.setOnClickListener(this);
        btnReplace.setOnClickListener(this);
        btnBack.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_add:
                addFragment(R.id.fl_main_content, getFragmentName(), false);

                break;

            case R.id.btn_replace:
                replaceFragment(R.id.fl_main_content, getFragmentName(), false);
                break;

            case R.id.btn_back:
                onBackPressed();
                break;
        }
    }

    public <T extends Fragment> void addFragment(@IdRes int containerLayoutId, String className, boolean isAddBackStack){
        Fragment fragment = Fragment.instantiate(this, className, null);
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.add(containerLayoutId, fragment, className);
        if (isAddBackStack) {
            transaction.addToBackStack(className);
        }
        transaction.commit();
    }

    public <T extends Fragment>void replaceFragment(@IdRes int containerLayoutId, String className, boolean isAddBackStack){
        Fragment fragment = Fragment.instantiate(this, className);
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(containerLayoutId, fragment, className);
        if (isAddBackStack) {
            transaction.addToBackStack(className);
        }
        transaction.commit();
    }

    private String getFragmentName(){
        number = ++number % 2;
        switch (number){
            case 0:
                return AFragment.class.getName();
            case 1:
                return BFragment.class.getName();
            default:
                return AFragment.class.getName();
        }
    }
}

Lớp BaseFragment.java lớp này chủ yếu override lại các method chính trong lifecycle của Fragment và ghi logs tương ứng xuống console.

package com.example.linhphan.fragmentmanager;

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

/**
 * Created by linhphan on 7/21/16.
 */
public class BaseFragment extends Fragment {

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.e(getClass().getName(), "onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(getClass().getName(), "onCreate");
    }

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

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.e(getClass().getName(), "onActivityCreated");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.e(getClass().getName(), "onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.e(getClass().getName(), "onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.e(getClass().getName(), "onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.e(getClass().getName(), "onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.e(getClass().getName(), "onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(getClass().getName(), "onDestroy");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.e(getClass().getName(), "onDetach");
    }
}

Các lớp AFragment.java, BFragment.java, chỉ đơn giản extend từ BaseFragment.java. 2 Lớp này sẽ được add hoặc replace trong MainActivity. Để cho ngắn gọn mình chỉ show code của 1 lớp, các lớp còn lại đều tương tự.

package com.example.linhphan.fragmentmanager;

/**
 * Created by linhphan on 7/21/16.
 */
public class AFragment extends BaseFragment {
}

Ok như vậy là mình đã show hết cho các bạn phần source code rồi. Bây giờ thì run dự án để xem sự khác biệt giữ Replace() và add() nhé.

FragmentTransaction#Replace()
Lần lượt tab vào button replace 3 lần, sau mỗi lần tab thì nhớ xem log xong rồi mới tab tiếp. Nhớ bật cửa sổ Android Monitor của Android Studio lên để xem logs nhé.

**********    tab lần 1    **********

com.example.linhphan.fragmentmanager.AFragment: onAttach
com.example.linhphan.fragmentmanager.AFragment: onCreate
com.example.linhphan.fragmentmanager.AFragment: onCreateView
com.example.linhphan.fragmentmanager.AFragment: onActivityCreated
com.example.linhphan.fragmentmanager.AFragment: onStart
com.example.linhphan.fragmentmanager.AFragment: onResume

**********    tab lần 2     *************
com.example.linhphan.fragmentmanager.AFragment: onPause
com.example.linhphan.fragmentmanager.AFragment: onStop
com.example.linhphan.fragmentmanager.AFragment: onDestroyView
com.example.linhphan.fragmentmanager.AFragment: onDestroy
com.example.linhphan.fragmentmanager.AFragment: onDetach
com.example.linhphan.fragmentmanager.BFragment: onAttach
com.example.linhphan.fragmentmanager.BFragment: onCreate
com.example.linhphan.fragmentmanager.BFragment: onCreateView
com.example.linhphan.fragmentmanager.BFragment: onActivityCreated
com.example.linhphan.fragmentmanager.BFragment: onStart
com.example.linhphan.fragmentmanager.BFragment: onResume

*********   tab back button    ***************
com.example.linhphan.fragmentmanager.BFragment: onPause
com.example.linhphan.fragmentmanager.BFragment: onStop
com.example.linhphan.fragmentmanager.BFragment: onDestroyView
com.example.linhphan.fragmentmanager.BFragment: onDestroy
com.example.linhphan.fragmentmanager.BFragment: onDetach

Từ kết quả logs ở trên chúng ta nhận thấy rằng?
Ở lần tab thứ nhất AFragment được khởi tạo và được attach lên màn hình(onResume)
Ở lần tab thứ 2 AFragment bị destroy hoàn toàn(onDestroy, onDetach) sau đó BFragment mới được khởi tạo và attach lên màn hình
Ở lần tab back button BFragment bị destroy hoàn toàn và sau đó hệ thống sẽ đóng ứng dụng.

FragmentTransaction#add()
Các bạn củng lần lượt tab vào button add 3 lần, sau mỗi lần tab thì nhớ xem log xong rồi mới tab tiếp. Ah mà tốt nhất bạn nên đóng app rồi mở lại để xem nhé!

*********   tab lần 1    ***************  com.example.linhphan.fragmentmanager.AFragment: onAttach
com.example.linhphan.fragmentmanager.AFragment: onCreate
com.example.linhphan.fragmentmanager.AFragment: onCreateView
com.example.linhphan.fragmentmanager.AFragment: onActivityCreated
com.example.linhphan.fragmentmanager.AFragment: onStart
com.example.linhphan.fragmentmanager.AFragment: onResume

*********   tab lần 2    ***************

com.example.linhphan.fragmentmanager.BFragment: onAttach
com.example.linhphan.fragmentmanager.BFragment: onCreate
com.example.linhphan.fragmentmanager.BFragment: onCreateView
com.example.linhphan.fragmentmanager.BFragment: onActivityCreated
com.example.linhphan.fragmentmanager.BFragment: onStart
com.example.linhphan.fragmentmanager.BFragment: onResume

*********   tab back button    ***************
com.example.linhphan.fragmentmanager.AFragment: onPause
com.example.linhphan.fragmentmanager.BFragment: onPause
com.example.linhphan.fragmentmanager.AFragment: onStop
com.example.linhphan.fragmentmanager.BFragment: onStop
com.example.linhphan.fragmentmanager.AFragment: onDestroyView
com.example.linhphan.fragmentmanager.AFragment: onDestroy
com.example.linhphan.fragmentmanager.AFragment: onDetach
com.example.linhphan.fragmentmanager.BFragment: onDestroyView
com.example.linhphan.fragmentmanager.BFragment: onDestroy
com.example.linhphan.fragmentmanager.BFragment: onDetach

Từ kết quả logs trên bạn đã thấy sự khác nhau giữ add() và replace() chưa? Yeah sự khác nhau ở chổ lần tab thứ 2 và back.
Ở lần tab thứ 2 BFragment được khởi tạo và attach lên màn hình nhưng AFragment vẩn không bị destroy thậm chí không bị pause (vẩn tiếp tục chạy song song với BFragment)
Ở làn tab back lúc này thì cả AFragment và BFragment mới được destroy.

Kết Luận:
– replace() là remove fragment hiện tại và add 1 fragment mới. Trong trường hợp bạn gọi hàm addToBackStack() thì khi nhấn back, fragment củ sẽ được khởi tạo lại từ onCreateView() -> onResume()
– Trái lại add() vẩn giữ nguyên trạng thái(không bị pause) của fragment hiện tại và add 1 fragment mới. Nên khi nhấn back fragment củ sẽ không cần gọi lại các method trong lifecycle của nó như onPause(), onCreateView(),….

thân ái!

Advertisements