靜態(tài)單元格
效果演示:
我們看到這個界面之后, 其實我們可以利用我們之前的知識來完成, 但是今天我們講的是利用靜態(tài)單元格完成(這樣就不需要寫任何的代碼)
靜態(tài)單元格的處理環(huán)境就是對于和我們應(yīng)用的服務(wù)器沒有任何關(guān)系的(只有程序更新該界面才會改變的界面)
首先, 按照我們以前的方式: 讓我們的控制器繼承自tableViewController
然后將我們的storyboard中的view換成tableView
我們上面的右邊一個小框框上面那個是代表的是動態(tài)的cell就是數(shù)據(jù)可以動態(tài)的
小框框的是靜態(tài)的, 里面的數(shù)據(jù)是死的, 沒有辦法改變的.
然后我們cell的style是Groups
這上面的小框框里面表示的是有多少組, 但是首先我們要先寫一個1組, 這樣當(dāng)我們完成了這一個cell之后, 在改成3組時, 他會自動復(fù)制我們之前設(shè)置好的這一組 .
這里面的row是每一組cell里面有幾個cell
這個框框可以選擇我們cell的類型, 以及下面可以添加圖片
我們只需要將我們的圖片名稱填到里面就可以了
然后, 我們的文字, 只需要我們連續(xù)點擊兩次這個位置:
就可以顯示出這種樣式, 這樣我們就可以修改里面的內(nèi)容了. 最后我們再直接將以前的那個多少組, 再改成3組依次就可以了.
好友列表- 01加載數(shù)據(jù)模型
效果演示;
我們注意到, 在我們的這個界面中, 一個是好友列表, 一個是好友組列表
所以我們不妨創(chuàng)建兩個模型, 一個是好友信息數(shù)據(jù)模型, 一個是好友組信息數(shù)據(jù)模型
在這加載數(shù)據(jù)模型, 是為了復(fù)習(xí) 我就直接將代碼拿出來了好吧:
FriendGroups.h文件
#import <Foundation/Foundation.h>
@interface GJFriendGroup : NSObject
@property (nonatomic, copy) NSString *name;
/**
* 數(shù)組中裝的都是GJFriend模型
*/
@property (nonatomic, strong) NSArray *friends;
@property (nonatomic, assign) int online;
+ (instancetype)groupWithDict:(NSDictionary *)dict;
- (instancetype)initWithDict:(NSDictionary *)dict;
@end
FriendGroups.m文件
#import "GJFriendGroup.h"
#import "GJFriend.h"
@implementation GJFriendGroup
+ (instancetype)groupWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
}
- (instancetype)initWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
// 1.注入所有屬性
[self setValuesForKeysWithDictionary:dict];
// 2.特殊處理friends屬性
NSMutableArray *friendArray = [NSMutableArray array];
for (NSDictionary *dict in self.friends) {
GJFriend *friend = [GJFriend friendWithDict:dict];
[friendArray addObject:friend];
}
self.friends = friendArray;
}
return self;
}
@end
friend.h文件
#import <Foundation/Foundation.h>
@interface GJFriend : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *icon;
@property (nonatomic, copy) NSString *intro;
@property (nonatomic, assign, getter = isVip) BOOL vip;
+ (instancetype)friendWithDict:(NSDictionary *)dict;
- (instancetype)initWithDict:(NSDictionary *)dict;
@end
friend.m文件
#import "GJFriend.h"
@implementation GJFriend
+ (instancetype)friendWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
}
- (instancetype)initWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
@end
控制器.m文件
- (NSArray *)groups
{
if (_groups == nil) {
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"friends.plist" ofType:nil]];
NSMutableArray *groupArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
GJFriendGroup *group = [GJFriendGroup groupWithDict:dict];
[groupArray addObject:group];
}
_groups = groupArray;
}
return _groups;
}
好友列表 - 02顯示好友數(shù)據(jù)
我們首先將所有的數(shù)據(jù)給弄到界面里面去, 然后再考慮其他的, 所以這個又是直接上代碼:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.groups.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
GJFriendGroup *group = self.groups[section];
return group.friends.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.創(chuàng)建cell
static NSString *ID = @"friend";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
// 2.設(shè)置cell的數(shù)據(jù)
GJFriendGroup *group = self.groups[indexPath.section];
GJFriend *friend = group.friends[indexPath.row];
cell.imageView.image = [UIImage imageNamed:friend.icon];
cell.textLabel.text = friend.name;
cell.detailTextLabel.text = friend.intro;
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
GJFriendGroup *group = self.groups[section];
return group.name;
}
@end
好友列表 -03添加頭部控件
之前我們在做汽車品牌展示的 時候就已經(jīng)知道了, 他有一個頭部的label用來顯示我我們的頭部標(biāo)題的, 現(xiàn)在其實我們的這個頭部不僅僅可以使label同時也可以是UIView當(dāng)然 他的命名可不是這個, 他的命名是: UITableViewHeaderFooterView
觀察上面的效果圖, 我們發(fā)現(xiàn), 如果讓他作為我們每一個好友列表的頭部就需要具備四個控件(UIImage, UILabel, UILablel, UIButton), 當(dāng)然,這種方法是不可取的, 因為 當(dāng)我們點擊我們那個小箭頭的時候, 我們的好友列表也會展開, 所以我們采用的措施是:
用一個大的UIButton覆蓋整個UITableViewHeaderFooterView, 然后再在UIButton上面添加一個UILabel, 這樣我們的UITableViewHeaderFooterView就具備了以上四個控件(UIButton自身還具備了UIImage, UILabel)
下面開始寫:
首先, 我們要有一個繼承自UITableViewHeaderFooterView的類(GJHeaderView)
然后在這個類中設(shè)置我們的UITableViewHeaderFooterView
- 寫一個方法, 方便我們的控制器調(diào)用這個方法來創(chuàng)建頭部控件
+ (instancetype)headerViewWithTableView:(UITableView *)tableView;
2 . 在該方法里面設(shè)置我們headerView
其實我們的頭部也是和我們的UIViewCell一樣可以重復(fù)利用的, 所以他在創(chuàng)建的時候, 類似于我們的UIViewCell:
+ (instancetype)headerViewWithTableView:(UITableView *)tableView
{
static NSString *ID = @"header";
GJHeaderView *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID];
if (header == nil) {
header = [[GJHeaderView alloc] initWithReuseIdentifier:ID];
}
return header;
}
注意這個方法的調(diào)用: header = [[GJHeaderView alloc] initWithReuseIdentifier:ID];
我們?nèi)绻朐趧?chuàng)建頭部的時候, 自動為我們添加我們說的幾個子控件 就需要改寫這個方法(直接在這個方法里面創(chuàng)建)
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
// 添加子控件
// 1.添加按鈕
UIButton *nameView = [UIButton buttonWithType:UIButtonTypeCustom];
[self.contentView addSubview:nameView];
self.nameView = nameView;
// 2.添加好友數(shù)
UILabel *countView = [[UILabel alloc] init];
[self.contentView addSubview:countView];
self.countView = countView;
}
return self;
}
大家注意到了吧, 就是我們的控件的創(chuàng)建并沒有設(shè)置我們每一個子控件的frame
原因:
首先, 我們子控件的創(chuàng)建一定需要我們這個HeaderView
的frame
其次, 其實由于我們的這個HeaderView
在init....方法中, 他一開始初始化時沒有任何值的, 所以當(dāng)我們在這個方法里面設(shè)置我們子控件的frame(需要調(diào)用HeaderView的frame)時 , 是沒有值的, 所以蘋果公司為我們提供了一個方法: - (void)layoutSubviews
而且這個方法是不需要我們自己刻意調(diào)用的而是 **當(dāng)一個控件的frame發(fā)生改變的時候就會調(diào)用. **
所以我們一般是在這個方法中設(shè)置我們子控件的frame:
- (void)layoutSubviews
{
#warning 一定要調(diào)用super的方法
[super layoutSubviews];
// 1.設(shè)置按鈕的frame
self.nameView.frame = self.bounds;
// 2.設(shè)置好友數(shù)的frame
CGFloat countY = 0;
CGFloat countH = self.frame.size.height;
CGFloat countW = 150;
CGFloat countX = self.frame.size.width - 10 - countW;
self.countView.frame = CGRectMake(countX, countY, countW, countH);
}
而在控制器中我們是這么調(diào)用這個方法來創(chuàng)建headerView的:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// 1.創(chuàng)建頭部控件
GJHeaderView *header = [GJHeaderView headerViewWithTableView:tableView];
// 2.給header設(shè)置數(shù)據(jù)(給header傳遞模型)
return header;
}
好友列表 -04設(shè)置頭部數(shù)據(jù)
關(guān)于我們添加頭部控件的數(shù)據(jù) , 其實也是很簡單的, 只是注意的是, 我們的關(guān)于頭部控件的子控件的排放問題
當(dāng)我們直接將我們的子控件添加到我們的頭部控件時, 他會居中擺放, 這樣不是我們想要的結(jié)果, 所以我們就要自己設(shè)置
// 設(shè)置按鈕的內(nèi)容左對齊 nameView.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
這個方法就是 直接設(shè)置我們每一個子控件都要左對齊, 然后, 我們的控件也不能直接擺放到里面, 最好是有一些空隙 所以這就需要另一個方法:
// 設(shè)置按鈕的內(nèi)邊距
// nameView.imageEdgeInsets
nameView.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
nameView.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
所以這樣我們的上面一個頭部標(biāo)題和箭頭都解決了, 剩下的就是關(guān)于右邊的好友數(shù)了
- (void)setGroup:(GJFriendGroup *)group
{
_group = group;
// 1.設(shè)置按鈕文字(組名)
[self.nameView setTitle:group.name forState:UIControlStateNormal];
// 2.設(shè)置好友數(shù)(在線數(shù)/總數(shù))
self.countView.text = [NSString stringWithFormat:@"%d/%d", group.online, group.friends.count];
}
這樣就完成了我們的頭部數(shù)據(jù)的添加, 具體的代碼如下:
// 添加子控件
// 1.添加按鈕
UIButton *nameView = [UIButton buttonWithType:UIButtonTypeCustom];
// 背景圖片
[nameView setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg"] forState:UIControlStateNormal];
[nameView setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg_highlighted"] forState:UIControlStateHighlighted];
// 設(shè)置按鈕內(nèi)部的左邊箭頭圖片
[nameView setImage:[UIImage imageNamed:@"buddy_header_arrow"] forState:UIControlStateNormal];
[nameView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 設(shè)置按鈕的內(nèi)容左對齊
nameView.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
// 設(shè)置按鈕的內(nèi)邊距
// nameView.imageEdgeInsets
nameView.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
nameView.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
[self.contentView addSubview:nameView];
self.nameView = nameView;
// 2.添加好友數(shù)
UILabel *countView = [[UILabel alloc] init];
countView.textAlignment = NSTextAlignmentRight;
countView.textColor = [UIColor grayColor];
[self.contentView addSubview:countView];
self.countView = countView;
然而設(shè)置我們的好友數(shù), 就是我們上面的一個方法.
還有一點就是關(guān)于我們的封裝:
在我們的上面的控制器牽扯到一個cell的創(chuàng)建:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
其實我們完全可以自定義一個cell來將原先的創(chuàng)建cell的過程封裝起來
該自定義cell的名稱就叫做: friendCell
注意的是, 我們的這個cell是需要我們的friend這個模型數(shù)據(jù)的, 因為我們這個cell在創(chuàng)建的時候需要的數(shù)據(jù)都是從這個模型中取出來的
**friendCell.h文件: **
#import <UIKit/UIKit.h>
@class GJFriend;
@interface GJFriendCell : UITableViewCell
+ (instancetype)cellWithTableView:(UITableView *)tableView;
// friend是C++的關(guān)鍵字,不能用friend作為屬性名
@property (nonatomic, strong) GJFriend *friendData;
@end
friendCell.m文件
#import "GJFriendCell.h"
#import "GJFriend.h"
@implementation GJFriendCell
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"friend";
GJFriendCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[GJFriendCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
return cell;
}
- (void)setFriendData:(MJFriend *)friendData
{
_friendData = friendData;
self.imageView.image = [UIImage imageNamed:friendData.icon];
self.textLabel.text = friendData.name;
self.detailTextLabel.text = friendData.intro;
}
@end
這個就是我們封裝的代碼:
好友列表 -05展開合并好友列表
其實, 我們可以換一種思路去想如果讓我們的好友列表合并, 原來模式下, 我們的好友列表是自動展開的, 所以我們在想如何將他合并
其實我們可以這樣做:
當(dāng)我們的好友列表展開時 , 我們就不動 , 一旦需要合并的時候, 我們將我們的friend模型改成0, 然后在重新刷新表格, 這樣就做到我們想要的那個效果
**1. 我們要監(jiān)聽按鈕的點擊: **
[nameView addTarget:self action:@selector(nameViewClick) forControlEvents:UIControlEventTouchUpInside];
這樣當(dāng)我們的按鈕被點擊的時候, 它就會調(diào)用我們的nameViewClick
這個方法, 然后我們只需要在這個方法里面完成展開合并好友列表的這個功能
2 . 思考: 如何使我們的列表里面的好友有時候顯示, 有時候不顯示???
很簡單, 我們利用BOOL類型判斷我們的列表是否展開, 如果是YES(表示是展開的)就顯示好友, 如果是NO就在這個方法里面返回一個0, 就代表著, 所有組的好友數(shù)為0:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
如何寫: 利用三目運算:
return (group.isOpened ? group.friends.count : 0);
3 . 在我們friendGroup里面添加一個BOOL類型的屬性: (是否要顯示好友列表)
/**
* 標(biāo)識這組是否需要展開, YES : 展開 , NO : 關(guān)閉
*/
@property (nonatomic, assign, getter = isOpened) BOOL opened;
4 . 在headerView的nameViewClick
方法中按位取反
意思就是每當(dāng)我們點擊頭部控件的時候, 如果這個isOpened是YES就取反為NO, 這樣就可以完成我們第一次點擊頭部控件, 為展開, 再點一次isOpened變?yōu)镹O了, 就合并, 然后又是展開
// 1.修改組模型的標(biāo)記(狀態(tài)取反)
self.group.opened = !self.group.isOpened;
**5. 刷新表格: **
對于我們整個的文件來說, 誰最適合刷新表格???當(dāng)然是控制器了, 所以我們當(dāng)我們的按鈕被點擊的時候, 我們要通知我們的控制器, 按鈕被人點了, 所以我們就需要用到** 代理**
先寫好協(xié)議:
@protocol GJHeaderViewDelegate <NSObject>
@optional
- (void)headerViewDidClickedNameView:(GJHeaderView *)headerView;
@end
然后再創(chuàng)一個協(xié)議屬性:
@property (nonatomic, weak) id<MJHeaderViewDelegate> delegate;
- (void)nameViewClick
{
// 1.修改組模型的標(biāo)記(狀態(tài)取反)
self.group.opened = !self.group.isOpened;
// 2.刷新表格
if ([self.delegate respondsToSelector:@selector(headerViewDidClickedNameView:)]) {
[self.delegate headerViewDidClickedNameView:self];
}
其次 , 在這個方法中告訴我么你的代理, 按鈕被點擊了, 把自己傳給代理, 告訴代理是自己被人點擊了
最后, 控制器遵守協(xié)議,成為我們頭部控件的代理 然后實現(xiàn)協(xié)議方法:
@interface GJViewController () <GJHeaderViewDelegate>
實現(xiàn)方法:
#pragma mark - headerView的代理方法
/**
* 點擊了headerView上面的名字按鈕時就會調(diào)用
*/
- (void)headerViewDidClickedNameView:(GJHeaderView *)headerView
{
[self.tableView reloadData];
}
**6 關(guān)于左邊下箭頭的旋轉(zhuǎn) **
當(dāng)我們的列表展開后, 我們的小箭頭應(yīng)該向下旋轉(zhuǎn)90°, 但是,首先提示我們的這個工能是不可以在nameClick這個方法中寫的, 因為, 每當(dāng)我們調(diào)用這個方法時, 我們的表格就會重新刷新一次, 所以就算我們在這里修改, 也無濟于事
所以, 蘋果官方又給我們推薦了一個新的方法, 該方法不需要我們?nèi)フ{(diào)用, 他會在合適的時間自動調(diào)用
/**
* 當(dāng)一個控件被添加到父控件中就會調(diào)用
*/
- (void)didMoveToSuperview
{
if (self.group.opened) {
self.nameView.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
} else {
self.nameView.imageView.transform = CGAffineTransformMakeRotation(0);
}
}
注釋寫的就是我們這個方法是在什么時候調(diào)用的
**但是: **
就算我們這樣弄好了, 圖片也可以旋轉(zhuǎn)了, 但是效果不是很好, 圖片會變形, 因為我們的UIImage
會自動拉伸, 以及裁剪, 所以我們需要在設(shè)置圖片的代碼里添加這樣兩句代碼:
// 設(shè)置按鈕內(nèi)部的imageView的內(nèi)容模式為居中
nameView.imageView.contentMode = UIViewContentModeCenter;
// 超出邊框的內(nèi)容不需要裁剪
nameView.imageView.clipsToBounds = NO;
這樣, 我們的這個程序, 就到了最后一步, 設(shè)置顯示會員:
好友列表- 06 標(biāo)識會員
很簡單, 在我們的friendCell里面直接來個三目運算就可以了:
self.textLabel.textColor = friendData.isVip ? [UIColor redColor] : [UIColor blackColor];
APP管理
現(xiàn)在我們又回到我們一開始的那一項, 單元格, 我們刪掉原來的UIView, 重新拖入一個UITableView, 他會自動在上面生成一個單元格(Prototype Cells
), 上面我們介紹的是靜態(tài)單元格, 現(xiàn)在我們要利用他做一個動態(tài)的單元格 (需要寫代碼的) 利用動態(tài)單元格做一個以前做過的應(yīng)用管理 :
效果展示:
然后, 由于我們系統(tǒng)自帶的Prototype Cells
沒有自帶我們的這種格式,而且, 在系統(tǒng)中原有的格式中 ,我們再添加任何子控件, 所以我們只有選擇, 自定義:
其次, 原來的那個數(shù)據(jù)轉(zhuǎn)模型代碼我就不寫了, 直接寫一個appCell
其實, 我們可以將我們的這個動態(tài)單元格看成一個Xib 只是他要比Xib方便的多
對于我們在創(chuàng)建一個新的cell時候 , 我們一般會給cell一個標(biāo)識, 這樣方便我們以后從緩存池中找, 所以, 同樣的我們的動態(tài)單元格, 也有一個標(biāo)識:
然后我們在控制器里面創(chuàng)建的代碼就變成這樣了:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
GJAppCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
cell.delegate = self;
cell.app = self.apps[indexPath.row];
return cell;
}
對比以前的代碼, 下奶在看看現(xiàn)在的代碼:
GJAppCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
只要這一句代碼就完成了, 以前我們還需要用一個判斷語句來判斷緩存池中是否有cell, 現(xiàn)在不需要了
而且, 可以發(fā)現(xiàn), 一個cell的創(chuàng)建是只需要表示(Identifier), 那么我們完全可以多創(chuàng)建幾個Prototype Cells 只需要寫上不同的標(biāo)識而已, 例如:
利用代理, 顯示已下載, 和彈出框框
首先我們設(shè)置代理的方法已經(jīng)說過很多次了, 今天就不說了,
- 首先, 我們需要得到我們的下載按鈕(同時也要設(shè)置一個參數(shù), 用來告訴他的代理是誰被點擊了) , 利用方法監(jiān)聽他的點擊事件:
- (IBAction)downloadClick:(UIButton *)btn;
/**
* 點擊了下載按鈕
*/
- (IBAction)downloadClick:(UIButton *)btn {
// 1.讓按鈕失效
self.app.downloaded = YES;
btn.enabled = NO;
// 2.通知代理
if ([self.delegate respondsToSelector:@selector(appCellDidClickedDownloadBtn:)]) {
[self.delegate appCellDidClickedDownloadBtn:self];
}
}
- 然后讓我們的控制器成為我們的代理
* 遵守協(xié)議: <GJAppCellDelegate>
*實現(xiàn)方法:
#pragma mark - cell的代理方法
- (void)appCellDidClickedDownloadBtn:(MJAppCell *)cell
{
// 1.添加標(biāo)簽
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:@"成功下載%@", cell.app.name];
label.font = [UIFont systemFontOfSize:12];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor blackColor];
label.frame = CGRectMake(0, 0, 150, 25);
label.center = CGPointMake(160, 240);
label.alpha = 0.0;
label.layer.cornerRadius = 5;
label.clipsToBounds = YES;
[self.view addSubview:label];
// 2.動畫
[UIView animateWithDuration:0.5 animations:^{
label.alpha = 0.5;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 delay:0.5 options:UIViewAnimationOptionCurveLinear animations:^{
label.alpha = 0.0;
} completion:^(BOOL finished) {
[label removeFromSuperview];
}];
}];
}
但是, 注意的是, 一旦我們讓現(xiàn)在按鈕失效, 在后面的當(dāng)我們的cell從緩存池去的時候, 我們的cell中的下載按鈕還是失效的, 所以我們的解決方法就是在重寫cell的setter方法里面加上這么一句代碼:
// 覆蓋按鈕的狀態(tài)
self.downloadView.enabled = (self.app.isDownloaded == NO);
至于這個應(yīng)用的數(shù)組轉(zhuǎn)模型, 數(shù)據(jù)的加載等等我就不寫了, 和我們以前的代碼一樣的