android 课程表 ui,UICollectionViewLayout实现课程表布局
因为项目中有课程表的相关模块,第一时间想到用UICollectionView。然而后期的需求越来越复杂,每个格子需要展示的内容越来越多,所以不得不寻找合适的解决方案。最后发现自定义UICollectionViewLayout可以实现我的需求。
先放效果图:
课程表.gif
需要知道的:
1. UICollectionViewLayoutAttributes
首先我们要明白,它是我们自定义layout非常关键的一个类,它包含了cell、header、footer等视图的边框,中心点,大小,形状,透明度,层次关系和是否隐藏等信息,其中frame是我们需要用到的。
2. 自定义UICollectionViewLayout需要重载的方法:
// 返回collectionView的内容的尺寸
- (CGSize)collectionViewContentSize;
// 返回rect中的所有的元素的布局属性
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
// 返回对应于indexPath的位置的cell的布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
// 当边界发生改变时,是否应该刷新布局。
// return YES 表示在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
// 以上四个方法是我们需要用到的,重载他们就行。下面两个方法暂时不说。
- (UICollectionViewLayoutAttributes _)layoutAttributesForSupplementaryViewOfKind:(NSString _)kind atIndexPath:(NSIndexPath *)indexPath;
- (UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString_)decorationViewKind atIndexPath:(NSIndexPath _)indexPath;
3. 生命周期
当一个UICollectionViewLayout被创建后,会有一系列方法被系统自动调用;
// 首先,我们在这个方法中重新计算Attributes的各个属性
-(void)prepareLayout;
// 然后,我们从这个方法获取cell的布局属性
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
// 另外,我们可以调用这个方法立即刷新layout
- (void)invalidateLayout;
具体实现:
1. 首先,我们需要一个变量来记录被选中列
// 被选中列
@property (nonatomic, assign) IBInspectable NSInteger extendIndex;
2. 接下来,我们根据UICollectionView的section数、每个section的item数确定全部cell的itemSize,并保存他们的集合。这里的重点是需要区分第一行,第一列还有选中列的itemSize。
// 计算得到表格视图itemSize的集合
- (void)calculateItemsSize {
NSMutableArray *sectionArray = [@[] mutableCopy];
for (NSInteger section = 0; section < [self.collectionView numberOfSections]; section ++) {
NSMutableArray *itemArray = [@[] mutableCopy];
for (NSUInteger index = 0; index < self.rows; index++) {
if (self.itemsSize.count <= index) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:section];
CGSize itemSize = [self sizeForItemWithColumnIndexPath:indexPath];
NSValue *itemSizeValue = [NSValue valueWithCGSize:itemSize];
[itemArray addObject:itemSizeValue];
}
}
[sectionArray addObject:itemArray];
}
self.itemsSize = sectionArray;
}
// 获取对应indexPath的itemSize
static CGFloat const extendWidth = 100.0;
- (CGSize)sizeForItemWithColumnIndexPath:(NSIndexPath *)indexPath {
CGSize size = CGSizeMake(60, 100);//预设cell的size
//根据表格的特性,调整不同情况下cell的宽高
size.width = indexPath.section ? size.width : 40;
size.height = indexPath.item ? size.height : 50;
size.width = (indexPath.section == self.extendIndex && indexPath.section > 0) ? extendWidth : size.width;
return size;
}
3. 根据itemSize重载对应attributes
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
// 确保第一行与第一列永远顶在NavigationBar的下方跟屏幕的左边
if (section == 0 && index == 0) {
attributes.zIndex = 1024;
} else if (section == 0 || index == 0) {
attributes.zIndex = 1023;
}
if (section == 0) { //第一列
CGRect frame = attributes.frame;
frame.origin.x = self.collectionView.contentOffset.x;
attributes.frame = frame;
}
if (index == 0) { // 第一行
CGRect frame = attributes.frame;
frame.origin.y = self.collectionView.contentOffset.y;
attributes.frame = frame;
}
PS:这里重点在于,collectionView横向或者竖向滚动时第一列与第一行的cell不能跟着移动位置,而通过self.collectionView.contentOffset我们可以知道collectionView分别在水平、竖直方向上的位移,那么有两种情况:
水平滚动:我们将collectionView在x轴上的偏移量赋给第一列cell的frame.origin.x,即可造成第一列没有滑动的视觉假象。
竖直滚动:原理同上。
for (int section = 0; section < [self.collectionView numberOfSections]; section++) {
NSMutableArray *sectionAttributes = [@[] mutableCopy];
for (NSUInteger index = 0; index < self.rows; index++) {
CGSize itemSize = [self.itemsSize[section][index] CGSizeValue];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:section];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attributes.frame = CGRectIntegral(CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height));
yOffset = yOffset+itemSize.height;
column++;
if (column == self.rows) {
if (yOffset > contentHeight) {
contentHeight = yOffset;
}
// Reset values
column = 0;
xOffset += itemSize.width;
yOffset = 0;
}
}
}
这里我们通过遍历cell得到attributes,继而通过累加算出collectionView的contentSize:
UICollectionViewLayoutAttributes *attributes = [[self.itemAttributes lastObject] lastObject];
contentWidth = attributes.frame.origin.x + attributes.frame.size.width;
self.contentSize = CGSizeMake(contentWidth, contentHeight);
4. 之后,就是去重载UICollectionViewLayout的那几个需要用到的方法了:
#pragma mark - Overwrite
//返回collectionView的内容的尺寸
- (CGSize)collectionViewContentSize {
return self.contentSize;
}
// 返回对应于indexPath的位置的cell的布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.itemAttributes[indexPath.section][indexPath.row];
}
// 返回rect中的所有的元素的布局属性
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *attributes = [@[] mutableCopy];
for (NSArray *section in self.itemAttributes) {
[attributes addObjectsFromArray:[section filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *evaluatedObject, NSDictionary *bindings) {
return CGRectIntersectsRect(rect, [evaluatedObject frame]);
}]]];
}
return attributes;
}
// 当边界发生改变时,是否应该刷新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES; // 在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息
}
5. 最后,在-prepareLayout方法中清空布局数组、重新计算itemSize、刷新Attributes
- (void)prepareLayout {
[self clearLayoutArray];
[self calculateItemsSize];
[self updateItemsAttributes];
}
到这里也就大功告成了,放上
Tips:
通过IBInspectable关键字可申明该属性可在Storyboard中设置
// 被选中列
@property (nonatomic, assign) IBInspectable NSInteger extendIndex;
image.png
参考资料
android 课程表 ui,UICollectionViewLayout实现课程表布局相关推荐
- Android抓取正方系统课程——实现自己的课程表
Android抓取正方系统课程--实现自己的课程表 上一篇博客讲解了如何使用http协议模拟登陆正方系统,今天继续实现如何抓取课程表并显示在Android界面上,效果如图: 由于偷懒,在界面上没下太多 ...
- android 自定义课程表,Android课程表界面布局实现代码
前言 Android课程表布局实现 我是个菜鸟,文章供参考 示例 图1: 图2: 布局分析 该界面主要可分为三部分: 1.显示年份及周数部分 2.显示周一到周日 3.课程显示部分 实现步骤 1.首先整 ...
- android jsoup 课程表,使用jsoup爬取数据实现android课程表
说明:只是爬虫的一个实现案例,所以没有多做功能,只做了登录跟课表功能,课表有修改周次,单击课程显示课程详细信息等功能. 开发平台:Android Studio 界面 使用TimetableView a ...
- Android课程表App
最近写了个简单的Android 课程表App,我是个初学者,这个App里使用了: Android内置的SQLite数据库储存课程数据. 课程的视图用CardView卡片视图. 课程的View是动态加入 ...
- android课程表控件、悬浮窗、Todo应用、MVP框架、Kotlin完整项目源码
Android精选源码 Android游戏2048 MVP Kotlin项目(RxJava+Rerotfit+OkHttp+Glide) Android基于自定义Span的富文本编辑器 android ...
- Android课程表的设计开发
Android课程表的设计开发 下载链接 鉴于很多人需要源码,这里给下代码. 下载地址(需要5积分,支持下(积累点积分...),没有积分的直接留言邮箱,我发给你或者找其他已经发过的人要下) 没积分的直 ...
- 第一本 Compose 图书上市,联想大咖教你学会 Android 全新 UI 编程
朱江 | 现任联想(北京)有限公司 Android 开发工程师,从事 Android 开发工作多年,有丰富的项目经验,负责和参与开发过多款移动应用程序,同时还是多个开源项目的作者.2017 年开始在 ...
- android ui 最新教程,Android更新UI的五种方式,androidui五种
Android更新UI的五种方式,androidui五种handler.post activity.runOnUiThread view.post handler+Thread AsyncTask 例 ...
- android的UI开发工程师指引
不管是MFC,还是linux,还是android,UI开发都是如下两大核心机制: 第一个是消息循环,第二个是界面组织结构. 围绕着这些,衍生出来的OpenGL,SurfaceView,SurfaceF ...
最新文章
- 计算两个日期之间的年数
- js获取浏览器高和宽的基本信息:屏幕信息
- Python 二分查找算法
- 网易实践|千万级在线直播弹幕方案
- 【Tools】VMware虚拟机三种网络模式详解和操作
- css实现透明度(兼容IE6、火狐等)
- C#软件开发实例.私人订制自己的屏幕截图工具(十)在截图中包括鼠标指针形状...
- CentOS 7 各个版本的区别
- Python爬虫编程常见问题解决方法
- signal、kill、fork
- 基于RSA解题时yafu的使用
- 如何新浪微博html5,新浪微博接入Html5游戏 注重轻量碎片化
- 新视野大学英语读写2 78单元翻译
- NLP算法-词性标注
- flutter 学习之项目一
- IO多路复用和epoll反应堆
- 原画师惊呆:这个爆火AI真把梦境画成现实了!下载APP人人可用
- COM(Componet Object Model_组件对象模型)技术概述
- Facebook要做的事,这家公司4年前就在做了
- RaspberryPi+OneNET MQTT方式 数据上传和命令下发
热门文章
- SCS【25】单细胞细胞间通信第一部分细胞通讯可视化(CellChat)
- 给网站添加建站时长的js代码
- python实现股票自动交易_利用python3.5 +TK 开发股票自动交易伴侣
- Android动画Animator家族使用指南
- 前缀表达式的计算机求值
- 黑盒测试方法—因果图法
- AreEngine IGeometry转WKT,WKB
- 如何申请专利,申请专利的步骤和费用
- 30+宝妈北漂4年,从行政成功转行软件测试,在地铁站外喜极而泣......
- JavaScript 获取随机数