Skip to content

Android - Fragment

一个 Fragment 代表应用 UI 或行为中可重用的部分,位于 Activity 内部。你可以将 Fragment 视为 Activity 的一个模块化部分,它拥有自己的生命周期,接收自己的输入事件,并且可以在 Activity 运行时添加或移除。

使用 Fragment 的主要特点和优势:

  • 模块化(Modularity):Fragment 允许你将 Activity 的 UI 和逻辑分解为独立的、可重用的组件。
  • 自适应 UI(Adaptable UIs):你可以在单个 Activity 中组合多个 Fragment 来构建多窗格 UI(例如,用于平板电脑),并为单窗格 UI(例如,用于手机)单独重用这些相同的 Fragment。
  • 生命周期(Lifecycle):Fragment 有自己的生命周期,它与宿主 Activity 的生命周期紧密相关。当 Activity 暂停时,其中的所有 Fragment 也会暂停,依此类推。
  • 可重用性(Reusability):一个 Fragment 可以在多个 Activity 中使用。
  • 无 UI Fragment(Headless Fragments):Fragment 不一定需要有 UI。你可以创建无 UI Fragment 来封装与 Activity 生命周期绑定的后台行为或数据处理。
  • 与现代 Android Jetpack 集成:Fragment 是 Android Jetpack 的核心部分,与 ViewModel、LiveData 和 Navigation Component 等组件良好集成。

通过扩展 androidx.fragment.app.Fragment 类来创建 Fragment。然后,你可以通过在 XML 布局文件中使用 <fragment> 标签(已弃用,推荐使用 <androidx.fragment.app.FragmentContainerView>)声明式地将 Fragment 插入到 Activity 的布局中,或者通过 FragmentManager 和 FragmentTransaction 以编程方式进行操作。

想象一个显示文章列表的应用。在手机上,选择一篇文章可能会导航到一个新屏幕(Activity)来显示文章内容。在平板电脑上,由于屏幕空间更大,文章列表可以在左侧的一个 Fragment 中,而所选文章的内容可以在右侧的另一个 Fragment 中显示,所有这些都在同一个 Activity 中。Fragment 使这种自适应设计更容易实现。

Fragment 的生命周期直接受其宿主 Activity 生命周期影响。例如,当 Activity 接收到 onPause() 时,Activity 中的每个 Fragment 也会接收到 onPause()。

以下是你可以在 Fragment 类中重写的关键生命周期回调方法(来自 androidx.fragment.app.Fragment):

  • onAttach(Context context): Fragment 与 Activity 关联时调用(Activity 在此处通过 context 传递,使用 context 更安全)。在 onCreate() 之前调用。
  • onCreate(Bundle savedInstanceState): 系统在创建 Fragment 时调用此方法。你应该在此初始化 Fragment 中需要保留的关键组件,以便在 Fragment 暂停或停止后恢复时使用。这是初始化 ViewModel 的好地方。
  • onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState): 系统在 Fragment 首次绘制其用户界面时调用此方法。你必须从此方法返回一个 View,作为 Fragment 布局的根视图。如果 Fragment 不提供 UI,可以返回 null。你在此处加载(inflate)你的布局 XML。
  • onViewCreated(View view, Bundle savedInstanceState): 在 onCreateView() 返回后立即调用,但在任何保存的状态恢复到视图之前。你应在此设置视图(例如,通过 ID 查找视图,设置监听器,观察 LiveData)。
  • onStart(): 当 Fragment 对用户可见时调用(其所在的 Activity 已启动)。
  • onResume(): 当 Fragment 可见并正在积极运行时调用(其所在的 Activity 已恢复)。Fragment 现在可以与用户交互。
  • onPause(): 系统调用此方法作为用户离开 Fragment 的首个指示(尽管这并不总是意味着 Fragment 将被销毁)。通常,你应在此处提交任何应在当前用户会话结束后持久化的更改(例如,保存用户输入)。
  • onStop(): 当 Fragment 对用户不再可见时调用。这可能是因为 Activity 正在停止或有新的 Fragment 覆盖了它。
  • onDestroyView(): 当与 Fragment 关联的视图层次结构被移除时调用。你应在此清理与 Fragment 视图关联的资源(例如,清除对视图的引用以防止内存泄漏)。
  • onDestroy(): 调用此方法进行 Fragment 状态的最终清理。在 Fragment 不再使用时调用。如果进程被终止,不保证会调用此方法。
  • onDetach(): Fragment 与 Activity 解除关联时调用。这是 onAttach() 的对应方法。

实现 Fragment 需要几个关键步骤:

  1. 定义 Fragment 类:创建一个扩展 androidx.fragment.app.Fragment 的类。实现必要的生命周期回调,特别是 onCreateView() 以提供 Fragment 的布局。
  2. 创建 Fragment 的布局:为 Fragment 的 UI 设计一个 XML 布局文件。
  3. 将 Fragment 添加到 Activity:你可以通过两种方式完成此操作:
  4. 管理 Fragment 事务:使用 getSupportFragmentManager()(来自 FragmentActivity 或 AppCompatActivity)开始添加、替换或移除 Fragment 的事务。

让我们创建一个简单示例,其中 Activity 动态地将一个 Fragment 加载到一个容器中。

  1. 创建 MyFragment.java:
package com.example.fragmentapp;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class MyFragment extends Fragment {
private static final String ARG_MESSAGE = "message";
public static MyFragment newInstance(String message) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
fragment.setArguments(args);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView textView = view.findViewById(R.id.fragment_textview);
if (getArguments() != null) {
String message = getArguments().getString(ARG_MESSAGE);
textView.setText(message != null ? message : "Hello from MyFragment!");
}
}
}
  1. 创建布局 res/layout/fragment_my.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp"
android:background="#E0E0E0">
<TextView
android:id="@+id/fragment_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Hello from MyFragment!" />
</LinearLayout>
  1. 修改 MainActivity.java 和 res/layout/activity_main.xml:

res/layout/activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/button_load_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Fragment" />
</LinearLayout>

MainActivity.java:

package com.example.fragmentapp;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button loadFragmentButton = findViewById(R.id.button_load_fragment);
loadFragmentButton.setOnClickListener(v -> {
loadFragment();
});
// Optionally load fragment initially
if (savedInstanceState == null) { // Ensure fragment is added only once
loadFragmentWithMessage("Initial Fragment Load");
}
}
private void loadFragment() {
loadFragmentWithMessage("Fragment Loaded by Button");
}
private void loadFragmentWithMessage(String message) {
// Create an instance of the fragment
MyFragment myFragment = MyFragment.newInstance(message);
// Get the FragmentManager and start a transaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransaction.replace(R.id.fragment_container, myFragment);
fragmentTransaction.addToBackStack(null); // Optional: if you want back navigation
// Commit the transaction
fragmentTransaction.commit();
}
}
  • 使用 androidx.fragment.app.FragmentContainerView 作为动态添加 Fragment 的容器。它提供更好的 Fragment 动画和 Z 轴顺序处理。
  • 使用 Bundle 通过 Fragment.setArguments(Bundle) 将参数传递给 Fragment。在 Fragment 类内部使用静态 newInstance() 工厂方法封装参数创建过程。
  • 使用 ViewModel 和 LiveData(或 Kotlin 的 Flow)在 Fragment 和 Activity 之间进行通信。这可以解耦组件并处理生命周期复杂性。
  • 使用 Navigation Component 管理 Fragment 导航并在目的地之间传递数据。它简化了复杂的导航图。
  • 对于简单的对话框,使用 DialogFragment。
  • 确保你使用的是 androidx.fragment 库,而不是已弃用的平台版本。

Fragment 是构建灵活和自适应 Android UI 的强大工具。掌握其生命周期和交互模式是开发健壮的 Android 应用的关键。