内容简介:本文为原创分享,转载请注明出处。即时通讯IM应用中的聊天消息时间显示是个再常见不过的需求,现在都讲究用户体验,所以时间显示再也不能像传统软件一样简单粗地暴显示成“年/月/日 时:分:秒”这样。所以,市面上几乎所有的IM都会对聊天消息的时间显示格化做人性化处理,从而提升用户体验(使用感受会明显友好)。这两天正在继续开发RainbowChat-Web产品,所以正需要这样的代码。但经过在即时通讯网的论坛和技术交流群里询问,以及网上的所谓仿微信例子,都不符合要求。这些例子要么简陋粗暴(有逻辑bug硬伤)、要么并不完
本文为原创分享,转载请注明出处。
1、引言
即时通讯IM应用中的聊天消息时间显示是个再常见不过的需求,现在都讲究用户体验,所以时间显示再也不能像传统软件一样简单粗地暴显示成“年/月/日 时:分:秒”这样。所以,市面上几乎所有的IM都会对聊天消息的时间显示格化做人性化处理,从而提升用户体验(使用感受会明显友好)。
这两天正在继续开发RainbowChat-Web产品,所以正需要这样的代码。但经过在即时通讯网的论坛和技术交流群里询问,以及网上的所谓仿微信例子,都不符合要求。这些例子要么简陋粗暴(有逻辑bug硬伤)、要么并不完整(可能只是随手写的练手代码,并不适合放到产品中),所以本着做技术精益求精的态度,没有现成的轮子可用,那就只能造轮子了。
那么,按怎样的显示逻辑来实现呢?作为移动端IM的王者,微信无疑处处是标杆,所以本次的消息时间显示格式,直接参照微信的实现逻辑准没错(随大流虽然没个性,但不至于非主流)。
* 提示: 本文中的代码实现,是从 RainbowChat 和 RainbowChat-Web 两个IM产品中扒出来简化后的结果,是基于完全相同的算法逻辑分别用OC、 Java 和JavaScript实现的。如您觉得有用,可以改改直接用于您的产品,如您有更好的建议请直接回复和评论。代码仅供参考,不足之外,还请见谅!
学习交流:
- 即时通讯/推送技术开发交流4群: 101279154 [推荐]
- 移动端IM开发入门文章:《 新手入门一篇就够:从零开发移动端IM 》
(本文同步发布于: http://www.52im.net/thread-2371-1-1.html )
2、相关文章
《 用于IM中图片压缩的Android工具类源码,效果可媲美微信 [附件下载] 》
《 高仿Android版手机QQ可拖拽未读数小气泡源码 [附件下载] 》
《 Android聊天界面源码:实现了聊天气泡、表情图标(可翻页) [附件下载] 》
《 高仿Android版手机QQ首页侧滑菜单源码 [附件下载] 》
《 高仿手机QQ的Android版锁屏聊天消息提醒功能 [附件下载] 》
《 高仿iOS版手机QQ录音及振幅动画完整实现 [源码下载] 》
《 Android端社交应用中的评论和回复功能实战分享[图文+源码] 》
《 Android端IM应用中的@人功能实现:仿微博、QQ、微信,零入侵、高可扩展[图文+源码] 》
3、看看微信中聊天消息的时间显示规则
先来看看微信中聊天消息的时间显示成什么样:
微信主页“消息”界面
聊天界面(注意聊天界面中默认带了“时:分”的显示)
来自微信官方对聊天消息时间显示的规则说明:
▲ 该规则的定义,主要是2、3条(本图引用自 微信官方FAQ文档 )
4、总结一下微信中聊天消息的时间显示逻辑
参见第3节中的截图和微信官方的说明,我们可以总结出微信对于聊天消息时间显示的规则。
① 微信对于聊天消息时间显示的规则总结如下(首页“消息”界面):
1)当聊天消息时间为一周之内时:当天的消息显示为“小时:分钟”形式,然后是“昨天”、“前天”,然后就是“星期几”这个样子; 2)当聊天消息的时间大于一周时:直接显示“年/月/日”的时间格式。
② 微信对于聊天消息时间显示的规则总结如下(聊天内容界面):
1)当聊天消息时间为一周之内时:当天的消息显示为“小时:分钟”形式,然后是“昨天 时:分”、“前天 时:分”,然后就是“星期几 时:分”这个样子; 2)当聊天消息的时间大于一周时:直接显示“年/月/日 时:分”的完整时间格式。
注意: 聊天内容界面里的时间格式,实际上是首页“消息”界面里的时间格式加上“时:分”后的结果,所以代码实现上这两套代码是可以重用的,无需两份代码。
好了,规则已经摸清,下面将直接上代码。
5、Android平台上的代码实现(标准Java)
5.1 完整源码
/** * 返回指定pattern样的日期时间字符串。 * * @param dt * @param pattern * @return 如果时间转换成功则返回结果,否则返回空字符串"" * @author 即时通讯网([url=http://www.52im.net]http://www.52im.net[/url]) */ publicstaticString getTimeString(Date dt, String pattern) { try { SimpleDateFormat sdf = newSimpleDateFormat(pattern);//"yyyy-MM-dd HH:mm:ss" sdf.setTimeZone(TimeZone.getDefault()); returnsdf.format(dt); } catch(Exception e) { return""; } } /** * 仿照微信中的消息时间显示逻辑,将时间戳(单位:毫秒)转换为友好的显示格式. *
* 1)7天之内的日期显示逻辑是:今天、昨天(-1d)、前天(-2d)、星期?(只显示总计7天之内的星期数,即<=-4d);
* 2)7天之外(即>7天)的逻辑:直接显示完整日期时间。 * * @param srcDate 要处理的源日期时间对象 * @param mustIncludeTime true表示输出的格式里一定会包含“时间:分钟”,否则不包含(参考微信,不包含时分的情况,用于首页“消息”中显示时) * @return 输出格式形如:“10:30”、“昨天 12:04”、“前天 20:51”、“星期二”、“2019/2/21 12:09”等形式 * @author 即时通讯网([url=http://www.52im.net]http://www.52im.net[/url]) * @since 4.5 */ publicstaticString getTimeStringAutoShort2(Date srcDate, booleanmustIncludeTime) { String ret = ""; try { GregorianCalendar gcCurrent = newGregorianCalendar(); gcCurrent.setTime(newDate()); intcurrentYear = gcCurrent.get(GregorianCalendar.YEAR); intcurrentMonth = gcCurrent.get(GregorianCalendar.MONTH)+1; intcurrentDay = gcCurrent.get(GregorianCalendar.DAY_OF_MONTH); GregorianCalendar gcSrc = newGregorianCalendar(); gcSrc.setTime(srcDate); intsrcYear = gcSrc.get(GregorianCalendar.YEAR); intsrcMonth = gcSrc.get(GregorianCalendar.MONTH)+1; intsrcDay = gcSrc.get(GregorianCalendar.DAY_OF_MONTH); // 要额外显示的时间分钟 String timeExtraStr = (mustIncludeTime?" "+getTimeString(srcDate, "HH:mm"):""); // 当年 if(currentYear == srcYear) { longcurrentTimestamp = gcCurrent.getTimeInMillis(); longsrcTimestamp = gcSrc.getTimeInMillis(); // 相差时间(单位:毫秒) longdelta = (currentTimestamp - srcTimestamp); // 当天(月份和日期一致才是) if(currentMonth == srcMonth && currentDay == srcDay) { // 时间相差60秒以内 if(delta < 60* 1000) ret = "刚刚"; // 否则当天其它时间段的,直接显示“时:分”的形式 else ret = getTimeString(srcDate, "HH:mm"); } // 当年 && 当天之外的时间(即昨天及以前的时间) else { // 昨天(以“现在”的时候为基准-1天) GregorianCalendar yesterdayDate = newGregorianCalendar(); yesterdayDate.add(GregorianCalendar.DAY_OF_MONTH, -1); // 前天(以“现在”的时候为基准-2天) GregorianCalendar beforeYesterdayDate = newGregorianCalendar(); beforeYesterdayDate.add(GregorianCalendar.DAY_OF_MONTH, -2); // 用目标日期的“月”和“天”跟上方计算出来的“昨天”进行比较,是最为准确的(如果用时间戳差值 // 的形式,是不准确的,比如:现在时刻是2019年02月22日1:00、而srcDate是2019年02月21日23:00, // 这两者间只相差2小时,直接用“delta/(3600 * 1000)” > 24小时来判断是否昨天,就完全是扯蛋的逻辑了) if(srcMonth == (yesterdayDate.get(GregorianCalendar.MONTH)+1) && srcDay == yesterdayDate.get(GregorianCalendar.DAY_OF_MONTH)) { ret = "昨天"+timeExtraStr;// -1d } // “前天”判断逻辑同上 elseif(srcMonth == (beforeYesterdayDate.get(GregorianCalendar.MONTH)+1) && srcDay == beforeYesterdayDate.get(GregorianCalendar.DAY_OF_MONTH)) { ret = "前天"+timeExtraStr;// -2d } else { // 跟当前时间相差的小时数 longdeltaHour = (delta/(3600* 1000)); // 如果小于 7*24小时就显示星期几 if(deltaHour < 7*24) { String[] weekday = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"}; // 取出当前是星期几 String weedayDesc = weekday[gcSrc.get(GregorianCalendar.DAY_OF_WEEK)-1]; ret = weedayDesc+timeExtraStr; } // 否则直接显示完整日期时间 else ret = getTimeString(srcDate, "yyyy/M/d")+timeExtraStr; } } } else ret = getTimeString(srcDate, "yyyy/M/d")+timeExtraStr; } catch(Exception e) { System.err.println("【DEBUG-getTimeStringAutoShort】计算出错:"+e.getMessage()+" 【NO】"); } returnret; }
5.2 调用示例
// 用于首页“消息”界面时 getTimeStringAutoShort2(newDate(), false); // 用于聊天内容界面时 getTimeStringAutoShort2(newDate(), true);
5.3 运行效果
▲ 上述代码在RainbowChat Android版上的运行效果(首页)
▲ 上述代码在RainbowChat Android版上的运行效果(聊天界面)
6、iOS平台上的代码实现(Objective-C)
6.1 完整源码
源文件TimeTool.h:
#import <Foundation/Foundation.h> @interfaceTimeTool : NSObject /** * 仿照微信中的消息时间显示逻辑,将时间戳(单位:毫秒)转换为友好的显示格式. * 1)7天之内的日期显示逻辑是:今天、昨天(-1d)、前天(-2d)、星期?(只显示总计7天之内的星期数,即<=-4d); * 2)7天之外(即>7天)的逻辑:直接显示完整日期时间。 * @param dt 日期时间对象(本次被判断对象) * @param includeTime YES表示输出的格式里一定会包含“时间:分钟”,否则不包含(参考微信,不包含时分的情况,用于首页“消息”中显示时) * @return 输出格式形如:“刚刚”、“10:30”、“昨天 12:04”、“前天 20:51”、“星期二”、“2019/2/21 12:09”等形式 * @since 1.3 */ + (NSString*)getTimeStringAutoShort2:(NSDate*)dt mustIncludeTime:(BOOL)includeTime; + (NSString*)getTimeString:(NSDate*)dt format:(NSString*)fmt; /** * 获得指定NSDate对象iOS时间戳(格式遵从ios的习惯,以秒为单位)。 */ + (NSTimeInterval) getIOSTimeStamp:(NSDate*)dat; /** * 获得指定NSDate对象iOS时间戳的long形式(格式遵从ios的习惯,以秒为单位,形如:1485159493)。 */ + (long) getIOSTimeStamp_l:(NSDate*)dat; @end
源文件TimeTool.m:
#import "TimeTool.h" @implementationTimeTool // 仿照微信的逻辑,显示一个人性化的时间字串 + (NSString*)getTimeStringAutoShort2:(NSDate*)dt mustIncludeTime:(BOOL)includeTime{ NSString*ret = nil; NSCalendar*calendar = [NSCalendarcurrentCalendar]; // 当前时间 NSDate*currentDate = [NSDatedate]; NSDateComponents*curComponents = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitWeekdayfromDate:currentDate]; NSIntegercurrentYear=[curComponents year]; NSIntegercurrentMonth=[curComponents month]; NSIntegercurrentDay=[curComponents day]; // 目标判断时间 NSDateComponents*srcComponents = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitWeekdayfromDate:dt]; NSIntegersrcYear=[srcComponents year]; NSIntegersrcMonth=[srcComponents month]; NSIntegersrcDay=[srcComponents day]; // 要额外显示的时间分钟 NSString*timeExtraStr = (includeTime?[TimeTool getTimeString:dt format:@" HH:mm"]:@""); // 当年 if(currentYear == srcYear) { longcurrentTimestamp = [TimeTool getIOSTimeStamp_l:currentDate]; longsrcTimestamp = [TimeTool getIOSTimeStamp_l:dt]; // 相差时间(单位:秒) longdelta = currentTimestamp - srcTimestamp; // 当天(月份和日期一致才是) if(currentMonth == srcMonth && currentDay == srcDay) { // 时间相差60秒以内 if(delta < 60) ret = @"刚刚"; // 否则当天其它时间段的,直接显示“时:分”的形式 else ret = [TimeTool getTimeString:dt format:@"HH:mm"]; } // 当年 && 当天之外的时间(即昨天及以前的时间) else{ // 昨天(以“现在”的时候为基准-1天) NSDate*yesterdayDate = [NSDatedate]; yesterdayDate = [NSDatedateWithTimeInterval:-24*60*60 sinceDate:yesterdayDate]; NSDateComponents*yesterdayComponents = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDayfromDate:yesterdayDate]; NSIntegeryesterdayMonth=[yesterdayComponents month]; NSIntegeryesterdayDay=[yesterdayComponents day]; // 前天(以“现在”的时候为基准-2天) NSDate*beforeYesterdayDate = [NSDatedate]; beforeYesterdayDate = [NSDatedateWithTimeInterval:-48*60*60 sinceDate:beforeYesterdayDate]; NSDateComponents*beforeYesterdayComponents = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDayfromDate:beforeYesterdayDate]; NSIntegerbeforeYesterdayMonth=[beforeYesterdayComponents month]; NSIntegerbeforeYesterdayDay=[beforeYesterdayComponents day]; // 用目标日期的“月”和“天”跟上方计算出来的“昨天”进行比较,是最为准确的(如果用时间戳差值 // 的形式,是不准确的,比如:现在时刻是2019年02月22日1:00、而srcDate是2019年02月21日23:00, // 这两者间只相差2小时,直接用“delta/3600” > 24小时来判断是否昨天,就完全是扯蛋的逻辑了) if(srcMonth == yesterdayMonth && srcDay == yesterdayDay) ret = [NSStringstringWithFormat:@"昨天%@", timeExtraStr];// -1d // “前天”判断逻辑同上 elseif(srcMonth == beforeYesterdayMonth && srcDay == beforeYesterdayDay) ret = [NSStringstringWithFormat:@"前天%@", timeExtraStr];// -2d else{ // 跟当前时间相差的小时数 longdeltaHour = (delta/3600); // 如果小于或等 7*24小时就显示星期几 if(deltaHour <= 7*24){ NSArray<NSString*> *weekdayAry = [NSArrayarrayWithObjects:@"星期日", @"星期一", @"星期二", @"星期三", @"星期四", @"星期五", @"星期六", nil]; // 取出的星期数:1表示星期天,2表示星期一,3表示星期二。。。。 6表示星期五,7表示星期六 NSIntegersrcWeekday=[srcComponents weekday]; // 取出当前是星期几 NSString*weedayDesc = [weekdayAry objectAtIndex:(srcWeekday-1)]; ret = [NSStringstringWithFormat:@"%@%@", weedayDesc, timeExtraStr]; } // 否则直接显示完整日期时间 else ret = [NSStringstringWithFormat:@"%@%@", [TimeTool getTimeString:dt format:@"yyyy/M/d"], timeExtraStr]; } } } // 往年 else{ ret = [NSStringstringWithFormat:@"%@%@", [TimeTool getTimeString:dt format:@"yyyy/M/d"], timeExtraStr]; } returnret; } + (NSString*)getTimeString:(NSDate*)dt format:(NSString*)fmt{ NSDateFormatter* format = [[NSDateFormatteralloc] init]; [format setDateFormat:fmt]; return[format stringFromDate:(dt==nil?[TimeTool getIOSDefaultDate]:dt)]; } + (NSTimeInterval) getIOSTimeStamp:(NSDate*)dat{ NSTimeIntervala = [dat timeIntervalSince1970]; returna; } + (long) getIOSTimeStamp_l:(NSDate*)dat{ return[[NSNumbernumberWithDouble:[TimeTool getIOSTimeStamp:dat]] longValue]; } @end
6.2 调用示例
// 用于首页“消息”界面时 [TimeTool getTimeStringAutoShort2:[NSDatedate] mustIncludeTime:NO];
6.3 运行效果
▲ 上述代码在RainbowChat iOS版上的运行效果(首页)
7、Web网页端的代码实现(JavaScript)
7.1 完整源码
抱歉:因文章字数限制,JavaScript版源码无非法贴上来,请从链接: http://www.52im.net/thread-2371-1-1.html ,查看JavaScript版完整源码!
7.2 调用示例
// 用于首页“消息”界面时 _getTimeStringAutoShort2(1550789954260, false); // 用于聊天内容界面时 _getTimeStringAutoShort2(1550789954260, true);
7.3 运行效果
▲ 上述代码在RainbowChat-Web产品上的运行效果
附录:更多精品资源下载
[1] 精品源码下载:
《 Java NIO基础视频教程、MINA视频教程、Netty快速入门视频 [有源码] 》
《 轻量级即时通讯框架MobileIMSDK的iOS源码(开源版)[附件下载] 》
《 开源IM工程“蘑菇街TeamTalk”2015年5月前未删减版完整代码 [附件下载] 》
《 微信本地数据库破解版(含iOS、Android),仅供学习研究 [附件下载] 》
《 NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战 [附件下载] 》
《 NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战 [附件下载] 》
《 NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示 [附件下载] 》
《 NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示 [附件下载] 》
《 用于IM中图片压缩的Android工具类源码,效果可媲美微信 [附件下载] 》
《 高仿Android版手机QQ可拖拽未读数小气泡源码 [附件下载] 》
《 一个WebSocket实时聊天室Demo:基于node.js+socket.io [附件下载] 》
《 Android聊天界面源码:实现了聊天气泡、表情图标(可翻页) [附件下载] 》
《 高仿Android版手机QQ首页侧滑菜单源码 [附件下载] 》
《 开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载] 》
《 微信团队原创Android资源混淆工具:AndResGuard [有源码] 》
《 一个基于MQTT通信协议的完整Android推送Demo [附件下载] 》
《 高仿手机QQ的Android版锁屏聊天消息提醒功能 [附件下载] 》
《 高仿iOS版手机QQ录音及振幅动画完整实现 [源码下载] 》
《 Android端社交应用中的评论和回复功能实战分享[图文+源码] 》
《 Android端IM应用中的@人功能实现:仿微博、QQ、微信,零入侵、高可扩展[图文+源码] 》
《 仿微信的IM聊天时间显示格式(含iOS/Android/Web实现源码)[图文+源码] 》
[2] 精品文档和 工具 下载:
《 重磅发布:《阿里巴巴Android开发手册(规约)》[附件下载] 》
《 阿里技术结晶:《阿里巴巴Java开发手册(规约)-终极版》[附件下载] 》
《 基于RTMP协议的流媒体技术的原理与应用(技术论文)[附件下载] 》
《 独家发布《TCP/IP详解 卷1:协议》CHM版 [附件下载] 》
《 良心分享:WebRTC 零基础开发者教程(中文)[附件下载] 》
《 经典书籍《UNIX网络编程》最全下载(卷1+卷2、中文版+英文版)[附件下载] 》
《 音视频开发理论入门书籍之《视频技术手册(第5版)》[附件下载] 》
《 国际电联H.264视频编码标准官方技术手册(中文版)[附件下载] 》
《 Apache MINA2.0 开发指南(中文版)[附件下载] 》
《 网络通讯数据抓包和分析工具 Wireshark 使用教程(中文) [附件下载] 》
《 最新收集NAT穿越(p2p打洞)免费STUN服务器列表 [附件下载] 》
《 高性能网络编程经典:《The C10K problem(英文)》[附件下载] 》
《 即时通讯系统的原理、技术和应用(技术论文)[附件下载] 》
《 技术论文:微信对网络影响的技术试验及分析[附件下载] 》
《 华为内部3G网络资料: WCDMA系统原理培训手册[附件下载] 》
《 网络测试:Android版多路ping命令工具EnterprisePing[附件下载] 》
《 Android反编译利器APKDB:没有美工的日子里继续坚强的撸 》
《 一款用于P2P开发的NAT类型检测工具 [附件下载] 》
《 两款增强型Ping工具:持续统计、图形化展式网络状况 [附件下载] 》
[3] 精选视频、演讲PPT下载:
《 美图海量用户的IM架构零基础演进之路(PPT)[附件下载] 》
《 开源实时音视频工程WebRTC的架构详解与实践总结(PPT+视频)[附件下载] 》
《 QQ空间百亿级流量的社交广告系统架构实践(视频+PPT)[附件下载] 》
《 海量实时消息的视频直播系统架构演进之路(视频+PPT)[附件下载] 》
《 YY直播在移动弱网环境下的深度优化实践分享(视频+PPT)[附件下载] 》
《 QQ空间移动端10亿级视频播放技术优化揭秘(视频+PPT)[附件下载] 》
《 RTC实时互联网2017年度大会精选演讲PPT [附件下载] 》
《 微信分享开源IM网络层组件库Mars的技术实现(视频+PPT)[附件下载] 》
《 微服务理念在微信海量用户后台架构中的实践(视频+PPT)[附件下载] 》
《 移动端IM开发和构建中的技术难点实践分享(视频+PPT)[附件下载] 》
《 网易云信的高品质即时通讯技术实践之路(视频+PPT)[附件下载] 》
《 腾讯音视频实验室:直面音视频质量评估之痛(视频+PPT)[附件下载] 》
《 腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT[附件下载] 》
《 手机淘宝消息推送系统的架构与实践(音频+PPT)[附件下载] 》
《 如何进行实时音视频的质量评估与监控(视频+PPT)[附件下载] 》
《 Go语言构建高并发消息推送系统实践PPT(来自360公司)[附件下载] 》
《 网易IM云千万级并发消息处理能力的架构设计与实践PPT [附件下载] 》
《 手机QQ的海量用户移动化实践分享(视频+PPT)[附件下载] 》
《 钉钉——基于IM技术的新一代企业OA平台的技术挑战(视频+PPT)[附件下载] 》
《 微信技术总监谈架构:微信之道——大道至简(PPT讲稿)[附件下载] 》
《 Netty的架构剖析及应用案例介绍(视频+PPT)[附件下载] 》
《 滴滴打车架构演变及应用实践(PPT讲稿)[附件下载] 》
《 微信海量用户背后的后台系统存储架构(视频+PPT)[附件下载] 》
《 在线音视频直播室服务端架构最佳实践(视频+PPT)[附件下载] 》
《 从0到1:万人在线的实时音视频直播技术实践分享(视频+PPT)[附件下载] 》
《 微信移动端应对弱网络情况的探索和实践PPT[附件下载] 》
《 Android版微信从300KB到30MB的技术演进(PPT讲稿)[附件下载] 》
《 从零开始搭建瓜子二手车IM系统(PPT)[附件下载] 》
《 极光分享:高并发海量消息推送系统架构演进(视频+PPT)[附件下载] 》
(本文同步发布于: http://www.52im.net/thread-2371-1-1.html )
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- libgo 源码剖析(2. libgo调度策略源码实现)
- HashMap源码实现分析
- 浅谈AsyncTask源码实现
- 【React源码解读】- 组件的实现
- HashMap 实现原理与源码分析
- 手写源码(四):自己实现Mybatis
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First HTML5 Programming
Eric Freeman、Elisabeth Robson / O'Reilly Media / 2011-10-18 / USD 49.99
What can HTML5 do for you? If you're a web developer looking to use this new version of HTML, you might be wondering how much has really changed. Head First HTML5 Programming introduces the key featur......一起来看看 《Head First HTML5 Programming》 这本书的介绍吧!