活动介绍
file-type

使用Jetpack Compose实现倒数计时器的Kotlin项目

ZIP文件

下载需积分: 9 | 1.42MB | 更新于2025-09-03 | 167 浏览量 | 0 下载量 举报 收藏
download 立即下载
根据提供的文件信息,我们可以提取以下知识点: ### 标题知识点:Countdown(倒数) - **定义与功能**:Countdown(倒数)是一个功能,允许用户设置一个起始时间点,然后倒数直至时间耗尽。通常用于计时,比如烹饪、赛事倒计时、活动准备等场景。 - **界面元素**:用户可以见到剩余时间的显示,包括数字和圆形指示器,这些是用户友好的可视元素,帮助用户直观了解剩余时间。 - **控制功能**:用户可以根据需要随时停止倒计时,这提供了灵活性和控制性。 ### 描述知识点:选择开始倒数的时间与动机情境 - **操作流程**:描述中提到用户可以选择开始倒数的时间,这暗示了倒计时功能需要用户进行初始设置,包括设定倒计时的总时长。 - **技术应用背景**:这是一个“厨房水槽项目”,即一个全面、包含多种功能的项目,用于通过Jetpack Compose学习和尝试状态管理和动画。 - **状态管理与动画**:在Jetpack Compose中使用倒数功能,可以让开发者学习如何处理组件的状态变化,以及如何在用户交互过程中添加动画效果,增强用户体验。 ### 标签知识点:Kotlin - **编程语言**:Kotlin是一种运行在Java虚拟机上的静态类型编程语言,被设计为可以与Java代码互操作。 - **与Android开发的关联**:Kotlin是Google推荐的Android应用程序开发语言之一,Jetpack Compose也支持使用Kotlin进行构建用户界面。 - **倒数功能实现**:在这个倒数项目中,开发者可能会使用Kotlin提供的各种功能,如协程(用于处理异步任务和长时间运行的操作)、扩展函数、以及高阶函数等,来实现倒数逻辑和用户界面的动态交互。 ### 文件信息知识点:Copyright与License(版权与许可) - **版权信息**:显示“Copyright 2020 The Android Open Source Project”,表明此文件或项目是由Android开源项目在2020年发布的。 - **许可协议**:文件遵从Apache License Version 2.0的许可,这是一种宽松的开源许可证,允许开发者在遵守许可证规定的情况下自由使用、修改和分发软件,包括商业用途,只要保留版权声明和许可声明。 ### 文件压缩包知识点:Countdown-main - **项目结构**:文件夹名称“Countdown-main”表明这是倒数功能项目的主目录。 - **项目组成**:通常在主目录下,开发者可以找到源代码文件(.kt)、资源文件(如图片、布局文件)、以及可能的配置文件(如build.gradle)。 - **项目构建**:对于Kotlin开发的Android项目,项目文件夹将通过Android Studio或命令行工具构建和运行。 总结来看,这些信息描绘了一个使用Jetpack Compose和Kotlin开发的倒数计时器项目。该项目不仅提供了基础的倒数功能,还被用作学习和实践状态管理和动画效果的工具。同时,它遵循了Apache 2.0的开源许可,鼓励开放源代码社区的共享和协作。

相关推荐

filetype

注意这才是任务主流程,我所有定义的方法都需要在这里被调用: @Override protected Integer doInBackground(Void... voids) { try { // 阶段1: 备份数据 currentPhase.set(PHASE_BACKUP); notifyProgress("开始备份数据..."); // 创建备份线程池(T1) ExecutorService backupExecutor = Executors.newSingleThreadExecutor(); Future<Boolean> backupFuture = backupExecutor.submit(() -> performBackup()); // 阶段2a: 拷贝 + 解密验签(TS2a) ExecutorService serialCopyExecutor = Executors.newSingleThreadExecutor(); ExecutorService parallelVerifyExecutor = Executors.newFixedThreadPool(3); // 阶段2b: 解压(T2b) ExecutorService serialExtractExecutor = Executors.newSingleThreadExecutor(); // 等待备份完成 backupLatch.await(); // 等待 T1 完成 boolean backupSuccess = backupFuture.get(); if (!backupSuccess || isCancelled()) { return ERROR_UNEXPECTED; } // 并行处理拷贝 + 解密验签(TS2a) processUpdatePackagesInParallel(updatePackages, serialCopyExecutor, parallelVerifyExecutor); // 等待 TS2a 完成 verifyLatch.await(); // 等待所有解密验签完成 // 阶段2b: 解压(T2b) for (File packageFile : updatePackages) { if (isCancelled()) break; serialExtractExecutor.submit(new SerialExtractTask(packageFile)); } // 关闭解压线程池 serialExtractExecutor.shutdown(); if (!serialExtractExecutor.awaitTermination(1, TimeUnit.HOURS)) { return ERROR_UNEXPECTED; } // 校验 if (!mapDataController.checkInstallationStatus()) { return ERROR_FILE_VERIFY_FAILED; } return SUCCESS; } catch (Exception e) { return ERROR_UNEXPECTED; } } 而我的方法包含这些: private boolean performBackup() { try { File backupFile = new File(cacheDir, "backup.zip"); if (backupFile.exists() && !backupFile.delete()) { Log.w(TAG, "删除旧备份文件失败"); } backupSize = estimateBackupSize(); Log.i(TAG, "需要备份的数据大小: [" + formatSize(backupSize) + "]"); try { boolean result = FileUtilszwx.compressDirectoryWithProgress( storageDir, backupFile, (copied, total) -> { currentCopiedBytes.set(copied); notifyProgress("备份数据: " + USBOfflineUpdater.formatSize(copied) + "/" + USBOfflineUpdater.formatSize(total)); if (isCancelled) { throw new RuntimeException(new InterruptedException("用户取消备份")); } } ); return result; } catch (RuntimeException e) { if (e.getCause() instanceof InterruptedException) { Log.d(TAG, "备份任务被用户取消"); return false; } else { Log.e(TAG, "未知异常", e); throw e; } } } catch (Exception e) { Log.e(TAG, "备份失败", e); return false; } finally { backupLatch.countDown(); // 无论成功与否,触发备份完成信号 } } private void updateProgress(String format, String packageName, long processed, long total) { String message = String.format(format, packageName, formatSize(processed), formatSize(total)); mainHandler.post(() -> notifyProgress(message)); } private void processUpdatePackagesInParallel( File[] packages, ExecutorService serialCopyExecutor, ExecutorService parallelVerifyExecutor) { if (packages == null || packages.length == 0) { notifyListener(SUCCESS, "无升级包"); return; } // 初始化每个包的进度信息 for (File packageFile : packages) { packageTotalSizeMap.put(packageFile.getName(), packageFile.length()); packageProgressMap.put(packageFile.getName(), 0.0); packageStageMap.put(packageFile.getName(), STAGE_COPY); } allPackagesLatch = new CountDownLatch(packages.length); verifyLatch = new CountDownLatch(packages.length); // 并行提交拷贝任务(使用线程池) for (File packageFile : packages) { if (isCancelled) break; File destFile = new File(cacheDir, packageFile.getName()); // 提交到线程池并行执行 serialCopyExecutor.submit(() -> { try { // 执行拷贝逻辑(示例) copyPackageFile(packageFile, destFile); // 提交解密验签任务到线程池 parallelVerifyExecutor.submit(new PostCopyProcessingTask(packageFile, destFile, allPackagesLatch, verifyLatch)); } catch (Exception e) { Log.e(TAG, "拷贝任务失败", e); allPackagesLatch.countDown(); verifyLatch.countDown(); } }); } // 关闭线程池后触发 verifyLatch serialCopyExecutor.shutdown(); parallelVerifyExecutor.shutdown(); new Thread(() -> { try { serialCopyExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); parallelVerifyExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); verifyLatch.countDown(); } catch (InterruptedException e) { Log.e(TAG, "线程池关闭异常", e); } }).start(); } // 拷贝逻辑方法 private void copyPackageFile(File srcFile, File destFile) throws IOException { // 实现文件拷贝逻辑 Files.copy(srcFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } /** * 更新包的进度,并限制最大为 MAX_PACKAGE_PROGRESS */ private void updatePackageProgress(String packageName, long processed, long total, int stage) { double stageRatio = (double) processed / total; double progress = 0.0; switch (stage) { case STAGE_COPY: progress = MAX_PACKAGE_PROGRESS / 3 * stageRatio; break; case STAGE_VERIFY: progress = MAX_PACKAGE_PROGRESS / 3 + MAX_PACKAGE_PROGRESS / 3 * stageRatio; break; case STAGE_EXTRACT_ONLY: progress = MAX_PACKAGE_PROGRESS / 3 * 2 + MAX_PACKAGE_PROGRESS / 3 * stageRatio; break; } packageProgressMap.put(packageName, Math.min(progress, MAX_PACKAGE_PROGRESS)); } /** * 串行拷贝任务 */ private class SerialCopyTask implements Runnable { private final File packageFile; private final ExecutorService parallelExecutor; private final File destFile; // 添加 destFile public SerialCopyTask(File packageFile, File destFile, ExecutorService parallelExecutor) { this.packageFile = packageFile; this.destFile = destFile; this.parallelExecutor = parallelExecutor; } @Override public void run() { if (isCancelled) { allPackagesLatch.countDown(); return; } String packageName = packageFile.getName(); // 阶段2: 解密验签 try { boolean verifyResult = dataVerification(destFile.getAbsolutePath(), new DecryptUtil.ProgressCallback() { @Override public void onProgress(long processed, long total) { packageStageMap.put(packageName, STAGE_VERIFY); updatePackageProgress(packageName, processed, total, STAGE_VERIFY); updateProgress("解密验签 %s: %s/%s", packageName, processed, total); } }); if (!verifyResult || isCancelled) { updateSuccess = false; allPackagesLatch.countDown(); return; } } catch (Exception e) { Log.e(TAG, "解密验签失败", e); updateSuccess = false; allPackagesLatch.countDown(); return; } // 解密验签完成,触发 verifyLatch verifyLatch.countDown(); allPackagesLatch.countDown(); } /** * 并行处理任务(解密验签、解压) */ private class PostCopyProcessingTask implements Runnable { private final File packageFile; private final File destFile; private final CountDownLatch allPackagesLatch; private final CountDownLatch verifyLatch; public PostCopyProcessingTask(File packageFile, File destFile, CountDownLatch allPackagesLatch, CountDownLatch verifyLatch) { this.packageFile = packageFile; this.destFile = destFile; this.allPackagesLatch = allPackagesLatch; this.verifyLatch = verifyLatch; } @Override public void run() { if (isCancelled) { allPackagesLatch.countDown(); verifyLatch.countDown(); return; } String packageName = packageFile.getName(); try { boolean verifyResult = dataVerification(destFile.getAbsolutePath(), new DecryptUtil.ProgressCallback() { @Override public void onProgress(long processed, long total) { packageStageMap.put(packageName, STAGE_VERIFY); updatePackageProgress(packageName, processed, total, STAGE_VERIFY); updateProgress("解密验签 %s: %s/%s", packageName, processed, total); } }); if (!verifyResult || isCancelled) { updateSuccess = false; } } catch (Exception e) { Log.e(TAG, "解密验签失败", e); updateSuccess = false; } finally { allPackagesLatch.countDown(); verifyLatch.countDown(); } } } private class SerialExtractTask implements Runnable { private final File packageFile; public SerialExtractTask(File packageFile) { this.packageFile = packageFile; } @Override public void run() { if (isCancelled) { allPackagesLatch.countDown(); return; } String packageName = packageFile.getName(); File destFile = new File(storageDir, packageName); // 阶段3: 解压 try { boolean extractResult = FileUtilszwx.extractZipWithProgress(destFile, storageDir, (extracted, total) -> { packageStageMap.put(packageName, STAGE_EXTRACT_ONLY); updatePackageProgress(packageName, extracted, total, STAGE_EXTRACT_ONLY); updateProgress("解压 %s: %s/%s", packageName, extracted, total); }); if (!extractResult || isCancelled) { updateSuccess = false; allPackagesLatch.countDown(); return; } } catch (Exception e) { Log.e(TAG, "解压失败", e); updateSuccess = false; allPackagesLatch.countDown(); return; } // 删除升级包 if (!destFile.delete()) { Log.w(TAG, "删除升级包失败: " + destFile.getName()); } // 最终进度设为最大值 packageProgressMap.put(packageName, MAX_PACKAGE_PROGRESS); allPackagesLatch.countDown(); // 任务完成 } } } 停止使用额外类而是使用方法

filetype

#!/bin/bash # ANSI 颜色定义 red="\e[31m" green="\e[32m" yellow="\e[33m" blue="\e[34m" magenta="\e[35m" cyan="\e[36m" reset="\e[0m" # 设置终端字体加粗 bold="\e[1m" reset_bold="\e[22m" # Python辅助函数:日期验证 validate_date() { python3 << EOF from datetime import datetime try: datetime(year=$1, month=$2, day=$3, hour=$4, minute=$5, second=$6) sys.exit(0) except ValueError: sys.exit(1) EOF } # Python辅助函数:倒计时计算 calculate_countdown() { python3 << EOF from datetime import datetime, timedelta try: target_dt = datetime.strptime("$target", "%Y-%m-%d %H:%M:%S") now_dt = datetime.now() if now_dt >= target_dt: print("${red}目标时间已过${reset}") sys.exit(1) delta = target_dt - now_dt days = delta.days seconds = delta.seconds hours, seconds = divmod(seconds, 3600) minutes, seconds = divmod(seconds, 60) # 精确计算年份(考虑闰年) years = 0 current = now_dt while current < target_dt: try: current = current.replace(year=current.year + 1) years += 1 except ValueError: # 处理2月29日特殊情况 current = current.replace(month=2, day=28, year=current.year + 1) remaining_days = (target_dt - now_dt.replace(year=target_dt.year - years)).days print(f"{years}年 {remaining_days}天 {hours}小时 {minutes}分 {seconds}秒") print(f"{days}天") print(f"{(days*86400 + delta.seconds)//60}分钟") print(f"{days*86400 + delta.seconds}秒") except Exception as e: print(f"计算错误: {str(e)}", file=sys.stderr) sys.exit(2) EOF } # 默认目标时间 target="2028-01-01 00:00:00" # 检查目标时间有效性 check_target() { python3 << EOF from datetime import datetime try: target_dt = datetime.strptime("$target", "%Y-%m-%d %H:%M:%S") now_dt = datetime.now() if now_dt >= target_dt: print("${red}目标时间已过${reset}") sys.exit(1) sys.exit(0) except Exception as e: print(f"日期错误: {str(e)}", file=sys.stderr) sys.exit(2) EOF return $? } # 设置目标时间 set_target() { clear echo -e "${bold}${cyan}===== 设置目标时间 =====${reset}${reset_bold}" # 获取当前时间作为默认值 current_year=$(python3 -c "from datetime import datetime; print(datetime.now().year)") current_month=$(python3 -c "from datetime import datetime; print(datetime.now().month)") current_day=$(python3 -c "from datetime import datetime; print(datetime.now().day)") current_hour=$(python3 -c "from datetime import datetime; print(datetime.now().hour)") current_minute=$(python3 -c "from datetime import datetime; print(datetime.now().minute)") current_second=$(python3 -c "from datetime import datetime; print(datetime.now().second)") # 年份验证(支持1-9999) while true; do echo -n "请输入年份 (1-9999) [默认 $current_year]: " read year year=${year:-$current_year} if [ "$year" -ge 1 ] && [ "$year" -le 9999 ] 2>/dev/null; then break else echo -e "${red}错误: 年份必须是1-9999的整数${reset}" fi done # 月份验证 while true; do echo -n "请输入月份 (1-12) [默认 $current_month]: " read month month=${month:-$current_month} case "$month" in 1|2|3|4|5|6|7|8|9|10|11|12) break;; *) echo -e "${red}错误: 月份必须是1-12${reset}" ;; esac done # 日期验证(自动处理月份天数) while true; do echo -n "请输入日期 (1-31) [默认 $current_day]: " read day day=${day:-$current_day} if [ "$day" -ge 1 ] && [ "$day" -le 31 ] 2>/dev/null; then # 验证日期有效性 validate_date $year $month $day $current_hour $current_minute $current_second if [ $? -eq 0 ]; then break else echo -e "${red}错误: 该月份没有这个日期${reset}" fi else echo -e "${red}错误: 日期必须是1-31${reset}" fi done # 小时验证 while true; do echo -n "请输入小时 (0-23) [默认 $current_hour]: " read hour hour=${hour:-$current_hour} if [ "$hour" -ge 0 ] && [ "$hour" -le 23 ] 2>/dev/null; then break else echo -e "${red}错误: 小时必须是0-23${reset}" fi done # 分钟验证 while true; do echo -n "请输入分钟 (0-59) [默认 $current_minute]: " read minute minute=${minute:-$current_minute} if [ "$minute" -ge 0 ] && [ "$minute" -le 59 ] 2>/dev/null; then break else echo -e "${red}错误: 分钟必须是0-59${reset}" fi done # 秒数验证 while true; do echo -n "请输入秒数 (0-59) [默认 $current_second]: " read second second=${second:-$current_second} if [ "$second" -ge 0 ] && [ "$second" -le 59 ] 2>/dev/null; then break else echo -e "${red}错误: 秒数必须是0-59${reset}" fi done # 设置目标时间 target="${year}-${month}-${day} ${hour}:${minute}:${second}" # 验证日期有效性 if ! check_target; then echo -e "${red}错误: 无效的日期格式(注意月份/日期范围)${reset}" return 1 fi echo -e "${green}目标时间已设置为: ${cyan}$target${reset}" sleep 2 return 0 } # 主菜单 show_menu() { while true; do clear echo -e "${bold}${magenta}===== 彩色倒计时菜单 =====${reset}${reset_bold}" echo -e "${green}1. 🕰设置目标时间🕰" echo -e "${blue}2. 👀查看时间👀" echo -e "${yellow}3. ✨退出程序✨${reset}" echo -e "${bold}${magenta}====Z=====J======开源${reset}${reset_bold}" echo -n "请选择操作 [1-3]: " read choice case $choice in 1) set_target ;; 2) if check_target; then main_loop else echo -e "${red}请设置有效的目标时间${reset}" sleep 2 fi ;; 3) clear echo -e "${cyan}⭐感谢使用ZJ彩色倒计时程序⭐${reset}" exit 0 ;; *) echo -e "${red}无效选择,请重新输入${reset}" sleep 1 ;; esac done } # 主循环 - 显示时间和倒计时 main_loop() { while true; do # 获取当前时间(使用Python确保兼容性) current_time=$(python3 -c "from datetime import datetime; print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))") IFS=' ' read -r date_part time_part <<< "$current_time" IFS='-' read -r year month day <<< "$date_part" IFS=':' read -r hour minute second <<< "$time_part" # 获取倒计时数据 countdown_output=$(calculate_countdown) countdown_exit=$? # 检查错误状态 if [ $countdown_exit -ne 0 ]; then clear echo -e "${bold}${red}🎉 时间到!目标时间已到达! 🎉${reset}${reset_bold}" echo -e "${cyan}目标时间: ${yellow}$target${reset}" echo -e "\n按任意键返回菜单..." read -n 1 return fi # 解析倒计时结果 IFS=$'\n' read -r -d '' countdown_line1 countdown_line2 countdown_line3 countdown_line4 <<< "$countdown_output" # 清屏 clear # 显示菜单提示 echo -e "${bold}${magenta}按 [M] 键返回菜单${reset}${reset_bold}" # 输出动态时钟 echo -e "📅${bold}当前时间${reset_bold} ${red}${year}${reset}-${green}${month}${reset}-${blue}${day}${reset} ${cyan}${hour}${reset}:${magenta}${minute}${reset}:${yellow}${second}${reset} ⏰" # 输出倒计时 echo -e "⏳${bold}距离目标时间${reset_bold} ${cyan}$target${reset} 还有:" echo -e " 🗓${bold}${red}${countdown_line1}${reset}${reset_bold}" echo -e " 🔅总共有 ${bold}${red}${countdown_line2}${reset}${reset_bold}" echo -e " 🔅总共有 ${bold}${green}${countdown_line3}${reset}${reset_bold}" echo -e " 🔅总共有 ${bold}${blue}${countdown_line4}${reset}${reset_bold}" # 检查按键(非阻塞) if read -t 1 -n 1 key; then if [[ $key == "m" || $key == "M" ]]; then return fi fi done } # 启动程序 show_menu 写出修复后的完成脚本

filetype
dnSpy是目前业界广泛使用的一款.NET程序的反编译工具,支持32位和64位系统环境。它允许用户查看和编辑.NET汇编和反编译代码,以及调试.NET程序。该工具通常用于程序开发者在维护和调试过程中分析程序代码,尤其在源代码丢失或者无法获取的情况下,dnSpy能提供很大的帮助。 V6.1.8版本的dnSpy是在此系列软件更新迭代中的一个具体版本号,代表着该软件所具备的功能与性能已经达到了一个相对稳定的水平,对于处理.NET程序具有较高的可用性和稳定性。两个版本,即32位的dnSpy-net-win32和64位的dnSpy-net-win64,确保了不同操作系统架构的用户都能使用dnSpy进行软件分析。 32位的系统架构相较于64位,由于其地址空间的限制,只能支持最多4GB的内存空间使用,这在处理大型项目时可能会出现不足。而64位的系统能够支持更大的内存空间,使得在处理大型项目时更为方便。随着计算机硬件的发展,64位系统已经成为了主流,因此64位的dnSpy也更加受开发者欢迎。 压缩包文件名“dnSpy-net-win64.7z”和“dnSpy-net-win32.7z”中的“.7z”表示该压缩包采用了7-Zip压缩格式,它是一种开源的文件压缩软件,以其高压缩比著称。在实际使用dnSpy时,用户需要下载对应架构的压缩包进行解压安装,以确保软件能够正确运行在用户的操作系统上。 dnSpy工具V6.1.8版本的发布,对于.NET程序员而言,无论是32位系统还是64位系统用户,都是一个提升工作效率的好工具。用户可以根据自己计算机的操作系统架构,选择合适的版本进行下载使用。而对于希望进行深度分析.NET程序的开发者来说,这个工具更是不可或缺的利器。
在南极找不到南
  • 粉丝: 36
上传资源 快速赚钱