说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

优化UITableViewCell高度计算的那些事

HTML文档下载 WORD文档下载 PDF文档下载
在将UITableView+FDTemplateLayoutCell自动算高工具更新至1.2版本之后,百度知道iOS团队对UITableViewCell利用AutoLayout自动高度计算和UITableView滑动优化进行了总结,以及RunLoop实践技巧。

前言

这篇文章是我和我们团队最近对UITableViewCell利用AutoLayout自动高度计算和UITableView滑动优化的一个总结。从这篇文章里,你可以读到:

  • UITableView高度计算和估算的机制
  • 不同iOS系统在高度计算上的差异
  • iOS8 self-sizing cell
  • UITableView+FDTemplateLayoutCell如何用一句话解决高度问题
  • UITableView+FDTemplateLayoutCell中对RunLoop的使用技巧

UITableViewCell高度计算

rowHeight

UITableView是我们再熟悉不过的视图了,它的delegate和data source回调不知写了多少次,也不免遇到UITableViewCell高度计算的事。UITableView询问cell高度有两种方式。一种是针对所有Cell具有固定高度的情况,通过:

self.tableView.rowHeight = 88;

上面的代码指定了一个所有cell都是88高度的UITableView,对于定高需求的表格,强烈建议使用这种(而非下面的)方式保证不必要的高度计算和调用。rowHeight属性的默认值是44,所以一个空的UITableView显示成那个样子。

另一种方式就是实现UITableViewDelegate中的:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    // return xxx}

需要注意的是,实现了这个方法后,rowHeight的设置将无效。所以,这个方法适用于具有多种cell高度的UITableView。

estimatedRowHeight

这个属性iOS7就出现了,文档是这么描述它的作用的:

If the table contains variable height rows, it might be expensive to calculate all their heights when the table loads. Using estimation allows you to defer some of the cost of geometry calculation from load time to scrolling time.

恩,听上去蛮靠谱的。我们知道,UITableView是个UIScrollView,就像平时使用UIScrollView一样,加载时指定contentSize后它才能根据自己的bounds、contentInset、contentOffset等属性共同决定是否可以滑动以及滚动条的长度。而UITableView在一开始并不知道自己会被填充多少内容,于是询问data source个数和创建cell,同时询问delegate这些cell应该显示的高度,这就造成它在加载的时候浪费了多余的计算在屏幕外边的cell上。和上面的rowHeight很类似,设置这个估算高度有两种方法:

self.tableView.estimatedRowHeight = 88;// or- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {    // return xxx}

有所不同的是,即使面对种类不同的cell,我们依然可以使用简单的estimatedRowHeight属性赋值,只要整体估算值接近就可以,比如大概有一半cell高度是44, 一半cell高度是88, 那就可以估算一个66,基本符合预期。

说完了估算高度的基本使用,可以开始吐槽了:

  1. 设置估算高度后,contentSize.height根据“cell估算值 x cell个数”计算,这就导致滚动条的大小处于不稳定的状态,contentSize会随着滚动从估算高度慢慢替换成真实高度,肉眼可见滚动条突然变化甚至“跳跃”。
  2. 若是有设计不好的下拉刷新或上拉加载控件,或是KVO了contentSize或contentOffset属性,有可能使表格滑动时跳动。
  3. 估算高度设计初衷是好的,让加载速度更快,那凭啥要去侵害滑动的流畅性呢,用户可能对进入页面时多零点几秒加载时间感觉不大,但是滑动时实时计算高度带来的卡顿是明显能体验到的,个人觉得还不如一开始都算好了呢(iOS 8更过分,即使都算好了也会边划边计算)。

iOS8 self-sizing cell

具有动态高度内容的cell一直是个头疼的问题,比如聊天气泡的cell,frame布局时代通常是用数据内容反算高度:

CGFloat height = textHeightWithFont() + imageHeight + topMargin + bottomMargin + ...;

供UITableViewDelegate调用时很可能是个cell的类方法:

@interface BubbleCell : UITableViewCell+ (CGFloat)heightWithEntity:(id)entity;@end

各种魔法margin加上耦合了屏幕宽度。

AutoLayout时代好了不少,提供了-systemLayoutSizeFittingSize:的API,在contentView中设置约束后,就能计算出准确的值;缺点是计算速度肯定没有手算快,而且这是个实例方法,需要维护专门为计算高度而生的template layout cell,它还要求使用者对约束设置的比较熟练,要保证contentView内部上下左右所有方向都有约束支撑,设置不合理的话计算的高度就成了0。

这里还不得不提到一个UILabel的蛋疼问题,当UILabel行数大于0时,需要指定preferredMaxLayoutWidth后它才知道自己什么时候该折行。这是个“鸡生蛋蛋生鸡”的问题,因为UILabel需要知道superview的宽度才能折行,而superview的宽度还依仗着子view宽度的累加才能确定。这个问题好像到iOS 8才能够自动解决(不过我们找到了解决方案)。

回到正题,iOS 8 WWDC中推出了self-sizing cell的概念,旨在让cell自己负责自己的高度计算,使用frame layout和auto layout都可以享受到:


这个特性首先要求是iOS 8,要是最低支持的系统版本小于8的话,还得针对老版本单写套老式的算高(囧),不过用的API到不是新面孔:

self.tableView.estimatedRowHeight = 213;self.tableView.rowHeight = UITableViewAutomaticDimension;

这里又不得不吐槽了,自动计算rowHeight跟estimatedRowHeight到底是有什么仇,如果不加上估算高度的设置,自动算高就失效了。

PS:iOS 8系统中rowHeight的默认值已经设置成了UITableViewAutomaticDimension,所以第二行代码可以省略。

问题:

  • 这个自动算高在push到下一个页面或者转屏时会出现高度特别诡异的情况,不过现在的版本修复了。
  • 求一个能让最低支持iOS 8的公司。

iOS 8抽风的算高机制

相同的代码在iOS 7和iOS 8上滑动顺畅程度完全不同,iOS 8莫名奇妙的卡。很大一部分原因是iOS 8上的算高机制大不相同,这是我做的小测试:


研究后发现这么多次额外计算有下面的原因:

  1. 不开启高度估算时,UITableView上来就要对所有cell调用算高来确定contentSize。
  2. dequeueReusableCellWithIdentifier:forIndexPath: 相比不带“forIndexPath”的版本会多调用一次高度计算。
  3. iOS 7计算高度后有“缓存”机制,不会重复计算;而iOS 8不论何时都会重新计算cell高度。

iOS 8把高度计算搞成这个样子,从WWDC也倒是能找到点解释,cell被认为随时都可能改变高度(如从设置中调整动态字体大小),所以每次滑动出来后都要重新计算高度。

说了这么多,究竟有没有既能省去算高烦恼,又能保证顺畅的滑动,还能支持iOS 6+的一站式解决方案呢?

UITableView+FDTemplateLayoutCell

使用UITableView+FDTemplateLayoutCell无疑是解决算高问题的最佳实践之一,既有iOS 8 self-sizing功能简单的API,又可以达到iOS7流畅的滑动效果,还保持了最低支持iOS6。

使用起来大概是这样:

#import <UITableView+FDTemplateLayoutCell.h>- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    return [tableView fd_heightForCellWithIdentifier:@"identifer" cacheByIndexPath:indexPath configuration:^(id cell) {        // 配置 cell 的数据源,和 "cellForRow" 干的事一致,比如:        cell.entity = self.feedEntities[indexPath.row];    }];}

写完上面的代码后,你就已经使用到了:

  • 和每个UITableViewCell ReuseID一一对应的template layout cell
这个cell只为了参加高度计算,不会真的显示到屏幕上;它通过UITableView的-dequeueCellForReuseIdentifier: 方法lazy创建并保存,所以要求这个ReuseID必须已经被注册到了UITableView中,也就是说,要么是Storyboard中的原型cell,要么就是使用了UITableView的-registerClass:forCellReuseIdentifier:或-registerNib:forCellReuseIdentifier:其中之一的注册方法。
  • 根据autolayout约束自动计算高度
使用了系统在iOS 6就提供的API:-systemLayoutSizeFittingSize:
  • 根据index path的一套高度缓存机制
计算出的高度会自动进行缓存,所以滑动时每个cell真正的高度计算只会发生一次,后面的高度询问都会命中缓存,减少了非常可观的多余计算。
  • 自动的缓存失效机制
无须担心你数据源的变化引起的缓存失效,当调用如-reloadData,-deleteRowsAtIndexPaths:withRowAnimation:等任何一个触发 UITableView 刷新机制的方法时,已有的高度缓存将以最小的代价执行失效。如删除一个indexPath为[0:5]的cell时,[0:0] ~ [0:4]的高度缓存不受影响,而[0:5]后面所有的缓存值都向前移动一个位置。自动缓存失效机制对UITableView的9个公有API都进行了分别的处理,以保证没有一次多余的高度计算。
  • 预缓存机制
预缓存机制将在UITableView没有滑动的空闲时刻执行,计算和缓存那些还没有显示到屏幕中的cell,整个缓存过程完全没有感知,这使得完整列表的高度计算既没有发生在加载时,又没有发生在滑动时,同时保证了加载速度和滑动流畅性,下文会着重讲下这块的实现原理。

我们在设计这个工具的API时斟酌了非常长的时间,既要保证功能的强大,也要保证接口的精简,一行调用背后隐藏着很多功能。

这一套缓存机制能对滑动起多大影响呢?除了肉眼能明显的感知到外,我还做了个小测试。一个有54个内容和高度不同cell的table view,从头滑动到尾,再从尾滑动到头,iOS 8系统下,iPhone 6,使用Time Profiler监测算高函数所花费的时间:

  • 未使用缓存API、未使用估算,共花费877ms


  • 使用缓存API、开启估算,共花费77ms


测试数据的精度先不管,从量级上就差了一个数量级,说实话自己也没想到差距有这么大。

同时,工具也顺手解决了-preferredMaxLayoutWidth的问题,在计算高度前向contentView加了一条和table view宽度相同的宽度约束,强行让contentView内部的控件知道了自己父view的宽度,再反算自己被外界约束的宽度,破除“鸡生蛋蛋生鸡”的问题,这里比较tricky,就不展开说了。下面说说利用RunLoop预缓存的实现。

利用RunLoop空闲时间执行预缓存任务

FDTemplateLayoutCell的高度预缓存是一个优化功能,它要求页面处于空闲状态时才执行计算,当用户正在滑动列表时显然不应该执行计算任务影响滑动体验。

一般来说,这个功能要耦合UITableView的滑动状态才行,但这种实现十分不优雅且可能破坏外部的delegate结构,但好在我们还有RunLoop这个工具,了解它的运行机制后,可以用很简单的代码实现上面的功能。

空闲RunLoopMode

当用户正在滑动UIScrollView时,RunLoop将切换到UITrackingRunLoopMode接受滑动手势和处理滑动事件(包括减速和弹簧效果),此时,其他Mode(除NSRunLoopCommonModes这个组合Mode)下的事件将全部暂停执行,来保证滑动事件的优先处理,这也是iOS滑动顺畅的重要原因。

当UI没在滑动时,默认的Mode是NSDefaultRunLoopMode(同CF中的kCFRunLoopDefaultMode),同时也是CF中定义的“空闲状态Mode”。当用户啥也不点,此时也没有什么网络 IO时,就是在这个Mode下。

用RunLoopObserver找准时机

注册RunLoopObserver可以观测当前RunLoop的运行状态,并在状态机切换时收到通知:

  1. RunLoop开始
  2. RunLoop即将处理Timer
  3. RunLoop即将处理Source
  4. RunLoop即将进入休眠状态
  5. RunLoop即将从休眠状态被事件唤醒
  6. RunLoop退出

因为“预缓存高度”的任务需要在最无感知的时刻进行,所以应该同时满足:

  1. RunLoop处于“空闲”状态Mode;
  2. 当这一次RunLoop迭代处理完成了所有事件,马上要休眠时。

使用CF的带block版本的注册函数可以让代码更简洁:

CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFStringRef runLoopMode = kCFRunLoopDefaultMode;CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {    // TODO here});CFRunLoopAddObserver(runLoop, observer, runLoopMode);

在其中的TODO位置,就可以开始任务的收集和分发了,当然,不能忘记适时的移除这个observer。

分解成多个RunLoop Source任务

假设列表有20个cell,加载后展示了前5个,那么开启估算后table view只计算了这5个的高度,此时剩下15个就是“预缓存”的任务,而我们并不希望这15个计算任务在同一个RunLoop迭代中同步执行,这样会卡顿UI,所以应该把它们分别分解到15个RunLoop迭代中执行,这时就需要手动向RunLoop中添加Source任务(由应用发起和处理的是Source 0任务)

Foundation层没对RunLoopSource提供直接构建的API,但是提供了一个间接的、既熟悉又陌生的API:

- (void)performSelector:(SEL)aSelector               onThread:(NSThread *)thr              withObject:(id)arg           waitUntilDone:(BOOL)wait                   modes:(NSArray *)array;

这个方法将创建一个Source 0任务,分发到指定线程的RunLoop中,在给定的Mode下执行,若指定的RunLoop处于休眠状态,则唤醒它处理事件,简单来说就是“睡你xx,起来嗨!”

于是,我们用一个可变数组装载当前所有需要“预缓存”的index path,每个RunLoopObserver回调时都把第一个任务拿出来分发:

NSMutableArray *mutableIndexPathsToBePrecached = self.fd_allIndexPathsToBePrecached.mutableCopy;CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {    if (mutableIndexPathsToBePrecached.count == 0) {        CFRunLoopRemoveObserver(runLoop, observer, runLoopMode);        return;    }    NSIndexPath *indexPath = mutableIndexPathsToBePrecached.firstObject;    [mutableIndexPathsToBePrecached removeObject:indexPath];    [self performSelector:@selector(fd_precacheIndexPathIfNeeded:)                 onThread:[NSThread mainThread]               withObject:indexPath            waitUntilDone:NO                    modes:@[NSDefaultRunLoopMode]];});

这样,每个任务都被分配到下个“空闲”RunLoop迭代中执行,其间但凡有滑动事件开始,Mode切换成UITrackingRunLoopMode,所有的“预缓存”任务的分发和执行都会自动暂定,最大程度保证滑动流畅。

本文原载于Sunny's Blog,作者:孙源(@我就叫Sunny怎么了),90后非主流iOS程序猿,现负责百度知道iOS团队。


CSDN移动将持续为您优选移动开发的精华内容,共同探讨移动开发的技术热点话题,涵盖移动应用、开发工具、移动游戏及引擎、智能硬件、物联网等方方面面,如果您有想分享的技术、观点,可通过电子邮件(tangxy#csdn.net,请把#改成@)投稿。

第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。


比较两个文件-VB资料 采用递归算法删除带有多级子目录的目录-VB资料 菜单项的动态装入 -VB资料 处理加了密码的MDB文件-VB资料 VB创建临时文件 从公共对话框控件中提取多个文件名称 -VB资料 从全路径名中提取文件名 -VB资料 打印MsChart之类的控件 -VB资料 谈谈在VB6.0中如何使用资源文件进行编程 动画图像分解在VB动画设计中的应用 多重文档界面设计方法-VB资料 恢复回收站中的文件 -VB资料 VB获得位图文件的信息 VB获得文本框中光标所在行的内容 VB获取程序工作路径的方法 VB获取文件或文件夹属性 基于VB的通用折行打印程序 基于VB和EXCEL的报表设计及打印 VB建立多级目录 -VB资料 VB建立快捷方式-VB资料 VB建立文件关联 -VB资料 将RichTextBox 之中被选取的内容复制到剪贴簿-VB资料 将我的程序的文本直接送到WORD中(不用粘贴)-VB资料 开启文件属性窗口 -VB资料 快速读取 TextBox 第 N 行的资料 -VB资料 快速选择里List全部项目-VB资料 VB利用 App.Path 读取「应用程序所在之目录」 VB利用 EM_LINESCROLL 信息控制 TextBox 的卷动。 命名的技巧 -VB资料 目录所占的字节数-VB资料 VB判断文件是否在IE的缓存中 高分求助!请问怎么去掉IE的最大化和最小化按钮!! 关于UltraDev中浏览的问题???? 请大家推荐几本vc与汇编混和编程的好书 为什么会这样呢?关不掉的对话框 菜鸟提问:Form 与 Frame 有何区别? 以下是Delphi帮助的说明,谁能帮忙解释一下? 流星花园 我在image的click()方法里定义的变量,为什么在image_dblclick()里不能用?? 请教一下:金山词霸的屏幕取单词,是如何用程序实现的? 用VC怎么实现生成一个虚拟目录? 那位朋友有2000命令行模式下命令操作的帮助手册? IIS虚拟主机的问题,请大家给解决呀! 一个有关ListCtrl中拖动Column的问题。 请问那位大虾有用vc开发openssl的经验和原码? 兄弟我想学习数据库 但学什么好呐 给点意见 急求:将savedialog中多选的文件一次全都添进listview 谁有DirectX开发(SDK),给我一份,或留个连接地址 谁有DirectX开发(SDK),给我一份,或留个连接地址 怎样动态生成函数??? 你好!请问怎样给分? 高分debug!!!!!!! 再加50分请各位高手帮忙:http://www.csdn.net/expert/topic/598/598527.xml?temp=.1428339 请问各位,这种问题如何解决? from now , I'll come here usuauly 为什么邮件的接受时间会超前?高手请进来看看。 请大家推荐几本vc与汇编混和编程的好书 from now ,I 'll often come here. 请问: 中文版的 。NET 如何英文化????????????????? 高分debug!!!!!!! 怎样用vc编出来的程序能够隐藏起来,就象任务管理器那样 如何得到Query1->SQL->Add(select sum(xxx) from Table);的返回值? 谁能告诉我,这样得到的IPaddress 仅是本地dns解析的, 关于getdlgitem的问题? 如何得到登陆到服务器上的所有机器IP地址?????? 怎么样用C#在WEB页上画图形? 谁能告诉我,这样得到的IPaddress 是否仅是本地dns解析的? 请大家推荐几本vc与汇编混和编程的好书,请注明是哪个出版社出版 老问题:一个关于DLL输出函数的问题? 关于Win2K启动时程序自动加载的问题 Win2K里面输入法顺序调整的问题 同时有16个客户端从服务器端用FTP下载文件,但失败率很高,请问是什么问题 问大家一个C语言的教科书问题,谢谢了! 请高手明天,有关于JBUILDER6在XP下安装的问题 SOS ADO数据库中的varBinary字段的存取 TStream 怎么样改变标题颜色(不准用控件) 难道adaptive server anywhere6.0不支持中文吗? 我的窗口怎么处理不了方向键消息? 这是不是CArray的BUG?????????????? 关于如何修改历史记录文件夹属性的问题,,特急!!!!!!!!! 系统存储器和扩展存储器都在内存条上,是不是? 数据流传输的TNMStrm和TNMStrmServ组件和TNMUDP控件有什么不同啊? 在oracle如何这么做? 苹果3gs手机怎样设置重力感应?请知道的告诉我, 分母一定,分子和分数值成正比例. 把4改写成以千分之一为单位的数是( ),减少100个这样的计数单位后是( ),最低位是( ). 人坐在凳子上,人受的重力和人受到凳子的支持力 为什么不是二力平衡? 求教第五题判断题 0.9改写成千分之一为单位是()这是他与原数相比是()他的计数单位变() 蚕豆怎么做 分子一定,分母与分数值.能成正比例吗?并说说原因. 判断:一质点在某段时间内做曲线运动,则在这段时间内加速度一定不断改变. 弹力和摩擦力的方向一定互相垂直么拜托各位了 3Q 芥菜和盖菜的区别 牛顿每千克是不是加速度的国际单位 摩擦力与弹力的方向一定垂直吗?rt 芥菜和芥兰菜有什么区别? 一质点在某时间段内做曲线运动,在这段时间内为什么速度一定在不断的改变,而加速度可以不变 你听说过著名的牛顿万有引力定律吗?任何两个物体之间都有吸引力.如果设两个物体的质量分别为m1,m2,它们之间的距离是d,那么它们之间的引力就是f=gm1m2/d的平方(g为常数).人在地面上所 需要什么原料,具体步骤是什么, "牛顿"这个单位是计量什么的? 摩擦力的大小和什么有关 牛顿万有引力定律的作用都说牛顿发现了万有引力的定律那么他的这一发现对人类有什么好处?万有引力这一定律的用处是什么? 梅菜和雪里红有什么区别呀? 摩擦力大小与什么有关是初三的内容 不考虑什么最大静摩擦摩擦力大小跟什么有关 ?滑动摩擦力大小又和什么有关? 判断题.一个分数的分母不变,分子加上2,分数值就扩大2倍.对还是错,为什么 小白菜和大头菜有什么区别? 在讲高一物理时,滑动摩擦力和接触面的粗糙程度以用压力有关时,是直接给出公式好,还是从实验中得出来好呢?和接触面积无关倒是从一小个例子就可以得出来了,但要说和接触面粗糙程度以 一个数的分子不变,分母缩小2倍,这个分数值缩小2倍,还是扩大2倍,还是大小不变 如果x=九分之y,x和y成不成正比例,为什么 分数的分子一定,分母与分数值成不成正比例,为什么 牛顿的生活故事! 需要什么原料,具体步骤是什么, 怎样做`黄焖猪肉`?``` . 当人体缺碘时,甲状腺激素的合成量减少,甲状腺因此逐渐萎缩。 2.胰能分泌分解蛋白质的酶,也能分泌胰岛素。但胰岛素是由分布在胰腺细胞合成和分 为什么有摩擦力一定要有正压力(弹力)? 需要什么原料,具体步骤是什么, 物体受到在一条直线上大小分别为3N和5N的同方向的两个力的作用,则这个物体所受到的合力大小为--- 关于弹力和摩擦力,下列说法中正确的是() A.两物体间的摩擦力和压力方向一定互相垂直B.两物体间的摩擦力大小与它们间的弹力大小无关C.物体间的摩擦力大小跟接触面积有关D.物体间有弹 0.09的计数单位是 素烧蚕豆怎么做?需要什么原料,具体步骤是什么,谢谢 压力、推力、弹力、摩擦力的本质为什么是电场力?首先,一楼的,不要不懂装懂,再就是三楼四楼,可以思考一下,电磁力,电磁作用的本质还是电场力。 0.09的计数单位是多少 有两个力,一个为3N一个为5N他们的合力大小可能为? 一个最简分数,如果把分子扩大到原来的5倍,分母缩小到原来的6分之1,就变成了14,求原来的分数是多少? 曲线运动是变速运动,因为做曲线运动的物体速度方向在时刻改变, 需要什么原料,具体步骤是什么, 一个最简分数分子扩大5倍,分母缩小2倍得4又6分之1求这个分数是多少 曲线运动是变速运动,加速度一定变化吗 两个大小分别为2N和3N的力,同时作用在质量为1Kg的物体上,物体产生加速度的值是?是0到5N吗 一个最简分数,把它的分子扩大到原来的2倍,分母缩小为原来的2分之1后是2分之1,这个分数是多少?写出计算步骤.学过方程. 牛顿单位换算,我没学过,希望能详细点. 滑动摩擦力中,摩擦力方向与其物体弹力方向有什么关系吗?是永远相互垂直吗?摩擦力方向与其物体弹力方向有什么关系 是否相互垂直 如果不是请举例 机械振动如何测量 平抛运动加速度方向时刻改变是匀变速曲线运动我们把加速度(或合外力)恒定的运动称为“匀变速运动”,把加速度(大小或方向或两者同时)变化的运动称为“变加速运动”.我混乱了 滑动摩擦力的方向与物体间弹力的方向垂直 分子一定 分数值和分母 能否成正比例要用这种格式 例如:圆的周长和直径周长和直径是相关联的量 周长随着直径变化而变化 周长÷直径=π(一定)所以周长和直径成正比例 质量牛顿 单位换算 两物体间弹力方向与摩擦力方向一定垂直,这话正确? 分子一定,分数值与分母成正比例.( ) (是对的还是错的) 做匀变速曲线运动的物体 随着时间的延续 变小 为什么 可以画一个图吗 判断题, 分子一定,分母和分数值,成正比例吗 求伽利略 哥白尼 开普勒 托勒密的天文学观点的不同处和相同处 还有最好能附上他们的观点模型图 和一些如果图片或资料来源于网站的话求地址 如果找到资料了直接给地址也行
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘