Android动态片段

2023-09-13 11:36:11

之前创建的片段都是静态的。一旦显示片段,片段的内容就不能改变了。尽管可以用一个新实例完全取代所显示的片段,但是并不能更新片段本身的内容。
之前已经创建过一个基础秒表应用,具体代码https://github.com/MADMAX110/Stopwatch。我们将这个应用增加到WorkoutDetailFragment,把它显示在训练项目的详细信息之下。

修改步骤

1、把StopwatchActivity转换为StopwatchFragment。
将其改为片段代码,还会在一个新的临时活动TempActivity中显示这个片段,以便检查它的具体工作。这里会暂时修改应用,使应用运行时启动TempActivity。
2、测试StopwatchFragment。
StopwatchActivity包含Start、Stop、Reset按钮。我们要检查将秒表代码放在一个片段中时这些按钮仍然能正常工作。
3、把StopwatchFragment增加到WorkoutDetailFragment。
让StopwatchFragment在一个新的临时活动TempActivity中工作。这样我们就能先确认StopwatchFragement能正常工作,然后再把它增加到WorkoutDetailFragment。

修改代码

回到之前创建的workout工程,在com.hfad.workout中创建一个新的活动TempActivity,布局名为activity_temp。
在这里插入图片描述
修改AndroidManifest.xml使得TempActivity为主活动:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Workout"
        tools:targetApi="31">
        <activity
            android:name=".TempActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".DetailActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
        </activity>
    </application>
</manifest>

我们要新增一个StopwatchFragment的秒表片段,鉴于片段和活动的生命周期有所不同,要对之前的秒表活动稍作修改使其成为片段。

package com.hfad.workout;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Locale;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;


public class StopwatchFragment extends Fragment {

    private int seconds = 0;//记录已经过去的秒数
    private boolean running;//秒表是否正常运行
    //记录onStop之前秒表是否在运行,这样就知道是否需要恢复运行
    private boolean wasRunning;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            seconds = savedInstanceState.getInt("seconds");
            running = savedInstanceState.getBoolean("running");
            //保存wasRunning变量的状态
            wasRunning = savedInstanceState.getBoolean("wasRunning");
        }
    }

    @Nullable
    @Override
    //片段改为在onCreateView()方法中设置片段的布局
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View layout = inflater.inflate(R.layout.fragment_stopwatch, container, false);
        runTimer(layout);
        return layout;
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
        savedInstanceState.putInt("seconds", seconds);
        savedInstanceState.putBoolean("running", running);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (wasRunning) {
            running = true;
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        wasRunning = running;
        running = false;
    }

    //启动秒表
    public void onClickStart(View view) {
        running = true;
    }

    //停止秒表
    public void onClickStop(View view) {
        running = false;
    }

    //单击reset按钮时会调用这个方法
    public void onClickReset(View view) {
        running = false;
        seconds = 0;
    }

    private void runTimer(View view) {
        //得到文本视图(片段不能为直接使用findViewById,使用view参数调用findViewById)
        final TextView timeView = (TextView) view.findViewById(R.id.time_view);
        //创建一个新地Handler
        final Handler handler = new Handler();
        //调用post()方法,传入一个新的Runnable。post()方法会立即运行代码
        handler.post(new Runnable() {
            public void run() {

                int hours = seconds / 3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds % 60;
                //设置显示格式
                String time = String.format(Locale.getDefault(), "%d:%02d%02d", hours, minutes, secs);
                //设置文本视图
                timeView.setText(time);
                if (running) {
                    ++seconds;
                }

                //在1000ms后再次提交并运行Runnable中的代码,会反复调用
                handler.postDelayed(this, 1000);
            }
        });
    }
}

StopwatchFragment仍然使用Stopwatch原来的布局:

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:orientation="vertical"
    android:padding="16dp"
    >
    <TextView
        android:id="@+id/time_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textAppearance="@android:style/TextAppearance.Large"
        android:textSize="56sp"
        />
    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:onClick="onClickStart"
        android:text="@string/start"
        />
    <Button
        android:id="@+id/stop_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="8dp"
        android:onClick="onClickStop"
        android:text="@string/stop"
        />
    <Button
        android:id="@+id/reset_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="8dp"
        android:onClick="onClickReset"
        android:text="@string/reset"
        />
</LinearLayout>

增加三个字符串资源:

    <string name="start">Start</string>
    <string name="stop">Stop</string>
    <string name="reset">Reset</string>

修改activity_temp.xml:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:name = "com.hfad.workout.StopwatchFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
</fragment>

在单击按钮时调用片段中的方法

此时尝试应用单击按钮时将会崩溃,其原因在于onClick调用的是活动中的方法,而不是片段中的方法。 Android不会调用片段中的方法,而只会调用父活动中的方法。如果在这个活动中无法找到相应的方法,应用就会崩溃。
如何在单击按钮时调用片段中的方法:
1、从片段布局中删除android:onClick的引用。
使用android:onCLick属性时,按钮就会调用活动中的方法,所以要从片段布局中将它们删除。
2、修改onClick方法签名(可选)
创建onClickStart、onClickStop和onCLickReset方法,声明它们是公共方法,并提供一个View参数。这样当用户单击一个按钮时就会调用这些方法。由于我们不在布局中使用android:onCLick属性,所以可以把这些方法设置为私有。
3、实现一个onCLickListener,将按钮绑定到片段中的方法。

完整的StopwatvhFragment:

package com.hfad.workout;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.Locale;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;


public class StopwatchFragment extends Fragment implements View.OnClickListener {

    private int seconds = 0;//记录已经过去的秒数
    private boolean running;//秒表是否正常运行
    //记录onStop之前秒表是否在运行,这样就知道是否需要恢复运行
    private boolean wasRunning;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            seconds = savedInstanceState.getInt("seconds");
            running = savedInstanceState.getBoolean("running");
            //保存wasRunning变量的状态
            wasRunning = savedInstanceState.getBoolean("wasRunning");
        }
    }

    @Nullable
    @Override
    //片段改为在onCreateView()方法中设置片段的布局
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View layout = inflater.inflate(R.layout.fragment_stopwatch, container, false);
        runTimer(layout);

        Button startButton = (Button)layout.findViewById(R.id.start_button);
        startButton.setOnClickListener(this);
        Button stopButton = (Button)layout.findViewById(R.id.stop_button);
        startButton.setOnClickListener(this);
        Button resetButton = (Button)layout.findViewById(R.id.reset_button);
        startButton.setOnClickListener(this);

        return layout;
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.start_button:
                onClickStart();
                break;
            case R.id.stop_button:
                onClickStop();
                break;
            case R.id.reset_button:
                onClickReset();
                break;
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
        savedInstanceState.putInt("seconds", seconds);
        savedInstanceState.putBoolean("running", running);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (wasRunning) {
            running = true;
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        wasRunning = running;
        running = false;
    }

    //启动秒表
    public void onClickStart() {
        running = true;
    }

    //停止秒表
    public void onClickStop() {
        running = false;
    }

    //单击reset按钮时会调用这个方法
    public void onClickReset() {
        running = false;
        seconds = 0;
    }

    private void runTimer(View view) {
        //得到文本视图(片段不能为直接使用findViewById,使用view参数调用findViewById)
        final TextView timeView = (TextView) view.findViewById(R.id.time_view);
        //创建一个新地Handler
        final Handler handler = new Handler();
        //调用post()方法,传入一个新的Runnable。post()方法会立即运行代码
        handler.post(new Runnable() {
            public void run() {

                int hours = seconds / 3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds % 60;
                //设置显示格式
                String time = String.format(Locale.getDefault(), "%d:%02d%02d", hours, minutes, secs);
                //设置文本视图
                timeView.setText(time);
                if (running) {
                    ++seconds;
                }

                //在1000ms后再次提交并运行Runnable中的代码,会反复调用
                handler.postDelayed(this, 1000);
            }
        });
    }
}

现在可以试试应用了。

更多推荐

SQL 语法速成手册

SQL语法速成手册一、基本概念数据库术语数据库(database)-保存有组织的数据的容器(通常是一个文件或一组文件)。数据表(table)-某种特定类型数据的结构化清单。模式(schema)-关于数据库和表的布局及特性的信息。模式定义了数据在表中如何存储,包含存储什么样的数据,数据如何分解,各部分信息如何命名等信息。

SCConv:用于特征冗余的空间和通道重构卷积

文章目录摘要1、简介2、方法2.1、SRU用于空间冗余2.2.CRU用于通道冗余2.3、复杂性分析3、实验3.1、实验设置3.3、基于CIFAR的图像分类3.4、基于ImageNet的图像分类3.5、物体检测4、结论摘要代码链接:https://github.com/cheng-haha/ScConv论文链接:http

关于阅读《重构的时机和方法》这本书所带来的启发

前言近期,我阅读了由克里斯蒂安·克劳森(ChristianClausen)写的、由郭涛翻译的《重构的时机和方法》这本书,读完这本书的内容你会发现《重构的时机和方法》是一本经典的软件开发书籍,它能够帮助软件开发人员提高代码质量和工作效率,作为程序员的我觉得这本书对我很有帮助,有了很多收获和感悟,接下来本文就来简单分享一下

Attention is all you need 论文笔记

该论文引入Transformer,主要核心是自注意力机制,自注意力(Self-Attention)机制是一种可以考虑输入序列中所有位置信息的机制。RNN介绍引入RNN为了更好的处理序列信息,比如我吃苹果,前后的输入之间是有联系的。如图:引入Transformer的原因解决长距离依赖的问题:传统的RNN存在梯度消失和梯度

Java8 Stream 数据流,大数据量下的性能效率

Stream是JavaSE8类库中新增的关键抽象,它被定义于java.util.stream(这个包里有若干流类型:Stream<T>代表对象引用流,此外还有一系列特化流,如IntStream,LongStream,DoubleStream等),Java8引入的的Stream主要用于取代部分Collection的操作,

MapReduce & YARN 的部署

1、部署说明HadoopHDFS分布式文件系统,我们会启动:NameNode进程作为管理节点DataNode进程作为工作节点SecondaryNamenode作为辅助同理,HadoopYARN分布式资源调度,会启动:ResourceManager进程作为管理节点NodeManager进程作为工作节点ProxyServe

Python 数据分析学习路线

Python数据分析学习路线第一阶段:Python语言基础第二阶段:数据采集和持久化第三阶段:数据分析第四阶段:数据挖掘与机器学习书籍介绍参与方式第一阶段:Python语言基础在学习数据分析之前,首先需要掌握Python语言的基础知识,包括语法、常用数据结构、函数以及面向对象编程等。同时,还需要熟悉Python的标准库

MySQL索引,事务及存储引擎

目录MySQL索引创建索引的依据:索引的类型普通索引唯一索引主键索引组合索引全文索引查看索引删除索引事务事务的ACID特性原子性一致性隔离性持久性隔离级别设置隔离级别事务管理操作自动提交事务存储引擎MyISAMInnoDB的区别MyISAM表支持3种不同的存储格式静态(固定长度)表动态表压缩表如何避免死锁MySQL索引

Windows 10怎么清理磁盘空间?

现在,电脑是我们日常生活中很普遍的的一个工具,但在平常使用电脑时,我们会经常遇到磁盘空间已满的问题。而很多计算机新手都不知道电脑怎么清理磁盘空间,本将已Win10系统为例,教您如何清理电脑磁盘空间。方法1.清理储存对于磁盘已满的问题,我们经常遇到的就是C盘已满。C盘也就系统盘,它存放着大量的系统文件,当您安装软件时若是

密码学概论

1.密码学的三大历史阶段:第一阶段古典密码学依赖设备,主要特点数据安全基于算法的保密,算法不公开,只要破译算法密文就会被破解,在1883年第一次提出加密算法应该基于算法公开不影响密文和秘钥的安全;衡量加密算法的安全是所需要话费的时间,一般电脑需要300年才能破译认为强度是安全的第二阶段计算机的出现进入电子密码学阶段,加

实时车辆行人多目标检测与跟踪系统(含UI界面,Python代码)

算法架构:目标检测:yolov5目标跟踪:OCSort其中,Yolov5带有详细的训练步骤,可以根据训练文档,训练自己的数据集,及其方便。另外后续目标检测会添加yolov7、yolox,目标跟踪会添加ByteTrack、deepsort等经典算法,代码主要部分添加了详细注释,方便自己学习。一、简介本文将详细介绍如何使用

热文推荐