Flutter 混合开发 - 03 百度地图定位功能 ios 篇

article/2025/9/21 17:17:44

本节目标

  • 创建 ios flutter 插件流程
  • 集成百度定位功能

视频

https://www.bilibili.com/video/BV1HT4y1L73i/

代码

https://github.com/ducafecat/flutter_baidu_plugin_ducafecat/releases/tag/v1.0.3

百度平台部分

设置 AK

https://lbsyun.baidu.com/apiconsole/key#/home

  • 添加应用

  • 查看 Bundle Identifier

IOS 部分

自动部署 CocoaPods

  • 安装工具
sudo gem install cocoapods
  • ios/flutter_baidu_plugin_ducafecat.podspec
  ...s.dependency 'Flutter's.platform = :ios, '8.0's.dependency 'BMKLocationKit'# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
end
  • 安装百度 sdk 包
pod install
  • 升级
pod repo update
  • pod search 无法搜索到类库的解决办法(找不到类库)
(1)执行pod setup(2)删除~/Library/Caches/CocoaPods目录下的search_index.json文件pod setup成功后会生成~/Library/Caches/CocoaPods/search_index.json文件终端输入rm ~/Library/Caches/CocoaPods/search_index.json删除成功后再执行pod search(3)执行pod search

Info.plist 定位授权

example/ios/Runner/Info.plist

<dict><key>NSLocationWhenInUseUsageDescription</key><string>需要定位</string>

消息通知 BdmapFlutterStreamManager

  • ios/Classes/BdmapFlutterStreamManager.h
//
//  Header.h
//  bdmap_location_flutter_plugin
//
//  Created by Wang,Shengzhan on 2020/2/4.
//#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>NS_ASSUME_NONNULL_BEGIN
@class BdmapFlutterStreamHandler;
@interface BdmapFlutterStreamManager : NSObject
+ (instancetype)sharedInstance ;
@property (nonatomic, strong) BdmapFlutterStreamHandler* streamHandler;@end@interface BdmapFlutterStreamHandler : NSObject<FlutterStreamHandler>
@property (nonatomic, strong) FlutterEventSink eventSink;@end
NS_ASSUME_NONNULL_END
  • ios/Classes/BdmapFlutterStreamManager.m
//
//  BdmapFlutterStreamManager.m
//  bdmap_location_flutter_plugin
//
//  Created by Wang,Shengzhan on 2020/2/4.
//#import "BdmapFlutterStreamManager.h"@implementation BdmapFlutterStreamManager+ (instancetype)sharedInstance {static dispatch_once_t onceToken;static BdmapFlutterStreamManager *manager = nil;dispatch_once(&onceToken, ^{manager = [[BdmapFlutterStreamManager alloc] init];BdmapFlutterStreamHandler * streamHandler = [[BdmapFlutterStreamHandler alloc] init];manager.streamHandler = streamHandler;});return manager;
}@end@implementation BdmapFlutterStreamHandler- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {self.eventSink = eventSink;return nil;
}- (FlutterError*)onCancelWithArguments:(id)arguments {return nil;
}@end

地图接口业务 FlutterBaiduPluginDucafecatPlugin

  • ios/Classes/FlutterBaiduPluginDucafecatPlugin.h
#import <Flutter/Flutter.h>@interface FlutterBaiduPluginDucafecatPlugin : NSObject<FlutterPlugin>
@end
  • ios/Classes/FlutterBaiduPluginDucafecatPlugin.m
#import "FlutterBaiduPluginDucafecatPlugin.h"
#import "BMKLocationkit/BMKLocationComponent.h"
#import "BdmapFlutterStreamManager.h"@interface FlutterBaiduPluginDucafecatPlugin()<BMKLocationManagerDelegate>@property (nonatomic,strong) BMKLocationManager *locManager;@property (nonatomic, copy) FlutterResult flutterResult;@end@implementation FlutterBaiduPluginDucafecatPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {// FlutterMethodChannel* channel = [FlutterMethodChannel//     methodChannelWithName:@"flutter_baidu_plugin_ducafecat"//           binaryMessenger:[registrar messenger]];// FlutterBaiduPluginDucafecatPlugin* instance = [[FlutterBaiduPluginDucafecatPlugin alloc] init];// [registrar addMethodCallDelegate:instance channel:channel];FlutterMethodChannel* channel = [FlutterMethodChannelmethodChannelWithName:@"flutter_baidu_plugin_ducafecat"binaryMessenger:[registrar messenger]];FlutterBaiduPluginDucafecatPlugin* instance = [[FlutterBaiduPluginDucafecatPlugin alloc] init];[registrar addMethodCallDelegate:instance channel:channel];FlutterEventChannel *eventChanel = [FlutterEventChannel eventChannelWithName:@"flutter_baidu_plugin_ducafecat_stream" binaryMessenger:[registrar messenger]];[eventChanel setStreamHandler:[[BdmapFlutterStreamManager sharedInstance] streamHandler]];}// - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
//   if ([@"getPlatformVersion" isEqualToString:call.method]) {
//     result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
//   }
//   else if ([@"duAddOne" isEqualToString:call.method]) {
//       NSInteger val = 100;
//       val += [[call.arguments objectForKey:@"num"] intValue];
//       result([NSNumber numberWithLong:val]);
//   }
//   else {
//     result(FlutterMethodNotImplemented);
//   }
// }- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {if ([@"getPlatformVersion" isEqualToString:call.method]) {result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);} else if ([@"startLocation" isEqualToString:call.method]){ // 开始定位//        NSLog((@"\n[bdmap_loc_flutter_plugin:%s]"), "startLocation...");[self startLocation:result];}else if ([@"stopLocation" isEqualToString:call.method]){ // 停止定位//        NSLog((@"\n[bdmap_loc_flutter_plugin:%s]"), "stopLocation...");[self stopLocation];result(@YES);} else if ([@"updateOption" isEqualToString:call.method] ) { // 设置定位参数result(@([self updateOption:call.arguments]));} else if ([@"setApiKey" isEqualToString:call.method]){ // 设置ios端ak
//        NSLog((@"\n[bdmap_loc_flutter_plugin:%s]"), "setApiKey...");[[BMKLocationAuth sharedInstance] checkPermisionWithKey:call.arguments authDelegate:self];result(@YES);} else {result(FlutterMethodNotImplemented);}
}/**获取设置的期望定位精度*/
-(double)getDesiredAccuracy:(NSString*)str{if([@"kCLLocationAccuracyBest" isEqualToString:str]) {return kCLLocationAccuracyBest;} else if ([@"kCLLocationAccuracyNearestTenMeters" isEqualToString:str]) {return kCLLocationAccuracyNearestTenMeters;} else if ([@"kCLLocationAccuracyHundredMeters" isEqualToString:str]) {return kCLLocationAccuracyHundredMeters;} else if ([@"kCLLocationAccuracyKilometer" isEqualToString:str]) {return kCLLocationAccuracyKilometer;} else {return kCLLocationAccuracyBest;}
}/**获取设置的经纬度坐标系类型*/
-(int)getCoordinateType:(NSString*)str{if([@"BMKLocationCoordinateTypeBMK09LL" isEqualToString:str]) {return BMKLocationCoordinateTypeBMK09LL;} else if ([@"BMKLocationCoordinateTypeBMK09MC" isEqualToString:str]) {return BMKLocationCoordinateTypeBMK09MC;} else if ([@"BMKLocationCoordinateTypeWGS84" isEqualToString:str]) {return BMKLocationCoordinateTypeWGS84;} else if ([@"BMKLocationCoordinateTypeGCJ02" isEqualToString:str]) {return BMKLocationCoordinateTypeGCJ02;} else {return BMKLocationCoordinateTypeGCJ02;}}/**获取设置的应用位置类型*/
-(int)getActivityType:(NSString*)str{if ([@"CLActivityTypeOther" isEqualToString:str]) {return CLActivityTypeOther;} else if ([@"CLActivityTypeAutomotiveNavigation" isEqualToString:str]) {return CLActivityTypeAutomotiveNavigation;} else if ([@"CLActivityTypeFitness" isEqualToString:str]) {return CLActivityTypeFitness;} else if ([@"CLActivityTypeOtherNavigation" isEqualToString:str]) {return CLActivityTypeOtherNavigation;} else {return CLActivityTypeAutomotiveNavigation;}}/**解析flutter端所设置的定位SDK参数*/
-(BOOL)updateOption:(NSDictionary*)args {if(self.locManager) {//        NSLog(@"定位参数配置:%@",args);self.locManager.isNeedNewVersionReGeocode = YES;// 设置期望定位精度if ([[args allKeys] containsObject:@"desiredAccuracy"]) {[self.locManager setDesiredAccuracy:[ self getDesiredAccuracy: args[@"desiredAccuracy"]]];}// 设置定位的最小更新距离if ([[args allKeys] containsObject:@"distanceFilter"]) {self.locManager.distanceFilter = [args[@"distanceFilter"] doubleValue];
//            NSLog(@"最小更新距离值:%f", [args[@"distanceFilter"] doubleValue]);}// 设置返回位置坐标系类型if ([[args allKeys] containsObject:@"BMKLocationCoordinateType"]) {[self.locManager setCoordinateType:[ self getCoordinateType: args[@"desiredAccuracy"]]];}// 设置应用位置类型if ([[args allKeys] containsObject:@"activityType"]) {[self.locManager setActivityType:[ self getActivityType: args[@"desiredAccuracy"]]];}// 设置是否需要返回新版本rgc信息if ([[args allKeys] containsObject:@"isNeedNewVersionRgc"]) {if ((bool)args[@"desiredAccuracy"]) {
//                NSLog(@"需要返回新版本rgc信息");self.locManager.isNeedNewVersionReGeocode = YES;} else {
//                NSLog(@"不需要返回新版本rgc信息");self.locManager.isNeedNewVersionReGeocode = NO;}}// 指定定位是否会被系统自动暂停if ([[args allKeys] containsObject:@"pausesLocationUpdatesAutomatically"]) {if ((bool)args[@"pausesLocationUpdatesAutomatically"]) {
//                NSLog(@"设置定位被系统自动暂停");self.locManager.isNeedNewVersionReGeocode = YES;} else {
//                NSLog(@"设置定位不能被系统自动暂停");self.locManager.isNeedNewVersionReGeocode = NO;}}// 设置是否允许后台定位if ([[args allKeys] containsObject:@"allowsBackgroundLocationUpdates"]) {if ((bool)args[@"allowsBackgroundLocationUpdates"]) {
//                  NSLog(@"设置允许后台定位");self.locManager.isNeedNewVersionReGeocode = YES;} else {
//                  NSLog(@"设置不允许后台定位");self.locManager.isNeedNewVersionReGeocode = NO;self.locManager.distanceFilter = kCLDistanceFilterNone;}}// 设置定位超时时间if ([[args allKeys] containsObject:@"locationTimeout"]) {[self.locManager setLocationTimeout:[args[@"locationTimeout"] integerValue]];self.locManager.coordinateType = BMKLocationCoordinateTypeGCJ02;}// 设置逆地理超时时间if ([[args allKeys] containsObject:@"reGeocodeTimeout"]) {[self.locManager setReGeocodeTimeout:[args[@"reGeocodeTimeout"] integerValue]];}return YES;}return NO;
}
/**启动定位*/
- (void)startLocation:(FlutterResult)result
{self.flutterResult = result;[self.locManager startUpdatingLocation];
}/**停止定位*/
- (void)stopLocation
{self.flutterResult = nil;[self.locManager stopUpdatingLocation];
}- (BMKLocationManager *)locManager {if (!_locManager) {_locManager = [[BMKLocationManager alloc] init];_locManager.locatingWithReGeocode = YES;_locManager.delegate = self;}return _locManager;
}/***  @brief 连续定位回调函数*  @param manager 定位 BMKLocationManager 类。*  @param location 定位结果。*/
- (void)BMKLocationManager:(BMKLocationManager * _Nonnull)manager didUpdateLocation:(BMKLocation * _Nullable)location orError:(NSError * _Nullable)error{if (error){
//        NSLog(@"locError:{%ld - %@};", (long)error.code, error.localizedDescription);} if (location) { // 得到定位信息,添加annotationNSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:1];if (location) {if (location.location.timestamp) {[dic setObject:[self getFormatTime:location.location.timestamp] forKey:@"locTime"]; // 定位时间}if (location.location.horizontalAccuracy) {[dic setObject:@(location.location.horizontalAccuracy) forKey:@"radius"]; // 定位精度}if (location.location.coordinate.latitude) {[dic setObject:@(location.location.coordinate.latitude) forKey:@"latitude"]; // 纬度}if (location.location.coordinate.longitude) {[dic setObject:@(location.location.coordinate.longitude) forKey:@"longitude"]; // 经度}if (location.location.altitude) {
//                NSLog(@"返回海拔高度信息");[dic setObject:@(location.location.altitude) forKey:@"altitude"];// 高度}if (location.rgcData) {[dic setObject:[location.rgcData country] forKey:@"country"]; // 国家[dic setObject:[location.rgcData province] forKey:@"province"]; // 省份[dic setObject:[location.rgcData city] forKey:@"city"]; // 城市if (location.rgcData.district) {[dic setObject:[location.rgcData district] forKey:@"district"]; // 区县}if (location.rgcData.street) {[dic setObject:[location.rgcData street] forKey:@"street"]; // 街道}if (location.rgcData.description) {// 地址信息[dic setObject:[location.rgcData description] forKey:@"address"];}if (location.rgcData.poiList) {NSString* poilist;if (location.rgcData.poiList.count == 1) {for (BMKLocationPoi * poi in location.rgcData.poiList) {poilist = [[poi name] stringByAppendingFormat:@",%@,%@", [poi tags], [poi addr]];}} else {for (int i = 0; i < location.rgcData.poiList.count - 1 ; i++) {poilist = [poilist stringByAppendingFormat:@"%@,%@,%@|", location.rgcData.poiList[i].name,location.rgcData.poiList[i].tags,location.rgcData.poiList[i].addr];}poilist = [poilist stringByAppendingFormat:@"%@,%@,%@",location.rgcData.poiList[location.rgcData.poiList.count-1].name,location.rgcData.poiList[location.rgcData.poiList.count-1].tags,location.rgcData.poiList[location.rgcData.poiList.count-1].addr];}[dic setObject: poilist forKey:@"poiList"]; // 周边poi信息}}} else {[dic setObject: @1 forKey:@"errorCode"]; // 定位结果错误码[dic setObject:@"location is null" forKey:@"errorInfo"]; // 定位错误信息}// 定位结果回调时间[dic setObject:[self getFormatTime:[NSDate date]] forKey:@"callbackTime"];[[BdmapFlutterStreamManager sharedInstance] streamHandler].eventSink(dic);
//        NSLog(@"x=%f,y=%f",location.location.coordinate.latitude,location.location.coordinate.longitude);}
}/**格式化时间*/
- (NSString *)getFormatTime:(NSDate*)date
{NSDateFormatter *formatter = [[NSDateFormatter alloc] init];[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];NSString *timeString = [formatter stringFromDate:date];return timeString;
}@end

Flutter 部分

设置 AK

  @overridevoid initState() {super.initState();_requestPermission(); // 执行权限请求if (Platform.isIOS == true) {FlutterBaiduPluginDucafecat.setApiKeyForIOS("dkYT07blcAj3drBbcN1eGFYqt16HP1pR");}}

其它代码和 android 同接口 无影响

参考

  • https://lbsyun.baidu.com/apiconsole/key#/home
  • https://flutter.dev/docs/development/packages-and-plugins/developing-packages#step-2c-add-ios-platform-code-swifthm
  • http://lbsyun.baidu.com/index.php?title=ios-locsdk/guide/create-project/cocoapods-create
  • http://lbsyun.baidu.com/index.php?title=ios-locsdk

© 猫哥

https://ducafecat.tech

https://ducafecat.gitee.io


http://chatgpt.dhexx.cn/article/H6X1BOsB.shtml

相关文章

iOS-Charts图表绘制一块平行X轴线性指标

养小猫咪的伙伴来我的店铺逛逛吧!抖音商城搜索#早睡早起的猫咪小铺子 最近做项目需要画柱状图和折线图&#xff0c;引入了第三方的图标库Charts。 这个图表库基本上能够满足大家对于图表绘制的需要&#xff0c;但是api接口的解释并不是很详细&#xff0c;该库有强大的功能&…

ios模拟器 - Simulator录制视频

养小猫咪的伙伴来我的店铺逛逛吧!抖音商城搜索#早睡早起的猫咪小铺子 1.进入终端&#xff0c;cd到要放置录屏文件的位置 2.输入命令 ,输入你的命名 xcrun simctl io booted recordVideo xxx.mov 提示&#xff1a;停止录屏 control c 最后进入到对应文件夹就可以找到录制好的…

猫猫学iOS之安装cocoapods

啥事cocoa pods 不解释&#xff0c;自己看这里只有一次安装流程&#xff0c;猫猫的安装流程。 打开命令行&#xff0c;我用的是ruby安装&#xff0c;mac自带ruby&#xff0c;啥是ruby&#xff0c;不解释&#xff0c;因为开始我也不懂&#xff0c;就当他是命令行。 1&#xff…

基于Java的在线聊天APP系统分析及设计

基于Java的在线聊天APP系统分析及设计 目录 基于Java的在线聊天APP系统分析及设计 1 一、 需求分析 3 核心用户分析 3系统的主要功能的概述 3项目操作流程图 4功能详解 4 登录 4注册 4消息盒子 4好友盒子 4好友列表 4朋友验证 4我的账号 4新的朋友 5验证消息 5好友资料卡 5删…

Taro+react仿微信app聊天室|taro仿微信界面|taro聊天/朋友圈

基于TaroreactreduxRNtaroPop等技术开发的跨端聊天App实例&#xff0c;支持编译到多端H5小程序RN端&#xff0c;界面仿制微信聊天界面&#xff0c;实现了消息发送、表情、图片预览、长按菜单、红包、朋友圈等功能。 Taro三端统一聊天应用&#xff1a;taro-chatroom (仿微信界面…

android机器人聊天软件,虚拟男友聊天机器人

虚拟男友聊天机器人是一款能为大家提供专业的虚拟聊天软件&#xff0c;在这里大家可以设定一个符合自己心意的男友&#xff0c;让你们之间的对话是充满了甜蜜&#xff0c;并且还可以自己设定回复方式&#xff0c;对话也是十分的轻松愉悦&#xff0c;快来下载虚拟男友聊天机器人…

uniapp开发即时通讯聊天app,纯nvue仿微信,前后端开源

github地址&#xff1a;GitHub - guipie/GpChat: uniapp开发的纯nvue的即时聊天通讯App。 gitee&#xff1a;https://gitee.com/chenwei_zq/GpChat uniapp开发的纯nvue的即时聊天通讯App。 后台采用.net6,一套解决方案&#xff0c;分布式部署。 App采用uniapp的纯nvue&#x…

使用dialogflow和firebase构建whatsapp聊天机器人的指南

ChatBots are conversational agents, programs capable of conducting a conversation with an Internet user. In this tutorial I’ll walk you through an implementation of WhatsApp chatbot using Twilio platform. ChatBots是对话代理&#xff0c;是能够与Internet用户…

【uni-app】uni-app实现聊天页面功能(小程序)——布局篇

文章目录 前言划分区域问题内容溢出关于调试聊天框 代码实现 前言 在工作中使用uni-app参与开发一个小程序&#xff0c;其中要写一个简单的聊天页面&#xff0c;虽然功能不多&#xff08;只有一个发送文字的功能&#xff09;&#xff0c;但是其中的细节比较多&#xff0c;也踩…

如何搜索WhatsApp聊天消息

Trying to find a specific message in your huge WhatsApp chat log? There are two ways to search, so you can find what you’re looking for quickly. 试图在庞大的WhatsApp聊天日志中查找特定消息&#xff1f; 有两种搜索方式&#xff0c;因此您可以快速找到要查找的内…

【uni-app】uni-app实现聊天页面功能——功能篇(下)

目录 前言一、聊天框随键盘抬起思路代码实现 二、聊天消息列表随着聊天框的增高而滚动到最底部思路 三、问题完整代码实现总结 前言 前面我有写关于如何进行聊天页面布局和实现聊天消息滚动到最底部的文章。 【uni-app】uni-app实现聊天页面功能——功能篇&#xff08;上&…

如何将 WhatsApp 聊天添加到您的网站

WhatsApp是全球最受欢迎的消息传递应用程序。平台上有超过 2 亿活跃用户与朋友、家人和企业进行交流。对于企业而言&#xff0c;WhatsApp 是与客户进行个人、可访问和非正式对话的理想渠道。 要将 WhatsApp 作为渠道引入您的客户旅程&#xff0c;第一步是将 WhatsApp 聊天按钮…

uni-app 即时聊天

项目介绍 前段时间在B站看到了有一个UP主在讲uni-app即时聊天的项目&#xff08;逸刻时光&#xff09;&#xff0c;在看了这个视频之后&#xff0c;感觉还是挺有兴趣的&#xff0c;所以在看他的讲解视频之后&#xff0c;就自己动手写了这个即时聊天项目&#xff0c;在样式方面…

uniapp+nvue实现仿微信App聊天应用 —— 成功实现好友聊天+语音视频通话功能

基于uniapp nvue实现的uniapp仿微信App聊天应用 txim 实例项目&#xff0c;实现了以下功能。 1: 聊天会话管理 2: 好友列表 3: 文字、语音、视频、表情、位置等聊天消息收发 4: 一对一语音视频在线通话 技术实现 开发环境&#xff1a;HbuilderX nodejs技术框架&#xff…

WhatsApp聊天记录迁移新手机,备份如何找回和删除?

当外贸人更换手机时&#xff0c;面临的第一大问题就是WhatsApp数据迁移的问题。 因为WhatsApp的聊天记录、联系人、图片、视频等&#xff0c;都是与客户息息相关的数据&#xff0c;对外贸人来讲都非常重要&#xff0c;所以一定要确保在换手机过程中不丢失WhatsApp的任何资料。…

[uni-app]聊天App实例

项目简介 基于uni-appvuevuexuniPopswiper等技术开发的仿微信聊天室uniapp-chatroom项目&#xff0c;类似vue及小程序api语法使开发更加方便&#xff0c;实现了发送图文消息、表情(gif动图)&#xff0c;图片预览、地图位置、红包、仿微信朋友圈等功能 效果图 在H5 / 小程序 …

开发社交聊天APP需要注意什么?如何快速开发聊天功能

随着互联网的发展&#xff0c;人们的沟通方式也在悄悄发生变化&#xff0c;由原来的面对面沟通&#xff0c;发展为网上沟通。让大家日常生活的通讯越来越方便了&#xff0c;各种APP层出不穷。那么&#xff0c;想开发一款社交聊天并进行运营&#xff0c;需要注意哪些方面&#x…

比较好用的聊天交友软件?最受年轻人欢迎的APP在这

现在都有哪些比较火,比较好用的聊天交友软件?移动社交是一件越来越普遍的事情&#xff0c;人们可以通过互联网&#xff0c;在社交软件上认识众多的一样的有趣人群&#xff0c;在国内都有哪些**的交友app哪个好??在中国在中国最受年轻人欢迎的APP又有哪些呢? 微信 微信可以…

程序员的时钟/原生抖音罗盘时钟代码分享/ 罗盘文字时钟软件非常火 /文字时钟

原生抖音罗盘时钟代码分享 罗盘文字时钟软件非常火 文字时钟 本站给大家分享这款就是通过原生j来s实现的文字时钟源码&#xff0c;有喜欢的朋友可以下载哦&#xff0c;无需安装&#xff0c;上传访问即可使用 简单实现&#xff1a;https://sucaiip.com/time-302.html

Android实现模拟时钟(简单+漂亮)--时针、分针、秒针

前言 前不久在网上看见Android实现的模拟时钟&#xff0c;感觉十分有意思&#xff0c;这里是地址&#xff1a; http://www.eoeandroid.com/forum.php?modviewthread&tid58324可惜的是这种方式没有 秒表。笔者突然对其有了兴趣&#xff0c;也想去实现以下自己的模拟时钟。折…