一直以來(lái)磁椒,客戶端的應(yīng)用中堤瘤,為了提升應(yīng)用的加載等待這段時(shí)間的用戶感知體驗(yàn),各種技術(shù)層出不窮浆熔。其中本辐,尤以菊花圖以及由它衍生各種加載動(dòng)畫(huà)最為突出。
對(duì)于菊花圖我們自不必多說(shuō)医增,現(xiàn)在對(duì)于加載的設(shè)計(jì)體驗(yàn)有了比菊花加載體驗(yàn)更棒的方法慎皱,即大家常看到的Skeleton Screen Loading叶骨,中文叫做骨架屏茫多。
所謂Skeleton Screen Loading,即表示在頁(yè)面完全渲染完成之前忽刽,用戶會(huì)看到一個(gè)占位的樣式天揖,用以描繪了當(dāng)前頁(yè)面的大致框架,加載完成后跪帝,最終骨架屏中各個(gè)占位部分將被真實(shí)的數(shù)據(jù)替換今膊。很多項(xiàng)目中都有相關(guān)的應(yīng)用,如餓了么h5版本伞剑、知乎斑唬、facebook等網(wǎng)站中都有應(yīng)用。 其效果如下圖所示:
iOS
iOS實(shí)現(xiàn)Skeleton效果的第三方庫(kù)有很多,當(dāng)然也可以自己創(chuàng)建一個(gè)恕刘,而骨架屏最核心的就是占位和屬性動(dòng)畫(huà)缤谎。在實(shí)現(xiàn)方面,本文介紹幾種主流的實(shí)現(xiàn)方式:
SkeletonView
實(shí)現(xiàn)原理
對(duì)UIView進(jìn)行擴(kuò)展雪营,增加skeletonable弓千、skeletonLayer等屬性。調(diào)用showSkeleton方法献起,對(duì)屬性skeletonable為true的視圖進(jìn)行遍歷,找到其最上層的镣陕、skeletonable為true的子View谴餐,然后創(chuàng)建skeletonLayer添加到上面,構(gòu)成骨架圖呆抑,動(dòng)效效果亦是在skeletonLayer層岂嗓。需要隱藏效果時(shí),調(diào)用hideSkeleton鹊碍,同樣進(jìn)行遍歷厌殉,移除skeletonLayer。
簡(jiǎn)單的說(shuō)侈咕,在顯示占位的時(shí)候公罕,將tableView的代理設(shè)置為通過(guò)某個(gè)對(duì)象,這個(gè)對(duì)象根據(jù)cell的Idenfier創(chuàng)建cell并添加占位顯示耀销。關(guān)閉顯示占位的時(shí)候楼眷,將代理tableView的代理切回ViewController,正常顯示熊尉。
特點(diǎn)
1罐柳、不需手動(dòng)寫(xiě)占位控件,不需處理圓角等問(wèn)題狰住,占位效果與實(shí)際控件布局一致张吉。
2、缺點(diǎn)是有的控件是自適應(yīng)大小催植,在未獲得數(shù)據(jù)之前肮蛹,控件位置是錯(cuò)誤的,導(dǎo)致占位效果有問(wèn)題查邢。
Somo
同樣是擴(kuò)展UIView蔗崎,添加屬性somoContainer,表示占位視圖的容器視圖扰藕,其中每個(gè)占位區(qū)域都是一個(gè)SomoView缓苛。對(duì)于想要顯示占位效果的View,需實(shí)現(xiàn)協(xié)議,在協(xié)議方法中返回SomoView列表未桥。將這些SomoView添加到somoContainer笔刹,并顯示。
特點(diǎn)
1冬耿、避免了上述自適應(yīng)控件無(wú)數(shù)據(jù)時(shí)大小不正確的問(wèn)題舌菜。
2、需要手工指定每個(gè)占位區(qū)域亦镶,且每個(gè)占位區(qū)域是UIView級(jí)別日月,不是CALayer。
TABAnimated
除此之外缤骨,TABAnimated也是一個(gè)被使用的比較多的爱咬,同樣TABAnimated也是擴(kuò)展的UIView。在ios中集成TABAnimated需要經(jīng)歷以下幾步:
1绊起,Install
pod 'TABAnimated'
2精拟,第二步(可選)
可以選擇在appDelegate的didFinishLaunchingWithOptions方法全局設(shè)置動(dòng)畫(huà)屬性,設(shè)有默認(rèn)屬性虱歪。例如:
// 設(shè)置TABAnimated相關(guān)屬性
[[TABViewAnimated sharedAnimated]initWithAnimatedDuration:0.3 withColor:tab_kBackColor];
3蜂绎,第三步,設(shè)置animatedStyle屬性
在需要?jiǎng)赢?huà)的view上笋鄙,將屬性animatedStyle設(shè)置為T(mén)ABTableViewAnimationStart,不需要?jiǎng)赢?huà)的view不用做額外的操作师枣。
// UIView和UICollectionView枚舉
typedef NS_ENUM(NSInteger,TABViewAnimationStyle) {
? ? TABViewAnimationDefault = 0,? ? ? ? ? ? ? // 默認(rèn),沒(méi)有動(dòng)畫(huà)
? ? TABViewAnimationStart,? ? ? ? ? ? ? ? ? ? // 開(kāi)始動(dòng)畫(huà)
? ? TABViewAnimationRuning,? ? ? ? ? ? ? ? ? ? // 動(dòng)畫(huà)中
? ? TABViewAnimationEnd,? ? ? ? ? ? ? ? ? ? ? // 結(jié)束動(dòng)畫(huà)
? ? TABCollectionViewAnimationStart,? ? ? ? ? // CollectionView 開(kāi)始動(dòng)畫(huà)
? ? TABCollectionViewAnimationRunning,? ? ? ? // CollectionView 動(dòng)畫(huà)中
? ? TABCollectionViewAnimationEnd? ? ? ? ? ? ? // CollectionView 結(jié)束動(dòng)畫(huà)
};
// UITableView枚舉
typedef NS_ENUM(NSInteger,TABViewAnimationStyle) {
? ? TABViewAnimationDefault = 0,? ? // 沒(méi)有動(dòng)畫(huà),默認(rèn)
? ? TABViewAnimationStart,? ? ? ? ? // 開(kāi)始動(dòng)畫(huà)
? ? TABViewAnimationEnd? ? ? ? ? ? // 結(jié)束動(dòng)畫(huà)
};
// UITableView例子
- (UITableView *)mainTV {
? ? if (!_mainTV) {
? ? ? ? _mainTV = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
? ? ? ? _mainTV.animatedStyle = TABTableViewAnimationStart;? // 開(kāi)啟動(dòng)畫(huà)
? ? ? ? _mainTV.delegate = self;
? ? ? ? _mainTV.dataSource = self;
? ? ? ? _mainTV.rowHeight = 100;
? ? ? ? _mainTV.backgroundColor = [UIColor whiteColor];
? ? ? ? _mainTV.estimatedRowHeight = 0;
? ? ? ? _mainTV.estimatedSectionFooterHeight = 0;
? ? ? ? _mainTV.estimatedSectionHeaderHeight = 0;
? ? ? ? _mainTV.separatorStyle = UITableViewCellSeparatorStyleNone;
? ? }
? ? return _mainTV;
}
// UIView例子
- (TestHeadView *)headView {
? ? if (!_headView) {
? ? ? ? _headView = [[TestHeadView alloc]initWithFrame:CGRectMake(0, 0, tab_kScreenWidth, 90)];
? ? ? ? _headView.animatedStyle = TABViewAnimationStart;? //開(kāi)啟動(dòng)畫(huà)
? ? }
? ? return _headView;
}
4局装,第四步
1坛吁、將需要?jiǎng)拥慕M件的屬性loadStyle,設(shè)置為需要的類型铐尚,不需要?jiǎng)拥慕M件不用做額外的操作拨脉;
2、(可選)新增屬性tabViewWidth宣增,其為動(dòng)畫(huà)開(kāi)啟時(shí)該組件的寬度,有較為合理默認(rèn)值玫膀;
typedef enum {
? ? TABViewLoadAnimationDefault = 0, //默認(rèn)沒(méi)有動(dòng)畫(huà)
? ? TABViewLoadAnimationShort,? ? ? //動(dòng)畫(huà)先變短再變長(zhǎng)
? ? TABViewLoadAnimationLong? ? ? ? //動(dòng)畫(huà)先變長(zhǎng)再變短
}TABViewLoadAnimationStyle;? ? ? ? ? //view動(dòng)畫(huà)類型枚舉
{
? ? ? ? UILabel *lab = [[UILabel alloc]init];
? ? ? ? [lab setFont:tab_kFont(15)];
? ? ? ? lab.loadStyle = TABViewLoadAnimationLong;
? ? ? ? lab.tabViewWidth = 100;
? ? ? ? [lab setTextColor:[UIColor blackColor]];
? ? ? ? [lab setText:@""];
? ? ? ? titleLab = lab;
? ? ? ? [self.contentView addSubview:lab];
}
5,第五步
在獲取到數(shù)據(jù)后爹脾,停止動(dòng)畫(huà)帖旨。
//停止動(dòng)畫(huà),并刷新數(shù)據(jù)
_mainTV.animatedStyle = TABTableViewAnimationEnd;
[_mainTV reloadData];
_headView.animatedStyle = TABViewAnimationEnd;
[_headView initWithData:headGame];
示例源碼鏈接:iOS骨架屏示例
Android
在Android中,骨架屏的實(shí)現(xiàn)也后很多的第三方框架灵妨,常見(jiàn)的有以下幾個(gè)庫(kù):
ShimmerRecyclerView
ShimmerRecyclerView是一個(gè)帶有閃光和指示效果的庫(kù)解阅,其運(yùn)行效果如下圖:
源碼地址:https://github.com/sharish/ShimmerRecyclerView
Skeleton
Skeleton也是一個(gè)使用得比較廣泛的庫(kù),它現(xiàn)在使用閃存動(dòng)畫(huà)的內(nèi)存優(yōu)化版本泌霍,因此速度更快货抄,您也可以設(shè)置更大的布局動(dòng)畫(huà)。
項(xiàng)目源碼:https://github.com/ethanhua/Skeleton
spruce-android
Spruce 是一個(gè)輕量級(jí)動(dòng)畫(huà)庫(kù),可幫助編排屏幕上的動(dòng)畫(huà)蟹地,該庫(kù)同時(shí)還支持 iOS积暖。