【Android】Fragment基本使用

Fragment是什么

Fragment是一种可以嵌入在Activity当中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用得非常广泛。

Fragment的使用方式

介绍了这么多抽象的东西,是时候学习一下Fragment的具体用法了。首先我们要创建一个平板模拟器,创建模拟器的方法在第1章中已经学过了,这里就不再赘述。这次我们选择创建一个Pixel C平板模拟器,创建完成后启动模拟器

好了,准备工作都完成了,接着新建一个FragmentTest项目,然后开始我们的Fragment探索之旅吧。

Fragment的简单用法

这里我们准备先写一个最简单的Fragment示例来练练手。在一个Activity当中添加两个Fragment,并让这两个Fragment平分Activity的空间。

新建一个左侧Fragment的布局left_fragment.xml,代码如下所示:

Java 复制代码
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Button" />
</LinearLayout>

这个布局非常简单,只放置了一个按钮,并让它水平居中显示。

然后新建右侧Fragment的布局right_fragment.xml,代码如下所示:

Java 复制代码
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#00ff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="24sp"
        android:text="This is right fragment" />
</LinearLayout>

可以看到,我们将这个布局的背景色设置成了绿色,并放置了一个TextView用于显示一段文本。接着新建一个LeftFragment类,并让它继承自Fragment。

注意,这里可能会有两个不同包下的Fragment供你选择:一个是系统内置的android.app.Fragment,一个是AndroidX库中的androidx.fragment.app.Fragment。

这里请一定要使用AndroidX库中的Fragment,因为它可以让Fragment的特性在所有Android系统版本中保持一致,而系统内置的Fragment在Android 9.0版本中已被废弃。使用AndroidX库中的Fragment并不需要在build.gradle文件中添加额外的依赖,只要你在创建新项目时勾选了Use androidx.* artifacts选项,AndroidStudio会自动帮你导入必要的AndroidX库。

现在编写一下LeftFragment中的代码,如下所示:

Java 复制代码
public class LeftFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.left_fragment, container, false);
    }
}

这里仅仅是重写了Fragment的onCreateView()方法,然后在这个方法中通过LayoutInflater的inflate()方法将刚才定义的left_fragment布局动态加载进来,整个方法简单明了。接着我们用同样的方法再新建一个RightFragment,代码如下所示:

Java 复制代码
public class RightFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.right_fragment, container, false);
    }
}

修改activity_main.xml中的代码,如下所示:

Java 复制代码
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/leftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/rightFrag"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
</LinearLayout>

可以看到,我们使用了标签在布局中添加Fragment,其中指定的大多数属性你已经非常熟悉了,只不过这里还需要通过android:name属性来显式声明要添加的Fragment类名,注意一定要将类的包名也加上。这样最简单的Fragment示例就已经写好了,现在运行一下程序。

正如我们预期的一样,两个Fragment平分了整个Activity的布局。不过这个例子实在是太简单了,在真正的项目中很难有什么实际的作用,因此下面我们马上来看一看,关于Fragment更加高级的使用技巧。

静态2

再fragment类里面重写onviewcreated方法

其中调用view。findbyid方法

动态添加Fragment

在上一节当中,你已经学会了在布局文件中添加Fragment的方法,不过Fragment真正的强大之处在于,它可以在程序运行时动态地添加到Activity当中。根据具体情况来动态地添加Fragment,你就可以将程序界面定制得更加多样化。

们在上一节代码的基础上继续完善,新建another_right_fragment.xml,代码如下所示:

Java 复制代码
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ffff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="24sp"
        android:text="This is another right fragment" />
</LinearLayout>

这个布局文件的代码和right_fragment.xml中的代码基本相同,只是将背景色改成了黄色,并将显示的文字改了改。然后新建AnotherRightFragment作为另一个右侧Fragment,代码如下所示:

Java 复制代码
public class AnotherRightFragment extends Fragment {

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

代码同样非常简单,在onCreateView()方法中加载了刚刚创建的another_right_fragment布局。这样我们就准备好了另一个Fragment,接下来看一下如何将它动态地添加到Activity当中。

Java 复制代码
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 定义了一个fragment,用于动态加载LeftFragment -->
    <fragment
        android:id="@+id/leftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <!-- 定义了一个FrameLayout,可用于占位或加载其他视图组件 -->
    <FrameLayout
        android:id="@+id/rightLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" >
    </FrameLayout>

</LinearLayout>

可以看到,现在将右侧Fragment替换成了一个FrameLayout。还记得这个布局吗?在上一章中我们学过,这是Android中最简单的一种布局,所有的控件默认都会摆放在布局的左上角。由于这里仅需要在布局里放入一个Fragment,不需要任何定位,因此非常适合使用FrameLayout。

下面我们将在代码中向FrameLayout里添加内容,从而实现动态添加Fragment的功能。修改MainActivity中的代码,如下所示:

Java 复制代码
public class MainActivity extends AppCompatActivity {

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

        // 获取button绑定的OnClickListener
        View button = findViewById(R.id.button); // 确保您在XML中定义了一个id为button的视图
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                replaceFragment(new AnotherRightFragment());
            }
        });

        // 初始时加载RightFragment
        replaceFragment(new RightFragment());
    }
    private void replaceFragment(Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.rightLayout, fragment); // 确保您在XML中定义了一个id为rightLayout的FrameLayout
        transaction.commit();
    }
}

可以看到,首先我们给左侧Fragment中的按钮注册了一个点击事件,然后调用replaceFragment()方法动态添加了RightFragment。当点击左侧Fragment中的按钮时,又会调用replaceFragment()方法,将右侧Fragment替换成AnotherRightFragment。

结合replaceFragment()方法中的代码可以看出,动态添加Fragment主要分为5步。

(1) 创建待添加Fragment的实例。

(2) 获取FragmentManager,在Activity中可以直接调用getSupportFragmentManager()方法获取。

(3) 开启一个事务,通过调用beginTransaction()方法开启。

(4) 向容器内添加或替换Fragment,一般使用replace()方法实现,需要传入容器的id和待添加的Fragment实例。

(5) 提交事务,调用commit()方法来完成。

在Fragment中实现返回栈

在上一小节中,我们成功实现了向Activity中动态添加Fragment的功能。不过你尝试一下就会发现,通过点击按钮添加了一个Fragment之后,这时按下Back键程序就会直接退出。如果我们想实现类似于返回栈的效果,按下Back键可以回到上一个Fragment,该如何实现呢?其实很简单,FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中。修改MainActivity中的代码,如下所示:

Java 复制代码
public class MainActivity extends AppCompatActivity {

    private void replaceFragment(Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 使用transaction.replace()方法替换fragment
        transaction.replace(R.id.rightLayout, fragment);
        // 将事务添加到后退栈
        transaction.addToBackStack(null);
        // 提交事务
        transaction.commit();
    }
}

这里我们在事务提交之前调用了FragmentTransaction的addToBackStack()方法,它可以接收一个名字用于描述返回栈的状态,一般传入null即可。现在重新运行程序,并点击按钮将AnotherRightFragment添加到Activity中,然后按下Back键,你会发现程序并没有退出,而是回到了RightFragment界面。继续按下Back键,RightFragment界面也会消失,再次按下Back键,程序才会退出。

Fragment和Activity之间的交互

虽然Fragment是嵌入在Activity中显示的,可是它们的关系并没有那么亲密。实际上,Fragment和Activity是各自存在于一个独立的类当中的,它们之间并没有那么明显的方式来直接进行交互。如果想要在Activity中调用Fragment里的方法,或者在Fragment中调用Activity里的方法,应该如何实现呢?为了方便Fragment和Activity之间进行交互,FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取Fragment的实例,代码如下所示:

Java 复制代码
RightFragment rightFragment = (RightFragment) getFragmentManager().findFraqmentById(R.id.right_fragment);

调用FragmentManager的findFragmentById()方法,可以在Activity中得到相应Fragment的实例,然后就能轻松地调用Fragment里的方法了。

掌握了如何在Activity中调用Fragment里的方法,那么在Fragment中又该怎样调用Activity里的方法呢?这就更简单了,在每个Fragment中都可以通过调用getActivity()方法来得到和当前Fragment相关联的Activity实例,代码如下所示:

Java 复制代码
// 假设 activity 是一个 Activity 类型的引用
if (activity != null && activity instanceof MainActivity) {
    MainActivity mainActivity = (MainActivity) activity;
    // 现在 mainActivity 是 MainActivity 类型的实例,可以调用 MainActivity 的方法或访问其成员变量
}

这里由于getActivity()方法有可能返回null,因此我们需要先进行一个判空处理。有了Activity的实例,在Fragment中调用Activity里的方法就变得轻而易举了。另外当Fragment中需要使用Context对象时,也可以使用getActivity()方法,因为获取到的Activity本身就是一个Context对象。

这里由于getActivity()方法有可能返回null,因此我们需要先进行一个判空处理。有了Activity的实例,在Fragment中调用Activity里的方法就变得轻而易举了。另外当Fragment中需要使用Context对象时,也可以使用getActivity()方法,因为获取到的Activity本身就是一个Context对象。这时不知道你心中会不会产生一个疑问:既然Fragment和Activity之间的通信问题已经解决了,那么不同的Fragment之间可不可以进行通信呢?说实在的,这个问题并没有看上去那么复杂,它的基本思路非常简单:首先在一个Fragment中可以得到与它相关联的Activity,然后再通过这个Activity去获取另外一个Fragme

Fragment的生命周期

和Activity一样,Fragment也有自己的生命周期,并且它和Activity的生命周期实在是太像了,我相信你很快就能学会,下面我们马上就来看一下。

Fragment的状态和回调

还记得每个Activity在其生命周期内可能会有哪几种状态吗?

没错,一共有运行状态、暂停状态、停止状态和销毁状态这4种。

类似地,每个Fragment在其生命周期内也可能会经历这几种状态,只不过在一些细小的地方会有部分区别。

  1. 运行状态
    1. 当一个Fragment所关联的Activity正处于运行状态时,该Fragment也处于运行状态。
  2. 暂停状态
    1. 当一个Activity进入暂停状态时(由于另一个未占满屏幕的Activity被添加到了栈顶),与它相关联的Fragment就会进入暂停状态。
  3. 停止状态
    1. 当一个Activity进入停止状态时,与它相关联的Fragment就会进入停止状态,或者通过调用FragmentTransaction的remove()、replace()方法将Fragment从Activity中移除,但在事务提交之前调用了addToBackStack()方法,这时的Fragment也会进入停止状态。总的来说,进入停止状态的Fragment对用户来说是完全不可见的,有可能会被系统回收。
  4. 销毁状态
    1. Fragment总是依附于Activity而存在,因此当Activity被销毁时,与它相关联的Fragment就会进入销毁状态。或者通过调用FragmentTransaction的remove()、replace()方法将Fragment从Activity中移除,但在事务提交之前并没有调用addToBackStack()方法,这时的Fragment也会进入销毁状态。

结合之前的Activity状态,相信你理解起来应该毫不费力吧。同样地,Fragment类中也提供了一系列的回调方法,以覆盖它生命周期的每个环节。其中,Activity中有的回调方法,Fragment中基本上也有,不过Fragment还提供了一些附加的回调方法,下面我们就重点看一下这几个回调。

  1. onAttach():当Fragment和Activity建立关联时调用。
  2. onCreateView():为Fragment创建视图(加载布局)时调用。
  3. onActivityCreated():确保与Fragment相关联的Activity已经创建完毕时调用。
  4. onDestroyView():当与Fragment关联的视图被移除时调用。
  5. onDetach():当Fragment和Activity解除关联时调用。

agment还提供了一些附加的回调方法,下面我们就重点看一下这几个回调。

  1. onAttach():当Fragment和Activity建立关联时调用。
  2. onCreateView():为Fragment创建视图(加载布局)时调用。
  3. onActivityCreated():确保与Fragment相关联的Activity已经创建完毕时调用。
  4. onDestroyView():当与Fragment关联的视图被移除时调用。
  5. onDetach():当Fragment和Activity解除关联时调用。

外链图片转存中...(img-AyI7gc6n-1722145897512)

在Fragment中你也可以通过onSaveInstanceState()方法来保存数据,因为进入停止状态的Fragment有可能在系统内存不足的时候被回收。保存下来的数据在onCreate()、onCreateView()和onActivityCreated()这3个方法中你都可以重新得到,它们都含有一个Bundle类型的savedInstanceState参数。具体的代码我就不在这里展示了

相关推荐
sun0077003 小时前
android ndk编译valgrind
android
AI视觉网奇4 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空4 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet5 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin5 小时前
PHP serialize 序列化完全指南
android·开发语言·php
tangweiguo030519877 小时前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
00后程序员张9 小时前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone
柳岸风10 小时前
Android Studio Meerkat | 2024.3.1 Gradle Tasks不展示
android·ide·android studio
编程乐学10 小时前
安卓原创--基于 Android 开发的菜单管理系统
android
whatever who cares13 小时前
android中ViewModel 和 onSaveInstanceState 的最佳使用方法
android