【Android】碎片的初识

news2024/9/21 0:40:25

之前我们学习的是一个活动作为一个页面,有了平板之后,页面如果像手机一样设计就会浪费很多的空间,会有很多的空白区域,为了使屏幕充分利用,引入了碎片这样一个概念。

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

碎片的使用方式

碎片的静态创建

我们要在一个活动当中存放两个碎片,并让这两个碎片平分活动的空间,首先新建两个碎片布局

新建一个左碎片布局,当中放一个按钮:

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

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="buttonl"
        android:id="@+id/Buttonleft2"/>

</LinearLayout>

新建一个右碎片布局,将背景颜色换为粉色,并放置一个TextView用于显示一段文本,用来区分两块碎片:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#f0a1a8"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is right fragment"
        android:layout_gravity="center"
        android:id="@+id/right2"/>

</LinearLayout>

紧接着新建两个类:LeftFragment与RightFragment,并让其继承Fragment,此时我们需要导包,有不同的包供我们选择,此时应导入import androidx.fragment.app.Fragment;如果你导的是别的包,有可能会报错,甚至程序崩溃。接下来编写一下里面的代码吧:

LeftFragment中的代码:

import androidx.fragment.app.Fragment;

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

代码当中我们重写了onCreateView方法,是Fragment生命周期的一部分,用于加载Fragment的布局:

  • LayoutInflater inflater:用于将XML布局文件实例化为View对象。
  • ViewGroup containerFragment 将被添加到这个容器中。
  • Bundle savedInstanceState:如果 Fragment 之前被保存过状态,这个 Bundle 包含了之前保存的状态信息。

同样的方法去重写RightFragment类,基本上代码是相同的。

接下来就去修改承载这两个碎片活动的XML文件代码,由于使用的手机,所以换成了上下平分:

<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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".StaticFragment">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        tools:layout="@layout/left_fragment"
        android:name="com.example.fragmenttext1.LeftFragment" />

    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/right_fragment"
        tools:layout="@layout/right_fragment"
        android:name="com.example.fragmenttext1.RightFragment4" />

</LinearLayout>

此时两个碎片就被加载到了一个活动当中,看一看运行结果吧:

在这里插入图片描述

注意,在使用fragment标签时:

  1. 必须声明android:id或者android:tag属性,否则会有错

android:tag 是一个可以在 XML 布局文件中使用的属性,它为视图或组件提供了一个标签(tag),方便在代码中通过 findViewWithTag() 方法来查找和引用特定的视图。

  1. 与之前不同在于,之前我们可以直接看到视图的预览,而在fragment时,我们需要使用tools:layout="@layout/left_fragment"来看到预览的视图

动态添加碎片

再新建一个another_right_fragment布局文件,此时修改了文本内容与背景颜色:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#8076a3"
        android:gravity="center"
        android:text="This is other right fragment"
        tools:layout_editor_absoluteX="105dp"
        tools:layout_editor_absoluteY="50dp" />

</LinearLayout>

新建一个AnotherRightFragment作为右边的碎片,与之前的代码类似:

import androidx.fragment.app.Fragment;

public class AnotherRightFragment extends Fragment {
    @Override
    public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.another_right_fragment, container, false);
        return view;
    }
}

代码与之前都非常类似,接下来我们就看看如何动态地添加到活动当中,我们修改放碎片的活动的XML文件:

<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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".dong_fragment">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        tools:layout="@layout/activity_dong_fragment"
        android:name="com.example.fragmenttext1.LeftFragment" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/right_fragment"/>

</LinearLayout>

此时,我们将右面的碎片替换成了FrameLayout,下面再通过代码向里面添加内容,从而实现动态的添加碎片,接下来就看放碎片的活动的代码:

public class dong_fragment extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_dong_fragment);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        Button button = (Button) findViewById(R.id.Buttonleft2);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                replaceFragment(new AnotherRightFragment());
            }
        });
        replaceFragment(new RightFragment4());
    }

    private void replaceFragment (Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.right_fragment, fragment);
        transaction.commit();
    }
}

此时我们是对左边碎片的按钮注册了点击事件,一开始会调用replaceFragment方法将右侧添加为一开始的右侧碎片,与上面静态添加碎片的结果相同,但当我们按下Button按钮,就会触发按钮的点击事件,从而使碎片发生变化,变成另一个右边的碎片。

由上面的代码我们知道动态添加布局需要5步:

  • 创建待添加的碎片实例,即上面调用换碎片时所new的实例
  • 获取FragmentManager,在活动中可以直接通过调用getSupportFragmentManager()获取

FragmentManager 是 Android 开发中用于管理 Fragment 生命周期和事务的核心类。它负责将 Fragment 附加到 ActivityFragment,以及执行 Fragment 事务,比如添加、移除、替换和回退等操作。

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

FragmentTransaction 是 Android 开发中用于执行 Fragment 事务操作的类。它允许你在一个事务中执行多个操作,比如添加、移除、替换或隐藏 Fragment

  • 向容器内添加或替换碎片,一般通过replace()方法实现需要传入容器的id和待添加的碎片实例
  • 提交事务,调用commit()方法来完成

重新运行程序,一开始会调用replaceFragment方法将右侧碎片添加到下面,与上面静态添加碎片的结果是一样的,当我们按下Button按钮就会触发点击事件,从而使右侧碎片变化为另一个右侧碎片,效果如图所示:

在这里插入图片描述

在碎片中模拟返回栈

我们上面实现向活动中动态添加碎片,当我们按下按钮转换到另一个碎片的时候,再按下返回键,就会直接退出程序,但我们如果想按下Back键可以返回到上一个碎片,又要如何实现呢?

其实,在FragmentTransaction当中就提供了一个addToBackStack()方法,可以将事务添加到返回栈当中,只需要对上面replaceFragment()进行小小的修改:

private void replaceFragment (Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_fragment, fragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

在提交事务之前,我们调用了FragmentTransactionaddToBackStack()方法,它可以接收一个名字用于描述返回栈的状态,一般传入null即可。现在运行程序,点击按钮使其另一个右边碎片替换进活动当中,然后按下Back键,你会发现,程序并没有退出,而是回到了第一个界面,继续按下Back键,右边的碎片会消失,再次按下back键,程序才会退出。

碎片的生命周期

碎片的状态和回调

之前学习活动的时候了解到活动有四种状态:运行状态、暂停状态、停止状态、销毁状态。其实每个碎片的生命周期也会经历这几种状态,但在细微的方面有差别。

  1. 运行状态

当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态

  1. 暂停状态

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

  1. 停止状态

当一个活动进入停止状态时,与它相关联的碎片就会进入到停止状态,或者通过调用FragmentTransactionremove()replace()方法将碎片从活动中移除,但在事务提交之前调用addToBackStack()方法,此时的碎片会进入停止状态。进入停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收

  1. 销毁状态

碎片是依附于活动而存在的,因此当活动被销毁时,与它相关联的碎片就会进入到销毁状态,或者通过调用FragmentTransactionremove()replace()方法将碎片从活动中移除,但在事务提交之前没有调用addToBackStack()方法,此时的碎片也会进入销毁状态。

活动的回调方法大家都已经接触过了,接下来就看看碎片的回调方法。活动的回调方法,碎片中几乎都有,但碎片还提供了一些附加的回调方法:

  • onAttach():当碎片和活动建立关联时调用
  • onCreateView():为碎片创建视图(加载布局)时调用
  • onActivityCreated():确保与碎片相关联的活动一定已经创建完毕的时候调用
  • onDestroyView():当与碎片关联的视图被移除的时候调用
  • onDetach():当碎片与活动解除关联的时候调用

完整的碎片过程如图所示:

在这里插入图片描述

体验碎片的生命周期

仍然按照上面的代码进行修改,在右侧碎片的代码当中进行修改

public class RightFragment4 extends Fragment {
    public static final String ATG = "RightFragment";

    @Override
    public View onCreateView (LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
        Log.d(ATG, "onCreateView");
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }

    @Override
    public void onAttach (Context context) {
        super.onAttach(context);
        Log.d(ATG, "onAttach");
    }

    @Override
    public void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(ATG, "onCreate");
    }

    @Override
    public void onActivityCreated (Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(ATG, "onActivityCreated");
    }

    @Override
    public void onStart () {
        super.onStart();
        Log.d(ATG, "onStart");
    }

    @Override
    public void onResume () {
        super.onResume();
        Log.d(ATG, "onResume");
    }

    @Override
    public void onPause () {
        super.onPause();
        Log.d(ATG, "onPause");
    }

    @Override
    public void onStop () {
        super.onStop();
        Log.d(ATG, "onStop");
    }

    @Override
    public void onDestroyView () {
        super.onDestroyView();
        Log.d(ATG, "onDestroyView");
    }

    @Override
    public void onDestroy () {
        super.onDestroy();
        Log.d(ATG, "onDestroy");
    }

    @Override
    public void onDetach () {
        super.onDetach();
        Log.d(ATG, "onDetach");
    }
}

我们在每一个回调方法里面都加入了打印日志的方法,然后重新运行程序,此时的打印信息为:

在这里插入图片描述

依次执行这5种方法,然后点击左边碎片的按钮跳转到另一个碎片,此时的打印信息:

在这里插入图片描述

由于在上面的代码当中我们加入了addToBackStack()方法,则会将现在的碎片进入停止状态,执行onStart()onResume()方法,但如果在替换的时候没有调用这个方法,此时这个碎片处于销毁状态,则会执行onDestroy()onDetach()方法,再次按下Back按钮回到最初的屏幕:

在这里插入图片描述

与第一次屏幕的创建少了两个回调方法:onAttach()onCreate(),再次按下Back键:

在这里插入图片描述

这下你对碎片的生命周期就有了更深刻的理解。

动态加载布局的技巧

使用限定符

在使用平板电脑的时候我们会看到双页模式,左侧含有一个列表,右侧显示内容,而在手机当中大部分都只能显示一页的内容,怎样使程序自己判断使用双页模式还是单页模式呢?此时就需要借助限定符(Qualifiers)来实现了

新建一个活动,在对应的XML文件(activity_qualifiers.xml)里面进行修改

<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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".xianDing">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/left_fragment"
        tools:layout="@layout/left_fragment"
        android:name="com.example.fragmenttext1.LeftFragment" />

</LinearLayout>

只放置一个左侧布局充满整个父布局,在res目录下新建layout-large文件夹

在这里插入图片描述

在这里插入图片描述

先将type转换为Layout,再选取Size选项,按到下一页,选取large即可,此时你需要对File进行命名,这时的名字就是第一个布局文件的名字。

在这个文件夹下新建一个名字一样的布局,代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    xmlns:tools="http://schemas.android.com/tools">

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        tools:layout="@layout/left_fragment"
        android:name="com.example.fragmenttext1.LeftFragment" />

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:id="@+id/right_fragment"
        tools:layout="@layout/right_fragment"
        android:name="com.example.fragmenttext1.RightFragment4" />
</LinearLayout>

前一个布局里面只包含了一个碎片,即单页模式,另一个布局包含了两个碎片,即双页模式,其中large就是一个限定符,那些屏幕被认为是large的设备就会自动加载下面的这个布局。此时在手机上运行就会看到加载的是第一个布局即只有一个碎片,当用平板运行加载的就是下面两个碎片的布局。

Android中常见的限定符:

在这里插入图片描述

在这里插入图片描述

使用最小的限定符

在上面我们学习了使用large限定符解决了双页单页问题,但large到底是多大?我们希望更加灵活地为不同的设备加载布局,不论是否被系统认为是large,这时就可以使用最小的限定符(Smallest-widthQualifier)

此时的操作步骤与上面类似,当我们设置最小宽度为600dp时,当屏幕的宽度大于600dp,则会加载现在的这个布局,当屏幕宽度小于600dp时,则会加载默认的布局

在这里插入图片描述

到这里就结束了!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pikachu之sql lnjet 字符型注入

先测试一下闭合 注释符号&#xff1a;-- 注释符号可以忽略其后的内容&#xff0c;使得后续的原始查询内容不会影响我们注入的SQL代码。 条件测试&#xff1a;通过and 11和and 12分别测试真假条件&#xff0c;可以判断输入是否成功闭合&#xff0c;并且可以检测注入是否成功。 …

构造+位运算,CF 1901C - Add, Divide and Floor

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1901C - Add, Divide and Floor 二、解题报告 1、思路分析 我们假设将原数组排序&#xff0c;那么每次操作不会改变数组单调性 当 最大值 调整等于 最小值时 所有数都相等&#xff0c;因为单调性不变&…

VS2022下安装和配置OpenCV环境参数+QT开发环境搭建(1)

1.工具准备 VS2022,OpenCV4.5.5版本&#xff0c;QT5.12.12 VisualStudio最新版直接官网下载&#xff0c;根据需要进行下载&#xff0c;我下载的免费社区版本。日常开发完全够用。 qt官网下载5.12版本。 OpenCVReleases - OpenCV 选择Windows版本下载并解压到本地磁盘&#xff0…

操作系统——笔记(1)

操作系统是管理计算机硬件资源&#xff0c;控制其他程序运行并为用户提供交互操作界面的系统软件的集合&#xff0c;控制和管理着整个计算机系统的硬件和软件资源&#xff0c;是最基本的系统软件。 常见的操作系统&#xff1a;ios、windows、Linux。 计算机系统的结构层次&am…

【SpringCloud】微服务远程调用OpenFeign

工作原理流程图 上代码 common中添加依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency><groupId>org.spri…

Android APP 音视频(01)MediaCodec解码H264码流

说明&#xff1a; 此MediaCodec解码H264实操主要针对Android12.0系统。通过读取sd卡上的H264码流Me获取视频数据&#xff0c;将数据通过mediacodec解码输出到surfaceview上。 1 H264码流和MediaCodec解码简介 1.1 H264码流简介 H.264&#xff0c;也被称为MPEG-4 AVC&#xff…

用51单片机或者stm32能否开发机器人呢?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;能的。但是由于单片机和st…

记录安装android studio踩的坑 win7系统

最近在一台新电脑上安装android studio,报了很多错误&#xff0c;也是费了大劲才解决&#xff0c;发出来大家一起避免一些问题&#xff0c;找到解决方法。 安装时一定要先安装jdk&#xff0c;cmd命令行用java -version查当前的版本&#xff0c;没有的话&#xff0c;先安装jdk,g…

ARP欺骗——华为ensp

首先&#xff0c;搭建好网络拓扑。网络设备包含客户端Client1和服务端Server1&#xff0c;交换机 以及 云。 图中的 Client和Server 配置IP地址&#xff0c;要和 vm8 在相同的网段。故设置客户端ip为192.168.11.10 &#xff0c;服务端ip为&#xff1a;192.168.11.20&#xff0…

MySQL补充性文件

数据库专属单词 authentication #身份验证 delimiter #分隔符 character #字符集 collate #整理。 指定字符集的排序规则 unicode #统一码 flush #刷新 privileges #特权 string #串 set #设置 use #使用 zerofill #修饰符。0可以填补输出的值 unsigned #修饰符。无符…

STM32--HAL库--定时器篇

一&#xff1a;如何配置定时器 打开对应工程串口配置好的工程&#xff08;上一篇博客&#xff09;做如下配置&#xff1a; 定时器的中断溢出时间计算公式是&#xff1a; 由图得T100*1000/100MHz 注&#xff1a;100MHz100000000 所以溢出时间等于1ms 关于上图4的自动重装…

Robot Operating System——初探动态配置Parameters

大纲 同步模式Node内使用declare_parameter方法声明Parameters创建Parameter同步访问客户端跨Node修改Parameters跨Node查询Parameters完整代码运行结果 异步模式创建Node&#xff0c;设置Parameters创建Parameter异步访问客户端异步设置&#xff0c;同步等待异步查询&#xff…

VMware三种网络模式---巨细

文章目录 目录 ‘一.网络模式概述 二.桥接模式 二.NAT模式 三.仅主机模式 四.案例演示 防火墙配置&#xff1a; 虚拟电脑配置 前言 本文主要介绍VMware的三种网络模式 ‘一.网络模式概述 VMware中分为三种网络模式&#xff1a; 桥接模式&#xff1a;默认与宿主机VMnet0绑…

CSP-J模拟赛day1——解析+答案

题目传送门 yjq的吉祥数 题解 送分题&#xff0c;暴力枚举即可 Code #include<bits/stdc.h> using namespace std;int l,r; int num1,tmp0,q[10000],a[10000]; int k (int x){for (int j1;j<tmp;j){if (xq[j])return 0;}return 1; } int main(){while (num<100…

php连接sql server

php连接sqlserver有三种方式 一&#xff1a;odbc连接&#xff0c;废话不多说直接上代码,封装了一个单例 <?php /*** odbcServer.php* Author: Erekys*/namespace App\Model; class odbcServer{public static $server;public static $username;public static $password;pu…

dora-rs学习之Rust 和机器人

最近在研究一个网红机器人框架 dora-rs&#xff0c;也看到一些具身智能相关&#xff0c;做机器人遥操作与数据采集及可视化系统的公司使用rust来开发&#xff0c;这里探讨一下dora-rs和rust给机器人带来什么&#xff0c;引述官方的描述&#xff1a;Hello from dora-rs | dora-r…

svelte - 5. 动画

svelte/motion模块导出两个函数&#xff1a; tweened 和 spring。 用于创建writable&#xff08;可写&#xff09;store&#xff0c;其值会在set 和 update之后更新&#xff0c;而不是立即更新。 &#xff08;人话&#xff1a;用 svelte 提供的tweened 和 spring可以达成流程变…

数学建模学习(1)遗传算法

一、简介 遗传算法&#xff08;Genetic Algorithm, GA&#xff09;是一种用于解决优化和搜索问题的进化算法。它基于自然选择和遗传学原理&#xff0c;通过模拟生物进化过程来寻找最优解。 以下是遗传算法的主要步骤和概念&#xff1a; 初始化种群&#xff08;Initialization&a…

react中嵌套路由以及默认显示二级路由

1.安装插件 npm install react-router-dom 2.新建文件及页面 在scr/page下新建layout、about、home文件夹&#xff0c;分别在对应的文件夹下新建入口文件index.js&#xff1b;src下新建router文件夹&#xff0c;该文件夹下新建入口文件index.js 3.配置路由 如何配置路由&am…

昇思学习打卡-25-自然语言处理/RNN实现情感分类

文章目录 数据下载加载预训练词向量数据集预处理模型构建损失函数与优化器训练逻辑评估指标和逻辑模型训练与保存模型加载与测试自定义输入测试测试 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&…