第10天:Fragments(碎片)使用

欢迎来到第10天的Android编程教程!今天我们将深入学习Fragments(碎片),了解其概念、生命周期以及如何在Activity中使用Fragments。通过详细的教程和实践实例,你将掌握如何在应用中灵活运用Fragments来构建模块化和可复用的用户界面。

本节学习内容对应的代码链接: FragmentTabExample

目录

  1. Fragments的概念
  2. Fragment的生命周期
  3. 在Activity中使用Fragment
  4. 实践实例:创建带有Tab的界面
    • 步骤1:创建新的Android项目
    • 步骤2:添加必要的依赖
    • 步骤3:创建Fragment布局和类
    • 步骤4:设置ViewPager2和TabLayout
    • 步骤5:创建FragmentStateAdapter
    • 步骤6:整合ViewPager2与TabLayout
    • 步骤7:运行和测试应用
  5. 总结

1. Fragments的概念

**Fragment(碎片)**是Android引入的一种可重用的用户界面组件,旨在简化复杂界面的管理,并提升应用在不同设备上的适应性。Fragments代表了Activity界面的一部分,允许在同一个Activity中组合多个独立的UI模块。

1.1 Fragments的起源与目的

随着Android设备种类的增多,屏幕尺寸和分辨率差异巨大,开发者需要构建能够适应各种屏幕的应用。传统上,开发者需要为不同设备设计不同的Activity,这不仅增加了开发复杂性,还导致代码重复和维护困难。

Fragments的引入解决了这些问题,通过以下方式提升开发效率和应用质量:

  • 模块化设计:将复杂的界面拆分为多个独立的模块,每个模块负责一个特定的功能或界面部分。
  • 可重用性:相同的Fragment可以在多个Activity中复用,减少代码重复。
  • 灵活的布局:在不同设备上,可以灵活地组合和排列Fragments,适应不同的屏幕尺寸和方向。
  • 动态管理:可以在运行时动态添加、替换或移除Fragments,实现更加动态和响应式的用户界面。

1.2 Fragments的主要特点

  • 生命周期独立于Activity:虽然Fragments依附于Activity,但它们拥有自己的生命周期,可以独立于Activity进行管理。
  • 交互与通信:Fragments可以与宿主Activity以及其他Fragments进行通信,传递数据和事件。
  • 管理界面状态:通过Fragments,可以更好地管理界面的状态和导航,提升用户体验。

1.3 Fragments的使用场景

  • 单一Activity,多模块界面:在一个Activity中通过多个Fragments构建复杂的用户界面,如导航栏、侧边栏等。
  • 平板与手机的适配:在手机上使用单一的Fragment,而在平板上同时显示多个Fragments,实现更丰富的界面布局。
  • 动态内容展示:根据用户操作动态加载不同的Fragments,如选项卡切换、详情页展示等。

1.4 Fragments与Activity的关系

Fragments并不是独立存在的,它们必须嵌入在Activity中。一个Activity可以包含一个或多个Fragments,而一个Fragment只能属于一个Activity。这种关系确保了应用界面的统一性和一致性。

1.5 示例:使用Fragments的优势

假设我们正在开发一个新闻应用,包含“头条”、“科技”、“体育”等多个类别。在没有Fragments的情况下,每个类别可能需要一个独立的Activity,导致大量重复代码和复杂的导航逻辑。而使用Fragments,可以在同一个Activity中通过不同的Fragments展示不同类别的新闻,实现模块化和高效管理。

使用Fragments的优势总结:

  • 简化界面管理:将复杂界面拆分为多个小模块,便于管理和维护。
  • 提升代码可维护性:通过模块化设计,减少代码重复,提升代码可读性和可维护性。
  • 增强界面灵活性:适应不同设备和屏幕尺寸,实现响应式设计。
  • 促进团队协作:不同的开发者可以独立开发和测试不同的Fragments,提高团队开发效率。

2. Fragment的生命周期

Fragments拥有一套独立于Activity的生命周期方法,但它们的生命周期与宿主Activity紧密相关。理解和正确管理Fragment的生命周期对于确保应用性能和用户体验至关重要。

2.1 Fragment生命周期概述

Fragment的生命周期分为多个阶段,每个阶段对应不同的生命周期方法。以下是Fragment的主要生命周期方法及其调用顺序:

  1. onAttach():Fragment与Activity关联时调用。
  2. onCreate():创建Fragment时调用,用于初始化非视图相关的组件。
  3. onCreateView():创建并返回Fragment的视图层次结构。
  4. onViewCreated():视图创建完成后调用,用于进一步初始化视图组件。
  5. onStart():Fragment对用户可见时调用。
  6. onResume():Fragment开始与用户交互时调用。
  7. onPause():Fragment即将停止与用户交互时调用。
  8. onStop():Fragment对用户不可见时调用。
  9. onDestroyView():销毁Fragment的视图层次结构时调用。
  10. onDestroy():销毁Fragment时调用。
  11. onDetach():Fragment与Activity解除关联时调用。

2.2 生命周期方法详解

2.2.1 onAttach()
  • 调用时机:Fragment被添加到Activity时调用,意味着Fragment与Activity建立了关联。

  • 用途:可以在此方法中获取宿主Activity的引用,进行一些依赖于Activity的初始化操作。

  • 示例

    override fun onAttach(context: Context) {super.onAttach(context)// 例如:初始化回调接口
    }
    
2.2.2 onCreate()
  • 调用时机:Fragment被创建时调用,此时Fragment还没有创建视图。

  • 用途:进行非视图相关的初始化操作,如初始化数据、配置选项等。

  • 示例

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 初始化数据
    }
    
2.2.3 onCreateView()
  • 调用时机:Fragment需要创建其视图时调用。

  • 用途:通过LayoutInflater加载布局文件,创建并返回Fragment的视图层次结构。

  • 示例

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?
    ): View? {// 加载布局return inflater.inflate(R.layout.fragment_example, container, false)
    }
    
2.2.4 onViewCreated()
  • 调用时机:在onCreateView()方法返回视图后立即调用。

  • 用途:对创建的视图进行进一步初始化,如设置监听器、绑定数据等。

  • 示例

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 初始化视图组件val button: Button = view.findViewById(R.id.button)button.setOnClickListener {// 处理点击事件}
    }
    
2.2.5 onStart()
  • 调用时机:Fragment即将对用户可见时调用。

  • 用途:进行一些与用户界面相关的准备工作,如注册广播接收器、启动动画等。

  • 示例

    override fun onStart() {super.onStart()// 启动动画或注册接收器
    }
    
2.2.6 onResume()
  • 调用时机:Fragment开始与用户交互时调用。

  • 用途:恢复用户界面状态,启动需要与用户交互的功能。

  • 示例

    override fun onResume() {super.onResume()// 恢复UI状态或启动交互功能
    }
    
2.2.7 onPause()
  • 调用时机:Fragment即将停止与用户交互时调用。

  • 用途:暂停正在进行的操作,如停止动画、保存数据等。

  • 示例

    override fun onPause() {super.onPause()// 暂停动画或保存数据
    }
    
2.2.8 onStop()
  • 调用时机:Fragment对用户不可见时调用。

  • 用途:释放占用的资源,如取消注册广播接收器、停止服务等。

  • 示例

    override fun onStop() {super.onStop()// 释放资源或取消注册
    }
    
2.2.9 onDestroyView()
  • 调用时机:Fragment的视图层次结构即将被销毁时调用。

  • 用途:清理与视图相关的资源,如解除绑定、清除引用等。

  • 示例

    override fun onDestroyView() {super.onDestroyView()// 清理视图相关资源
    }
    
2.2.10 onDestroy()
  • 调用时机:Fragment即将被销毁时调用。

  • 用途:进行最终的清理工作,如释放内存、关闭数据库连接等。

  • 示例

    override fun onDestroy() {super.onDestroy()// 释放内存或关闭连接
    }
    
2.2.11 onDetach()
  • 调用时机:Fragment与Activity解除关联时调用。

  • 用途:进行与Activity解除关联的清理工作,如释放Activity引用。

  • 示例

    override fun onDetach() {super.onDetach()// 释放Activity引用
    }
    

2.3 生命周期方法调用顺序

以下是Fragment生命周期方法的典型调用顺序:

  1. onAttach()
  2. onCreate()
  3. onCreateView()
  4. onViewCreated()
  5. onStart()
  6. onResume()
  7. onPause()
  8. onStop()
  9. onDestroyView()
  10. onDestroy()
  11. onDetach()

2.4 生命周期管理的最佳实践

  • 避免内存泄漏:在生命周期结束时,确保释放所有占用的资源,避免内存泄漏。例如,在onDestroyView()中清理视图绑定。
  • 遵循生命周期方法的职责分离:将不同的初始化和清理操作放在适当的生命周期方法中,确保代码逻辑清晰和可维护。
  • 处理配置变化:在生命周期方法中妥善处理配置变化(如屏幕旋转),确保数据和UI状态的正确恢复。
  • 使用ViewModel与LiveData:结合使用ViewModel和LiveData,可以更好地管理数据的生命周期,避免在Fragment生命周期方法中处理复杂的数据逻辑。

3. 在Activity中使用Fragment

在Android应用中,Fragments可以通过两种主要方式在Activity中使用:静态添加动态添加。了解这两种方式及其适用场景,有助于你灵活地构建应用界面。

3.1 静态添加Fragment

静态添加指的是在Activity的布局文件中直接定义Fragments。这种方式适用于界面结构相对固定,且不需要在运行时动态更改Fragments的场景。

3.1.1 优点
  • 简单易用:通过XML布局文件直接定义Fragments,减少了代码量。
  • 布局可视化:在设计器中可以直观地看到Fragments的布局效果。
3.1.2 缺点
  • 灵活性较低:无法在运行时动态更改Fragments的数量或类型。
  • 复用性有限:静态添加的Fragments难以在不同的Activity中复用。
3.1.3 示例:静态添加Fragment

假设我们有一个简单的Activity布局,需要在其中嵌入一个静态的Fragment。

步骤1:创建Fragment布局文件 fragment_static.xml

<!-- res/layout/fragment_static.xml -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E0F7FA"android:padding="16dp"><TextViewandroid:id="@+id/tv_static_fragment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是一个静态添加的Fragment"android:textSize="18sp"android:layout_gravity="center" />
</FrameLayout>

步骤2:创建Fragment类 StaticFragment.kt

// com.example.fragmenttabexample.StaticFragment.kt
package com.example.fragmenttabexampleimport android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroupclass StaticFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_static, container, false)}
}

步骤3:在Activity布局文件中添加Fragment

<!-- res/layout/activity_static_fragment.xml -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/static_fragment_container"android:layout_width="match_parent"android:layout_height="match_parent"><fragmentandroid:id="@+id/static_fragment"android:name="com.example.fragmenttabexample.StaticFragment"android:layout_width="match_parent"android:layout_height="match_parent" />
</FrameLayout>

步骤4:在Activity中设置布局

// com.example.fragmenttabexample.StaticFragmentActivity.kt
package com.example.fragmenttabexampleimport androidx.appcompat.app.AppCompatActivity
import android.os.Bundleclass StaticFragmentActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_static_fragment)}
}

运行效果

启动 StaticFragmentActivity,你将看到一个带有蓝色背景和文本“这是一个静态添加的Fragment”的界面。

3.2 动态添加Fragment

动态添加指的是在Activity运行时通过代码添加、替换或移除Fragments。这种方式适用于需要根据用户操作或应用状态动态调整界面的场景。

3.2.1 优点
  • 高度灵活:可以在运行时根据需要动态调整Fragments,适应不同的用户操作和应用状态。
  • 增强复用性:同一个Fragment可以在多个不同的场景中动态加载,提升复用性。
3.2.2 缺点
  • 增加代码复杂性:需要通过代码管理Fragments的添加、替换和移除,增加了代码的复杂性。
  • 生命周期管理:需要注意Fragments与Activity的生命周期同步,避免内存泄漏和状态异常。
3.2.3 示例:动态添加Fragment

假设我们有一个Activity,用户点击按钮后动态添加一个Fragment到界面中。

步骤1:创建动态添加的Fragment布局文件 fragment_dynamic.xml

<!-- res/layout/fragment_dynamic.xml -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFF9C4"android:padding="16dp"><TextViewandroid:id="@+id/tv_dynamic_fragment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是一个动态添加的Fragment"android:textSize="18sp"android:layout_gravity="center" />
</FrameLayout>

步骤2:创建Fragment类 DynamicFragment.kt

// com.example.fragmenttabexample.DynamicFragment.kt
package com.example.fragmenttabexampleimport android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroupclass DynamicFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_dynamic, container, false)}
}

步骤3:创建Activity布局文件 activity_dynamic_fragment.xml

<!-- res/layout/activity_dynamic_fragment.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/dynamic_fragment_container"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_add_fragment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="添加Fragment"android:layout_gravity="center_horizontal"android:layout_margin="16dp" /><FrameLayoutandroid:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>

步骤4:在Activity中实现动态添加Fragment的逻辑

// com.example.fragmenttabexample.DynamicFragmentActivity.kt
package com.example.fragmenttabexampleimport androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Buttonclass DynamicFragmentActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_dynamic_fragment)val addButton: Button = findViewById(R.id.btn_add_fragment)addButton.setOnClickListener {// 创建Fragment实例val dynamicFragment = DynamicFragment()// 获取FragmentManagerval fragmentManager = supportFragmentManager// 开启事务val fragmentTransaction = fragmentManager.beginTransaction()// 添加Fragment到容器fragmentTransaction.add(R.id.fragment_container, dynamicFragment)// 可选:添加到回退栈,方便用户按返回键撤销操作fragmentTransaction.addToBackStack(null)// 提交事务fragmentTransaction.commit()}}
}

运行效果

启动 DynamicFragmentActivity,点击“添加Fragment”按钮,界面下方将动态添加一个带有黄色背景和文本“这是一个动态添加的Fragment”的Fragment。如果多次点击按钮,多次添加相同的Fragment(实际应用中可以进行逻辑控制,避免重复添加)。

3.3 动态管理Fragments的最佳实践

  • 使用标签管理Fragments:在添加Fragments时,使用唯一的标签(tag)来标识,便于后续查找和管理。例如:

    fragmentTransaction.add(R.id.fragment_container, dynamicFragment, "DYNAMIC_FRAGMENT")
    
  • 添加到回退栈:在进行Fragment事务时,添加到回退栈(Back Stack)中,方便用户通过返回键撤销操作。

    fragmentTransaction.addToBackStack(null)
    
  • 避免内存泄漏:确保在生命周期结束时,解除所有引用,避免Fragment持有Activity的引用,导致内存泄漏。

  • 使用Fragment容器:为Fragments预留专门的容器(如FrameLayout),便于管理和布局调整。

  • 处理配置变化:在Fragment中妥善处理配置变化(如屏幕旋转),确保数据和UI状态的正确恢复。可以通过setRetainInstance(true)保持Fragment实例不被销毁,或结合使用ViewModel管理数据。


4. 实践实例:创建带有Tab的界面

在本节中,我们将通过一个具体的项目实例,演示如何使用Fragments、ViewPager2和TabLayout创建一个带有多个标签(Tab)的用户界面。每个Tab对应一个独立的Fragment,用户可以通过点击不同的标签切换显示内容。

项目概述

我们将创建一个名为 FragmentTabExample 的Android项目,该项目包含两个主要Fragment:“首页”和“设置”。用户可以通过顶部的TabLayout切换这两个Fragment。

步骤1:创建新的Android项目

  1. 打开Android Studio,点击 “Start a new Android Studio project”
  2. 选择 “Empty Activity” 模板,点击 “Next”
  3. 配置项目:
    • Name: FragmentTabExample
    • Package name: com.example.fragmenttabexample
    • Save location: 选择一个合适的位置。
    • Language: Kotlin
    • Minimum SDK: 根据需要选择(建议API 21+)。
  4. 点击 “Finish”,等待项目创建完成。

步骤2:添加必要的依赖

为了使用ViewPager2TabLayout,需要在项目的build.gradle文件中添加相关依赖。

  1. 打开 app/build.gradle 文件。

  2. dependencies 部分添加以下依赖:

    dependencies {implementation 'androidx.appcompat:appcompat:1.6.1'implementation 'com.google.android.material:material:1.9.0'implementation 'androidx.constraintlayout:constraintlayout:2.1.4'implementation 'androidx.viewpager2:viewpager2:1.0.0'// 其他依赖...
    }
    
  3. 点击 “Sync Now” 以同步项目。

步骤3:创建Fragment布局和类

我们将创建两个简单的Fragments,每个Fragment显示不同的内容。

3.1 创建第一个Fragment:FirstFragment
  1. 右键点击 com.example.fragmenttabexample 包,选择 “New” > “Fragment” > “Fragment (Blank)”

  2. 命名为 FirstFragment,取消勾选 “Generate Fragment Layout File”(我们将手动创建布局文件),然后点击 “Finish”

  3. res/layout 目录下创建布局文件 fragment_first.xml,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFCDD2"android:padding="16dp"><TextViewandroid:id="@+id/tv_first"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是第一个Fragment"android:textSize="18sp"android:layout_gravity="center" />
    </FrameLayout>
    
  4. 修改 FirstFragment.kt 文件,使其加载新的布局:

    package com.example.fragmenttabexampleimport android.os.Bundle
    import androidx.fragment.app.Fragment
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroupclass FirstFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_first, container, false)}
    }
    
3.2 创建第二个Fragment:SecondFragment
  1. 右键点击 com.example.fragmenttabexample 包,选择 “New” > “Fragment” > “Fragment (Blank)”

  2. 命名为 SecondFragment,取消勾选 “Generate Fragment Layout File”,然后点击 “Finish”

  3. res/layout 目录下创建布局文件 fragment_second.xml,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#C8E6C9"android:padding="16dp"><TextViewandroid:id="@+id/tv_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是第二个Fragment"android:textSize="18sp"android:layout_gravity="center" />
    </FrameLayout>
    
  4. 修改 SecondFragment.kt 文件,使其加载新的布局:

    package com.example.fragmenttabexampleimport android.os.Bundle
    import androidx.fragment.app.Fragment
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroupclass SecondFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_second, container, false)}
    }
    

步骤4:设置ViewPager2和TabLayout

我们将在主Activity的布局中添加ViewPager2TabLayout,以实现标签切换的功能。

  1. 打开 res/layout/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"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><com.google.android.material.tabs.TabLayoutandroid:id="@+id/tab_layout"android:layout_width="match_parent"android:layout_height="wrap_content"app:tabIndicatorColor="@color/teal_700"app:tabSelectedTextColor="@color/teal_700"app:tabTextColor="@color/black" /><androidx.viewpager2.widget.ViewPager2android:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>
    

步骤5:创建FragmentStateAdapter

为了解决Fragments与ViewPager2的关联问题,我们需要创建一个适配器类,该类负责管理Fragments的创建和销毁。

  1. com.example.fragmenttabexample 包下创建一个新Kotlin类,命名为 MyFragmentStateAdapter.kt

  2. MyFragmentStateAdapter.kt 中添加以下代码:

    package com.example.fragmenttabexampleimport androidx.fragment.app.Fragment
    import androidx.fragment.app.FragmentActivity
    import androidx.viewpager2.adapter.FragmentStateAdapterclass MyFragmentStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {// 定义要显示的Fragment列表private val fragments = listOf(FirstFragment(),SecondFragment())// 定义Tab标题private val fragmentTitles = listOf("首页","设置")override fun getItemCount(): Int = fragments.sizeoverride fun createFragment(position: Int): Fragment = fragments[position]// 提供Tab标题的方法fun getPageTitle(position: Int): String = fragmentTitles[position]
    }
    

    代码解释:

    • fragments:一个包含所有要显示的Fragment的列表。
    • fragmentTitles:每个Fragment对应的Tab标题。
    • getItemCount():返回Fragment的数量。
    • createFragment(position: Int):根据位置返回相应的Fragment实例。
    • getPageTitle(position: Int):返回Tab的标题,用于TabLayout显示。

步骤6:整合ViewPager2与TabLayout

现在,我们需要在主Activity中将ViewPager2TabLayout进行关联,使得用户可以通过点击Tab切换不同的Fragment。

  1. 打开 MainActivity.kt,修改为以下内容:

    package com.example.fragmenttabexampleimport androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import com.google.android.material.tabs.TabLayout
    import androidx.viewpager2.widget.ViewPager2
    import com.google.android.material.tabs.TabLayoutMediatorclass MainActivity : AppCompatActivity() {private lateinit var tabLayout: TabLayoutprivate lateinit var viewPager: ViewPager2private lateinit var pagerAdapter: MyFragmentStateAdapteroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 设置Activity的布局setContentView(R.layout.activity_main)// 初始化ViewPager2和TabLayoutviewPager = findViewById(R.id.view_pager)tabLayout = findViewById(R.id.tab_layout)// 创建适配器并设置给ViewPager2pagerAdapter = MyFragmentStateAdapter(this)viewPager.adapter = pagerAdapter// 将TabLayout和ViewPager2关联起来TabLayoutMediator(tabLayout, viewPager) { tab, position ->tab.text = pagerAdapter.getPageTitle(position)}.attach()}
    }
    

    代码解释:

    • 初始化视图组件:通过 findViewById 获取布局中的 ViewPager2TabLayout
    • 设置适配器:创建 MyFragmentStateAdapter 的实例,并将其设置给 ViewPager2
    • 关联TabLayout和ViewPager2:使用 TabLayoutMediatorTabLayoutViewPager2 关联起来,并为每个Tab设置标题。

步骤7:运行和测试应用

  1. 连接设备或启动模拟器
  2. 点击 “Run” 按钮,编译并运行应用。
  3. 你将看到一个带有两个标签(“首页” 和 “设置”)的界面,点击不同的标签会切换显示对应的Fragment内容。

预期效果:

  • 首页 Tab:显示带有红色背景和 “这是第一个Fragment” 文本的界面。

  • 设置 Tab:显示带有绿色背景和 “这是第二个Fragment” 文本的界面。

代码汇总

为了方便理解,以下是关键代码的汇总。

4.1 fragment_first.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFCDD2"android:padding="16dp"><TextViewandroid:id="@+id/tv_first"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是第一个Fragment"android:textSize="18sp"android:layout_gravity="center" />
</FrameLayout>
4.2 FirstFragment.kt
package com.example.fragmenttabexampleimport android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroupclass FirstFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_first, container, false)}
}
4.3 fragment_second.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#C8E6C9"android:padding="16dp"><TextViewandroid:id="@+id/tv_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是第二个Fragment"android:textSize="18sp"android:layout_gravity="center" />
</FrameLayout>
4.4 SecondFragment.kt
package com.example.fragmenttabexampleimport android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroupclass SecondFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_second, container, false)}
}
4.5 MyFragmentStateAdapter.kt
package com.example.fragmenttabexampleimport androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapterclass MyFragmentStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {// 定义要显示的Fragment列表private val fragments = listOf(FirstFragment(),SecondFragment())// 定义Tab标题private val fragmentTitles = listOf("首页","设置")override fun getItemCount(): Int = fragments.sizeoverride fun createFragment(position: Int): Fragment = fragments[position]// 提供Tab标题的方法fun getPageTitle(position: Int): String = fragmentTitles[position]
}
4.6 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"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><com.google.android.material.tabs.TabLayoutandroid:id="@+id/tab_layout"android:layout_width="match_parent"android:layout_height="wrap_content"app:tabIndicatorColor="@color/teal_700"app:tabSelectedTextColor="@color/teal_700"app:tabTextColor="@color/black" /><androidx.viewpager2.widget.ViewPager2android:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>
4.7 MainActivity.kt
package com.example.fragmenttabexampleimport androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediatorclass MainActivity : AppCompatActivity() {private lateinit var tabLayout: TabLayoutprivate lateinit var viewPager: ViewPager2private lateinit var pagerAdapter: MyFragmentStateAdapteroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 设置Activity的布局setContentView(R.layout.activity_main)// 初始化ViewPager2和TabLayoutviewPager = findViewById(R.id.view_pager)tabLayout = findViewById(R.id.tab_layout)// 创建适配器并设置给ViewPager2pagerAdapter = MyFragmentStateAdapter(this)viewPager.adapter = pagerAdapter// 将TabLayout和ViewPager2关联起来TabLayoutMediator(tabLayout, viewPager) { tab, position ->tab.text = pagerAdapter.getPageTitle(position)}.attach()}
}

4.8 运行和测试

  1. 连接设备或启动模拟器
  2. 点击 “Run” 按钮,编译并运行应用。
  3. 你将看到一个带有两个标签(“首页” 和 “设置”)的界面,点击不同的标签会切换显示对应的Fragment内容。

预期效果:

  • 首页 Tab:显示带有红色背景和 “这是第一个Fragment” 文本的界面。
  • 设置 Tab:显示带有绿色背景和 “这是第二个Fragment” 文本的界面。

5. 总结

今天我们深入学习了**Fragments(碎片)**的概念、生命周期以及在Activity中的使用方法。通过详细的理论讲解和具体的代码示例,你已经掌握了如何在应用中灵活运用Fragments来构建模块化和可复用的用户界面。

关键要点回顾:

  • Fragments的可重用性和模块化设计:提升应用的灵活性和可维护性。
  • Fragment的生命周期管理:了解并正确管理Fragments的生命周期,避免内存泄漏和性能问题。
  • 静态与动态添加Fragments:掌握在Activity中通过XML布局和代码两种方式添加Fragments,适应不同的开发需求。
  • ViewPager2与TabLayout的结合使用:实现流畅的多页面切换体验,并通过适配器管理Fragments与ViewPager2的关联。
  • 最佳实践:通过使用标签管理、回退栈、避免内存泄漏等方法,确保Fragments的高效和安全使用。

接下来的学习建议:

  • 探索高级Fragment用法:如嵌套Fragments、ViewPager与Fragments的结合使用等。
  • 结合ViewModel与LiveData:进一步优化Fragments的数据管理和生命周期同步。
  • 学习Navigation Component:了解Android Jetpack的Navigation组件,简化Fragments之间的导航和传参。
  • 优化用户体验:通过动画和过渡效果,提升Fragments切换的用户体验。

继续保持学习的热情,通过不断的实践和项目开发,你将成为一名熟练的Android开发者。祝你在Android开发的道路上取得更大的进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/465808.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Golang】validator库的使用

package mainimport ("fmt""github.com/go-playground/validator" )// MyStruct .. validate:"is-awesome"是一个结构体标签&#xff0c;它告诉验证器使用名为is-awesome的验证规则来验证String字段。 type MyStruct struct {String string vali…

Linux(CentOS)安装 MySQL

CentOS版本&#xff1a;CentOS 7 MySQL版本&#xff1a;MySQL Community Server 8.4.3 LTS 1、下载 MySQL 打开MySQL官网&#xff1a;https://www.mysql.com/ 直接下载网址&#xff1a;https://dev.mysql.com/downloads/mysql/ 其他版本 2、上传 MySQL 文件到 CentOS 使用F…

Pytorch实现transformer语言模型

转载自&#xff1a;| 03_language_model/02_Transformer语言模型.ipynb | 从头训练Transformer语言模型 |Open In Colab | Transformer语言模型 本节训练一个 sequence-to-sequence 模型&#xff0c;使用pytorch的 nn.Transformer <https://pytorch.org/docs/master/nn.ht…

<Project-20 YT-DLP> 给视频网站下载工具 yt-dlp/yt-dlp 加个页面 python web

介绍 yt-dlp Github 项目&#xff1a;https://github.com/yt-dlp/yt-dlp A feature-rich command-line audio/video downloader 一个功能丰富的视频与音频命令行下载器 原因与功能 之前我用的 cobalt 因为它不再提供Client Web功能&#xff0c;只能去它的官网使用。 翻 redd…

Sqli-Labs

目录 解题思路 题目设计原理 总结 解题思路 什么&#xff1f;sqli-labs&#xff1f;让我看看。还真是。想起了当初刚学被支配的恐惧。 悄咪咪点开第一关看看能不能秒了。测试闭合老样子&#xff0c;单引号闭合&#xff0c;双引号等都成功。这里 and 11 和 # 都不能通过检测&…

【基于Zynq FPGA对雷龙SD NAND的测试】

一、SD NAND 特征 1.1 SD 卡简介 雷龙的 SD NAND 有很多型号&#xff0c;在测试中使用的是 CSNP4GCR01-AMW 与 CSNP32GCR01-AOW。芯片是基于 NAND FLASH 和 SD 控制器实现的 SD 卡。具有强大的坏块管理和纠错功能&#xff0c;并且在意外掉电的情况下同样能保证数据的安全。 …

【NOIP提高组】引水入城

【NOIP提高组】引水入城 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 在一个遥远的国度&#xff0c;一侧是风景秀美的湖泊&#xff0c;另一侧则是漫无边际的沙漠。该国的行政 区划十分特殊&#xff0c;刚好构成一个N行M列的矩形&#xff…

鸿蒙开发:arkts 如何读取json数据

为了支持ArkTS语言的开发&#xff0c;华为提供了完善的工具链&#xff0c;包括代码编辑器、编译器、调试器、测试工具等。开发者可以使用这些工具进行ArkTS应用的开发、调试和测试。同时&#xff0c;华为还提供了DevEco Studio这一一站式的开发平台&#xff0c;为运行在Harmony…

OpenCV视觉分析之目标跟踪(11)计算两个图像之间的最佳变换矩阵函数findTransformECC的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 根据 ECC 标准 78找到两幅图像之间的几何变换&#xff08;warp&#xff09;。 该函数根据 ECC 标准 ([78]) 估计最优变换&#xff08;warpMatri…

【2024最新版Kotlin教程】Kotlin第一行代码系列第五课-类继承,抽象类,接口

【2024最新版Kotlin教程】Kotlin第一行代码系列第五课-类继承&#xff0c;抽象类&#xff0c;接口 为什么要有继承呢&#xff0c;现实中也是有继承的&#xff0c;对吧&#xff0c;你继承你爸的遗产&#xff0c;比如你爸建好了一个房子&#xff0c;儿子继承爸&#xff0c;就得了…

iOS用rime且导入自制输入方案

iPhone 16 的 cantonese 只能打传统汉字&#xff0c;没有繁简转换&#xff0c;m d sh d。考虑用「仓」输入法 [1] 使用 Rime 打字&#xff0c;且希望导入自制方案 [2]。 仓输入法有几种导入方案的方法&#xff0c;见 [3]&#xff0c;此处记录 wifi 上传法。准备工作&#xff1…

基于Zynq FPGA的雷龙SD NAND存储芯片性能测试

文章目录 前言一、SD NAND特征1.1 SD卡简介1.2 SD卡Block图 二、SD卡样片三、Zynq测试平台搭建3.1 测试流程3.2 SOC搭建 四、软件搭建五、测试结果六、总结 前言 随着嵌入式系统和物联网设备的快速发展&#xff0c;高效可靠的存储解决方案变得越来越重要。雷龙发展推出的SD NA…

【动态规划 数学】2745. 构造最长的新字符串|1607

本文涉及知识点 C动态规划 数学 LeetCode2745. 构造最长的新字符串 给你三个整数 x &#xff0c;y 和 z 。 这三个整数表示你有 x 个 “AA” 字符串&#xff0c;y 个 “BB” 字符串&#xff0c;和 z 个 “AB” 字符串。你需要选择这些字符串中的部分字符串&#xff08;可以全…

【Linux驱动开发】timer库下的jiffies时间戳和延时驱动编写

【Linux驱动开发】timer库下的jiffies时间戳和延时驱动编写 gitee地址&#xff1a; https://gitee.com/Mike_Zhou_Admin/Linux_Driver_Timestamp_Driver/更新以gitee为准 文章目录 timer库时间戳函数延时函数驱动代码应用测试附录&#xff1a;嵌入式Linux驱动开发基本步骤开发…

了解云计算工作负载保护的重要性及必要性

云计算de小白 云计算技术的快速发展使数据和应用程序安全成为一种关键需求&#xff0c;而不仅仅是一种偏好。随着越来越多的客户公司将业务迁移到云端&#xff0c;保护他们的云工作负载&#xff08;指所有部署的应用程序和服务&#xff09;变得越来越重要。云工作负载保护&…

C语言 循环高级

时间&#xff1a;2024.11.6 一、学习内容 1、无限循环 无限循环&#xff1a;循环永远停不下来 注意点&#xff1a;无限循环因为永远停不下来&#xff0c;所以下面不能再写其他的代码了 2、break 跳转控制语句&#xff1a; 在循环的过程中&#xff0c;跳到其他语句上执行 #…

易语言模拟真人动态生成鼠标滑动路径

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

CSS学习之Grid网格布局基本概念、容器属性

网格布局 网格布局&#xff08;Grid&#xff09;是将网页划分成一个个网格单元&#xff0c;可任意组合不同的网格&#xff0c;轻松实现各种布局效果&#xff0c;也是目前CSS中最强大布局方案&#xff0c;比Flex更强大。 基本概念 容器和项目 当一个 HTML 元素将 display 属性…

聊一聊Elasticsearch的索引的分片分配机制

1、什么是分片分配 分片分配是由ES主节点将索引分片移动到ES集群中各个节点上的过程。 该过程尽量保证&#xff0c;同一个索引的分片尽量分配到更多的节点上&#xff0c;以此来达到读写索引的时候可以利用更多硬件资源的效果。 在分配过程当中&#xff0c;也不能将某个主分片…

springboot的增删改查商城小实践(b to c)

首先准备一张表&#xff0c;根据业务去设计表 订单编号是参与业务的&#xff0c;他那订单编号里面是有特殊意义的&#xff0c;比如说像什么一些年月日什么的&#xff0c;一些用户的ID都在那编号里面呢&#xff1f;不能拿这种东西当主件啊 根据数据量去决定数据类型 价格需要注意…