Android动态片段

news/2024/6/19 3:17:02 标签: android

之前创建的片段都是静态的。一旦显示片段,片段的内容就不能改变了。尽管可以用一个新实例完全取代所显示的片段,但是并不能更新片段本身的内容。
之前已经创建过一个基础秒表应用,具体代码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);
            }
        });
    }
}

现在可以试试应用了。


http://www.niftyadmin.cn/n/5035571.html

相关文章

nVisual 模型创建

一、 什么是nVisual模型 nVisual的模型是物理设备经过美工绘制后建立在系统内的数字孪生体&#xff0c;模型库包含了节点图标、设备板卡模型、线缆模型和链路模型。在系统内增加某种类型的机柜或设备&#xff0c;都需要先增加该种类型的模型&#xff0c;才能根据模型生成实体…

C语言:字符指针

一、字符指针概念 字符指针是存放字符地址的指针 二、字符指针的两种用法 指向字符或指向字符串 指向字符&#xff1a; char ch w; char* p &ch; printf("%c\n", *p);//w指向字符串&#xff1a; char* p "abcdef"; printf("%s\n", p);//a…

java基础-并发编程-ThreadPoolExecutor源码学习

ThreadPoolExecutor源码 大纲 给线程池中添加任务 public void execute(Runnable command) {if (command null)throw new NullPointerException();int c ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c ctl.get();}if (isRunn…

如何解决 503 Service Temporarily Unavailable?

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

牛客月赛c(简单推理,以及对set的灵活运用)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 “O.o?” 集合中一开始拥有两个数字 a和b&#xff0c;如果 a与 b 相同&#xff0c;那么仅有一个数字。 小沙每次可以选择集合中的两个数&#xff08;可以相同&#xff09;,将他们的和放入集合…

【C++面向对象侯捷】1.C++编程简介

文章目录 视频来源&#xff1a;我的百度网盘

FPGA原理与结构(0)——目录与传送门

一、 简介 FPGA的设计和软件设计不同&#xff0c;我们所设计的RTL代码最终还是要落实到硬件底层来进行实例化&#xff0c;因此理解硬件底层的内容是很有意义的。 二、可编程逻辑块CLB 可配置逻辑块CLB&#xff08;Configurable Logic Block&#xff09;是xilinx系类FPGA的基本…

视频号:平均一场裂变7965人,电商增长玩法全揭秘!

我们整理了几何裂变平台上电商行业近3个月的视频号裂变活动&#xff0c;发现用的最多的玩法是粉丝裂变&#xff0c;其次是直播裂变。 平均一场视频号粉丝裂变活动涨粉7965人&#xff0c;裂变层级9级&#xff0c;裂变率872%&#xff0c;即1个老用户能带来8.72个新用户关注视频号…