【Android】Fragment 从基础到进阶全解析

325 阅读5分钟

Fragment 基础认知

1. 定义与作用

Fragment 代表了 Activity 里的一部分用户界面或者功能。它有独立的生命周期,能接收输入事件,还能在 Activity 运行期间添加或移除。其主要作用是实现界面模块化,提升代码复用率,以及让界面在不同屏幕尺寸上有更好的适配性。

2. 核心特性

  • 具备独立的生命周期,和 Activity 的生命周期相互关联。
  • 可以在多个 Activity 中重复使用。
  • 能通过 FragmentManager 进行管理,支持添加、替换等操作。
  • 可以和宿主 Activity 以及其他 Fragment 进行通信。

Fragment 生命周期

Fragment 的生命周期比 Activity 更为复杂,主要方法如下:

image.png

1. 关键回调方法

  • onAttach(Context context):当 Fragment 和 Activity 建立关联时调用。
  • onCreate(Bundle savedInstanceState):系统创建 Fragment 时调用。
  • onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState):用于创建 Fragment 的视图。
  • onActivityCreated(Bundle savedInstanceState):宿主 Activity 完成 onCreate() 后调用。
  • onStart():Fragment 对用户可见时调用。
  • onResume():Fragment 开始和用户交互时调用。
  • onPause():Fragment 暂停和用户交互时调用。
  • onStop():Fragment 对用户不可见时调用。
  • onDestroyView():Fragment 的视图被销毁时调用。
  • onDestroy():Fragment 被销毁时调用。
  • onDetach():Fragment 和 Activity 的关联解除时调用。

2. 与Activity生命周期关系

Activity 的生命周期会直接影响 Fragment 的生命周期。比如,当 Activity 执行 onPause() 时,它所包含的 Fragment 也会执行 onPause()

Fragment 创建与使用

1. 继承Fragment

要继承 Fragment(或 androidx.fragment.app.Fragment)类,并且重写 onCreateView() 方法。

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

2. 在布局中静态添加 Fragment

在 Activity 的布局文件里使用 <fragment> 标签。

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/my_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

3. 在代码中动态添加 Fragment

借助 FragmentManager 和 FragmentTransaction 来实现。

FragmentManager supportFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();

fragmentTransaction.add(R.id.my_fragment, new SecondFragment());
fragmentTransaction.commit();

Fragment 间通信

1. Fragment与Activity通信

  • 接口回调:Fragment 定义接口,Activity 实现该接口。
public class FirstFragment extends Fragment {

    private OnFragmentInteractionListener mListener;


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_first, container, false);
        Button button = view.findViewById(R.id.send_button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String data = "Hello from fragment";
                sendDataToActivity(data);
            }
        });
        return view;
    }


    public interface OnFragmentInteractionListener {
        void onFragmentInteraction(String data);
    }

    // 在需要通信的地方调用
    private void sendDataToActivity(String data) {
        if (mListener != null) {
            mListener.onFragmentInteraction(data);
        }
    }

public class MainActivity extends AppCompatActivity implements FirstFragment.OnFragmentInteractionListener{

    private TextView tvData;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        tvData = findViewById(R.id.tv_data);
        FragmentManager supportFragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();

        fragmentTransaction.add(R.id.my_fragment, new FirstFragment());
        fragmentTransaction.commit();
    }

    @Override
    public void onFragmentInteraction(String data) {
        tvData.setText(data);
    }
}
  • ViewModel:使用 ViewModel 实现数据共享。
public class SharedViewModel extends ViewModel {
    private MutableLiveData<String> data = new MutableLiveData<>();

    public LiveData<String> getData() {
        return data;
    }

    public void setData(String newData) {
        data.setValue(newData);
    }
}
public class MainActivity extends AppCompatActivity {

    private TextView tvData;

    private SharedViewModel viewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        tvData = findViewById(R.id.tv_data);
        viewModel = new ViewModelProvider(this).get(SharedViewModel.class);
        viewModel.getData().observe(this, data -> {
            // 当数据变化时,更新 Activity 的 UI
            Log.d("MainActivity", "Received data: " + data);
            tvData.setText(data);
            Toast.makeText(this, "Activity received: " + data, Toast.LENGTH_SHORT).show();
        });

        FragmentManager supportFragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();

        fragmentTransaction.add(R.id.my_fragment, new SecondFragment());
        fragmentTransaction.commit();
    }
}
public class SecondFragment extends Fragment {

    private SharedViewModel viewModel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_second, container, false);

        // 初始化 ViewModel
        viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

        // 观察 ViewModel 中的数据变化
        viewModel.getData().observe(getViewLifecycleOwner(), data -> {
            // 当数据变化时,更新 Fragment 的 UI
            Log.d("FirstFragment", "Received data: " + data);
            TextView textView = view.findViewById(R.id.fragment_text);
            textView.setText(data);
        });

        // 添加一个按钮点击事件,用于发送数据到 ViewModel
        Button sendButton = view.findViewById(R.id.send_button);
        sendButton.setOnClickListener(v -> {
            String data = "Hello from Fragment";
            viewModel.setData(data);
        });

        return view;
    }
}

2. Fragment之间通信

  • 通过共同的 Activity 作为中介。
  • 利用 ViewModel 共享数据。
public class MyFragment extends Fragment {
    private SharedViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 获取 Activity 作用域的 ViewModel
        viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

        // 发送数据
        button.setOnClickListener(v -> {
            viewModel.setData("Hello from MyFragment");
        });
    }
}

// AnotherFragment.java
public class AnotherFragment extends Fragment {
    private SharedViewModel viewModel;

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 获取相同的 ViewModel 实例
        viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

        // 观察数据变化
        viewModel.getData().observe(getViewLifecycleOwner(), data -> {
            textView.setText(data);
        });
    }
}

Fragment事务操作

1. 事务基础操作

1. 事务的本质

  • 原子性操作集:一组对 Fragment 的操作(如添加、替换)可作为一个单元执行。
  • 支持回退栈:通过 addToBackStack() 可模拟 “返回” 行为。

2. 获取FragmentManager

  • Activity中
FragmentManager manager = getSupportFragmentManager(); // AndroidX
  • Fragment中
FragmentManager childManager = getChildFragmentManager(); // 子 Fragment 管理

2. 核心事务操作

1. 添加Fragment

FragmentManager supportFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();
fragmentTransaction.add(R.id.my_fragment, new SecondFragment());
fragmentTransaction.commit();

2. 替换Fragment

fragmentTransaction.replace(R.id.my_fragment,new ThirdFragment()); // 等价于先 remove() 再 add()

3. 移除Fragment

Fragment second = supportFragmentManager.findFragmentByTag("Second");
if(second != null){
    fragmentTransaction.remove(second);
}

4. 隐藏/显示Fragment

fragmentTransaction.hide(second);
fragmentTransaction.show(second);

5. 附加/分离Fragment

transaction.detach(fragment); // 销毁视图,但保留实例和状态 
transaction.attach(fragment); // 重新创建视图

3. 事务回退栈管理

1. 添加到回退栈

transaction.addToBackStack("MyTransaction"); // 按名称入栈 
transaction.commit();

2. 出栈操作

manager.popBackStack(); // 弹出栈顶事务
manager.popBackStack("MyTransaction", 0); // 弹出指定事务及以上所有项

4. 事务执行时机

  1. 同步执行
transaction.commitNow(); // 同步执行,不支持回退栈
  1. 异步执行
transaction.commit(); // 在下一帧执行,允许与其他事务合并
  1. 安全提交
if(supportFragmentManager.isStateSaved()){
    fragmentTransaction.commit();
}
  • 适用场景:在 Activity/Fragment 状态已保存后(如 onSaveInstanceState 后)避免提交事务。

Fragment动画与过渡效果

1. 传统动画

transaction.setCustomAnimations(
        R.anim.enter_from_right,  // 进入动画
        R.anim.exit_to_left,      // 退出动画
        R.anim.pop_enter,         // 回退栈进入动画
        R.anim.pop_exit           // 回退栈退出动画
);

2. 过渡动画

// 在 Fragment 中设置
setEnterTransition(new Slide(Gravity.END));
setExitTransition(new Fade());

// 或在事务中设置
transaction.setReorderingAllowed(true); // 优化过渡效果

最佳实践

1. 避免状态丢失

  • 错误场景:在 Activity 的 onSaveInstanceState() 后提交事务。
  • 解决方案:使用 commitAllowingStateLoss()(谨慎使用)或检查状态:
    if (!isStateSaved()) {
        transaction.commit();
    }
    

2. 使用合适的容器

  • 推荐 FragmentContainerView
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

3. 谨慎使用回退栈

  • 避免过度入栈导致内存占用过高。
  • 使用 getBackStackEntryCount() 监控栈深度。

4. 处理配置变更*

  • 优先让系统自动恢复 Fragment 状态,而非手动管理。

  • 避免在 onCreate() 中重复添加 Fragment:

    if (savedInstanceState == null) {
        manager.beginTransaction()
            .add(R.id.container, new MyFragment())
            .commit();
    }