Android Fragment 使用详解

1,082 阅读5分钟

1. Fragment 是什么?

Fragment 直译为 “碎片 ”或 “片段”,是一种可嵌入在 Activity 中的 UI 片段,可以看作是 Activity 的模块化部分,用于构建动态和灵活的用户界面,更加合理和充分利用大屏幕的空间。

Fragment 不能独立存在,必须嵌入到 Activity 中,其生命周期与所在的 Activity 紧密相关。

Fragment 用法类似于一个自定义控件。

主要特点:

模块化:Fragment 允许开发者将应用界面划分为多个独立、可重用的部分,每个 Fragment 都可以定义自己的布局和行为。

灵活性:Fragment 可以在运行时动态添加到 Activity 布局中,也可以在不同的 Activity 中重复使用,从而实现灵活的UI布局和界面复用。

适应不同屏幕尺寸:Fragment 非常适合在大屏幕设备(如平板电脑)上实现多窗格UI设计,同时也可以在小屏幕设备上适配不同的屏幕尺寸和方向。

生命周期管理:Fragment 拥有自己的生命周期,与 Activity 的生命周期类似但更为复杂。系统负责管理 Fragment 的生命周期,开发者可以在相应的生命周期回调方法中进行初始化、视图创建、数据加载等操作。

2. Fragment 的使用方式

2.1 静态添加 Fragment

新建左侧 Fragment 布局 left_fragment.xml,新建右侧 Fragment 布局 right_fragment.xml

新建一个 LeftFragment 类,继承自 Fragment(AndroidX) 类,将定义的布局动态加载进来。

public class LeftFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.left_fragment,container,false);//动态加载布局
        return view;
    }
}

新建一个 RightFragment 类。

修改 activity_main.xml需要通过 android: name 显式声明要添加的 fragment 类名,一定要加类的包名:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.trainning.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
    <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.trainning.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>

2.2 动态添加 Fragment

新建 another_right_fragment.xml,新建 AnotherRightFragment,代码省略。

修改 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.trainning.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
    <FrameLayout
        android:id="@+id/right_layout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>

在 Activity 中动态添加 Fragment:

  1. 创建待添加的 fragment 实例。
  2. 获取 FragmentManager,调用 getSupportFragmentManager。
  3. 开启一个事务 beginTransaction。
  4. 替换 fragment,replace。
  5. 提交事务 。
public class NormalFragmentActivity extends AppCompatActivity {
    private Button leftFragmentButton;
    private View leftFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_normal_fragment);
        leftFragment = findViewById(R.id.left_fragment);
        leftFragmentButton = leftFragment.findViewById(R.id.left_fragment_button);
        replaceFragment(new RightFragment());
        leftFragmentButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                replaceFragment(new AnotherRightFragment());
            }
        });
    }

    private void replaceFragment(Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();// 获取 FragmentManager
        FragmentTransaction transaction = fragmentManager.beginTransaction();// 开启一个事务 beginTransaction()
        transaction.replace(R.id.right_layout, fragment);
        transaction.commit();
    }
}

FragmentManager 用于管理 Fragment 的生命周期、交互和事务。Activity 是 FragmentManager 的宿主环境类。

FragmentTransaction:提供了一系列的方法来执行对 Fragment 的事务性操作,用来管理 Fragment 的添加、移除、替换以及执行其他操作。

事务的提交是通过调用 commit 方法完成的。一旦事务被提交,系统就会按照你指定的操作顺序来更新Fragment的状态。

“事务”(Transaction)是指一系列对Fragment进行的操作,这些操作被封装在一个单独的执行单元中,并且要么全部成功执行,要么全部回滚(即不执行任何操作)。这种机制确保了Fragment状态的一致性,防止了因部分操作失败而导致的界面不一致或应用崩溃。

3. 在 Fragment 里模拟返回栈

模拟返回栈对于 Fragment 的管理是非常重要的。通过模拟返回栈,你可以让用户在 Fragment 之间进行导航,并且能够通过按下 back 键返回到之前的 Fragment 状态,而不是直接关闭 Activity。

在 Fragment 事务中,你使用 addToBackStack() 方法将 Fragment 事务添加到返回栈中。这样,当用户按下后退键时,系统会自动回退到上一个 Fragment。

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout,fragment);
    transaction.addToBackStack(null); //接受一个名字描述返回栈的状态
    transaction.commit();
}

addToBackStack 可以传入一个 null 作为名称,或者传入一个唯一的字符串来标识这个返回栈条目,如:

fragmentTransaction.addToBackStack("my_unique_back_stack_entry"); 

这个名称在调试时可能会派上用场,它可以帮助你识别返回栈中的不同条目。在大多数情况下,传入 null 就足够了,因为系统会自动为每个返回栈条目分配一个唯一的 ID。

4. Fragment 和 Activity 之间的通信

在 Activity 中获得 Fragment 实例:

LeftFragment leftFragment = (LeftFragment)getSupportFragmentManager().findFragmentById(R.id.left_fragment);

在 Fragment 中获得相关联 Activity 的实例:

NormalFragmentActivity activity = (NormalFragmentActivity) getActivity();

Fragment 中需要使用 Context 对象时,也可以使用 getActivity()方法。

下面介绍两种常用的通信方式:

4.1 Activity 使用 Bundle 向 Fragment 传递数据

在 Activity 中创建 Fragment 实例并传递数据:

MyFragment myFragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("key", "value");
myFragment.setArguments(bundle);
replaceFragment(myFragment);

在 Fragment 中接收数据:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bundle arguments = getArguments();
    if (arguments != null) {
        String value = arguments.getString("key");
        // 使用传递过来的数据
    }
}

4.2 Fragment 使用接口向 Activity 传递数据

定义接口:

public interface IFragmentCallback {
    void onFragmentChanged();
}

Activity:

public class FragmentActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_normal_fragment);
        LeftFragment leftFragment = new LeftFragment();
        leftFragment.setFragmentCallback(new IFragmentCallback() {
            @Override
            public void onFragmentChanged() {
                Log.i("this", "click button in the fragment");
            }
        });
    }
    
}

Fragment:

public class LeftFragment extends Fragment {

    private IFragmentCallback fragmentCallback;
    View view;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (view == null) {
            view = inflater.inflate(R.layout.left_fragment, container,false);
        }
        Button button = view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                fragmentCallback.onFragmentChanged();
            }
        });
        return view;
    }

    public void setFragmentCallback(IFragmentCallback fragmentCallback) {
        this.fragmentCallback = fragmentCallback;
    }
}

当点击 Fragment 中的按钮时,Activity 中就会收到回调,并可以处理相应的逻辑。

除此之外,activity 与 fragment 之间的通信还可以采用其他方案,如 EventBus、LiveData 等,其本质上都是使用了观察者模式。

5. Fragment 的生命周期

Fragment 的生命周期可参考:fragment 生命周期  |  Android Developers ,此文章中进行了详细解析。

image.png

后续会专门出文章对 Fragment 进行源码分析。