IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Fragment|让界面更灵活 -> 正文阅读

[移动开发]Fragment|让界面更灵活

Fragment | 让界面更灵活

Fragment 从 Android 3.0 版本开始引入,它可以称为“迷你版”的 Activity,在 Android 开发中可以让我们的界面更加灵活。

Fragment 允许将界面划分为零散的区块,它可以让你的界面模块化,并且它可以重复使用。但是 Fragment 不能独立存在,它必须由 Activity 或 另一个 Fragment 托管,Fragment 的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构。

如何创建一个 Fragment

当我们想要创建一个 Fragment,我们只需要创建一个类并让这个类继承 Fragment,然后重写它的 onCreateView 方法加载它的布局即可。

class MyFirstFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_my_first, container, false)
    }
}

或者使用更简单的方法:

Kotlin:

class MyFirstFragment : Fragment(R.layout.fragment_my_first) {
    
}

Java:

class MyFirstFragment extends Fragment {
    public MyFirstFragment() {
        super(R.layout.fragment_my_first);
    }
}

这样就可以创建出一个 Fragment 了,你还可以在类中定义其它方法去处理事务。

添加 Fragment

Fragment 是不能单独存在的,必须依附于 Activity 或另一个 Fragment,下面介绍一下添加 Fragment 的两种方法。

静态添加

静态添加需要在宿主的布局中添加一个 fragment 标签并指定 android:name 为你要添加的 Fragment 的类名(包括类的包名)。

<fragment
        android:id="@+id/my_first_fragment"    //id 是必需的
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.cpw.learningapplication.MyFirstFragment"/>

这样这个 Fragment 就成功的添加进去了。

动态添加

动态添加即通过代码进行添加,而不是像静态添加那样写死,实现 Fragment 的动态添加需要以下五步:

  1. 创建待添加的 Fragment 的实例。

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

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

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

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

代码如下:

val myFirstFragment = MyFirstFragment() //第一步
val manager = supportFragmentManager //第二步
val transaction = manager.beginTransaction() //第三步
transaction.replace(R.id.my_first_fragment, myFirstFragment) //第四步
transaction.commit() //第五步

当 Activity 处于 STARTED 生命周期状态或更高的状态时,都可以通过上述方法添加( add( ))、替换( replace( ))或移除( remove( )) Fragment。

实现返回栈

通常情况下,我们向 Activity 中添加多个Fragment 后,按返回键会退出这个 Activity,那如果我们想实现按返回键返回到上一个 Fragment 的效果,我们该怎么做呢?

FragmentTransaction 中提供了一个 addToBackStack( ) 方法,用于将一个事务添加到返回栈中,我们可以将上面的代码改写为:

val myFirstFragment = MyFirstFragment()
val manager = supportFragmentManager        
val transaction = manager.beginTransaction()
transaction.replace(R.id.my_first_fragment, myFirstFragment)
transaction.addToBackStack(null) //参数接收一个名字用于描述返回栈,一般传null
transaction.commit()

Fragment 与 Activity 的交互

既然 Fragment 是依附于 Activity 存在的,那么 Activity 和 Fragment 的交互就是必需的,但是 Fragmemt 和 Activity 是两个独立的类,它们要怎么进行交互呢?

当我们使用动态添加的方法添加 Fragment 时,我们创建了添加的 Fragment 的实例,我们可以通过这个实例直接调用 Fragment 中的方法,那使用静态方法添加时怎么获取这个 Fragment 的实例呢?为了方便 Fragment 和 Activity 进行交互,FragmentMananger 提供了一个类似于 findViewById( ) 的方法,专门从布局文件中获取 Activity 的实例,代码如下:

val fragment = supportFragmentManager.findFragmentById(R.id.my_first_fragment) as MyFirstFragment

上面的方法可以让我们在 Activity 中轻松调用 Fragment 中的方法,那么我们在 Fragment 中又该如何去调用 Activity 的方法呢?

在 Fragment 中可以通过调用 getActivity( ) 方法得到和当前 Fragment 相关联的 Activity。

if (activity != null) {
    val mainActivity = activity as MainActivity
}

Fragment 的生命周期

Fragement 的生命周期与 Activity 极为相似,也会经历以下几种状态:

  • 运行状态

    当一个 Fragment 所关联的 Activity 正处于运行状态时,该 Fragment 也处于运行状态。

  • 暂停状态

    当一个 Activity 进入暂停状态时(由于另一个未占满屏幕的 Activity 被添加到了栈顶),与它相关联的 Fragment 就会进入暂停状态。

  • 停止状态

    当一个 Activity 进入停止状态时,与它相关联的 Fragment 就会进入停止状态,或者通过调用 FragmentTransaction 的 remove()、replace() 方法将 Fragment 从 Activity 中移除,但在事务提交之前调用了 addToBackStack() 方法,这时的 Fragment 也会进入停止状态。总的来说,进入停止状态的 Fragment 对用户来说是完全不可见的,有可能会被系统回收。

  • 销毁状态

    Fragment 总是依附于 Activity 而存在,因此当 Activity 被销毁时,与它相关联的 Fragment 就会进入销毁状态。或者通过调用 FragmentTransaction 的 remove()、replace() 方法将 Fragment 从 Activity 中移除,但在事务提交之前并没有调用 addToBackStack() 方法,这时的 Fragment 也会进入销毁状态。

Fragment 完整的生命周期如下图:

Fragment 的生命周期(来自Android开发者文档)

在 onCreate( ) 之前和 onDestroy( ) 之后分别会调用 onAttach( ) 和 onDetach( ) 方法,表示和 Activy 建立\解除关联。

Fragment 的数据传递

Activity 向 Fragment 传递数据是有很多种方法的,因为 Activity 拥有 Fragment 的实例,它可以调用 Fragment 中所有的公有函数来达到数据传递的目的。除此之外,在将 Fragment 添加到 Activity 之前,可以调用 Fragment 的 setArguments( ) 方法向 Fragment 传递数据,然后在 Fragment 中通过调用 getArguments( ) 解析数据,它们传递的是一个 Bundle 对象。一般推荐使用 arguments 来传递数据,这是因为当 Fragment 销毁重建时,使用 arguments 传递的数据不会丢失,而其他方法传递的数据在没有经过存储时是会丢失的

Fragment 向 Activity 传递数据通常使用接口,在 Fragment 中定义接口,然后在 Activity 中实现接口,从而 Fragment 在需要传出数据时可以调用接口方法将数据传出。

具体代码如下:

// 在Fragment中定义接口
interface dataListener{
  fun onDataReceive(data: String)
}


//在Activity中实现接口
class SecondActivity : AppCompatActivity(), MyFirstFragment.dataListener {
  
    override fun onDataReceive(data: String) {
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
    }
  
}


// 在 Fragment 中调用接口方法
fun setData(data: String) {
  if (activity != null) {
    val myDataListener = activity as dataListener
    myDataListener.onDataReceive(data)
  }
}

实践,用 Fragment 实现 Android 底部导航栏

在 Android 应用中,底部导航栏是随处可见的,例如我们经常使用的微信、QQ、支付宝等,都有底部导航栏的身影,实现底部导航栏的方式也有很多种,这里我使用 Fragment 和其它基础的 View 来实现一个简单的底部导航栏。

Activity:

package com.cpw.learningapplication

import android.os.Bundle
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent.ACTION_MOVE
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_second.*
import kotlin.math.abs

class SecondActivity : AppCompatActivity() {
    private lateinit var myFirstFragment: MyFirstFragment
    private lateinit var mySecondFragment: MySecondFragment
    private lateinit var myThirdFragment: MyThirdFragment
    private var currentFragmentIndex: Int = 0


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        myFirstFragment = MyFirstFragment()
        mySecondFragment = MySecondFragment()
        myThirdFragment = MyThirdFragment()
        selectButton(button_1)
        button_1.setOnClickListener {
            selectButton(button_1)
        }

        button_2.setOnClickListener {
            selectButton(button_2)
        }

        button_3.setOnClickListener {
            selectButton(button_3)
        }

    }

    private fun selectButton(button: LinearLayout) {
        val currentFragment = when (button.tag.toString()) {
            "button_2" -> {
                currentFragmentIndex = 2
                mySecondFragment
            }
            "button_3" -> {
                currentFragmentIndex = 3
                myThirdFragment
            }
            else -> {
                currentFragmentIndex = 1
                myFirstFragment
            }
        }
        button_1.isSelected = currentFragmentIndex == 1
        button_2.isSelected = currentFragmentIndex == 2
        button_3.isSelected = currentFragmentIndex == 3
        supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, currentFragment)
                .commit()
    }
}

三个 Fragment 除了背景颜色和文字不同,其他的都一样。效果如下:

在这里插入图片描述

我们甚至可以通过处理滑动事件来实现左滑和右滑切换页面的功能,只需向 Activity 添加以下方法:

private fun toLeft() {
        if (!isMove) {
            when (currentFragmentIndex) {
                1 -> selectButton(button_2)
                2 -> selectButton(button_3)
            }
            isMove = true
        }
    }

    private fun toRight() {
        if (!isMove) {
            when (currentFragmentIndex) {
                2 -> selectButton(button_1)
                3 -> selectButton(button_2)
            }
            isMove = true
        }
    }

    private var oldX = 0
    private var currentX = 0
    private var isMove = false

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {

        when(ev?.action) {
            ACTION_DOWN -> {
                isMove = false
                oldX = ev.x.toInt()
            }
            ACTION_MOVE -> {
                currentX = ev.x.toInt()
                var delta = currentX - oldX
                if (abs(delta) > 300 && delta > 0) {
                    toRight()
                } else if (abs(delta) > 300 && delta < 0) {
                    toLeft()
                }
            }
        }
        return super.dispatchTouchEvent(ev)
    }

需要注意的是,如果你的页面内含有其他可以滑动的部件,你可能需要处理滑动冲突。

滑动效果:

在这里插入图片描述

欢迎微信搜索关注公众号:Android没有烦恼

一起学习更多Android知识

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-20 15:14:29  更:2021-08-20 15:15:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:41:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码