【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参数。具体的代码我就不在这里展示了

相关推荐
C4rpeDime14 分钟前
自建MD5解密平台-续
android
鲤籽鲲2 小时前
C# Random 随机数 全面解析
android·java·c#
m0_548514776 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯6 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯6 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐7 小时前
Handle
android
m0_748232928 小时前
Android Https和WebView
android·网络协议·https
m0_748251728 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
m0_7482546610 小时前
go官方日志库带色彩格式化
android·开发语言·golang
zhangphil10 小时前
Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现“刮刮乐”效果,Kotlin(2)
android·kotlin