Weex SDK中提供了下拉刷新和上拉加载的组件,但是功能比较单一,不能做到跟手滑动操作,还会导致weex刷新和原生刷新不统一的问题,所以最好的操作是扩展导出原生的刷新组件。
refresh
组件是WeexSDK官方提供的,但是不好在这个组件内进行扩展,为了方便前端的使用和客户端的扩展,最终在list组件的基础上进行扩展,因为几乎所有的刷新和加载都是基于list组件的。
iOS端
为了方便扩展,我选择继承官方的WXListComponent
类,先整体了解一下整个类中的代码
@interface MWSListComponent ()
@property (nonatomic, strong) MJRefreshGifHeader *refreshHeader;
@property (nonatomic, strong) MJRefreshAutoFooter *refreshFooter;
@property (nonatomic, assign) BOOL refresh; /**< 是否开启下拉刷新 */
@property (nonatomic, assign) BOOL loading; /**< 是否开启上拉加载 */
@property (nonatomic, assign) BOOL showLoading; /**< 控制loading是否显示 */
@property (nonatomic, assign) BOOL refreshEvent;
@property (nonatomic, assign) BOOL loadingEvent;
@end
@implementation MWSListComponent
WX_EXPORT_METHOD(@selector(endRefreshing));
WX_EXPORT_METHOD(@selector(endLoading));
WX_EXPORT_METHOD(@selector(noticeNoMoreData));
- (void)dealloc
{
_refreshFooter = nil;
_refreshHeader = nil;
}
- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
_refresh = [WXConvert BOOL:attributes[@"refresh"]];
_loading = [WXConvert BOOL:attributes[@"loading"]];
_showLoading = [attributes.allKeys containsObject:@"showLoading"] ? [WXConvert BOOL:attributes[@"showLoading"]] : YES;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self _updateRefreshHeader];
[self _updateRefreshFooter];
[self _updateLoadingState];
}
- (void)addEvent:(NSString *)eventName
{
[super addEvent:eventName];
if ([eventName isEqualToString:@"refresh"]) {
_refreshEvent = YES;
}
if ([eventName isEqualToString:@"loading"]) {
_loadingEvent = YES;
}
}
- (void)removeEvent:(NSString *)eventName
{
[super removeEvent:eventName];
if ([eventName isEqualToString:@"refresh"]) {
_refreshEvent = NO;
}
if ([eventName isEqualToString:@"loading"]) {
_loadingEvent = NO;
}
}
- (void)updateAttributes:(NSDictionary *)attributes
{
[super updateAttributes:attributes];
if ([attributes.allKeys containsObject:@"refresh"]) {
_refresh = [WXConvert BOOL:attributes[@"refresh"]];
[self _updateRefreshHeader];
}
if ([attributes.allKeys containsObject:@"loading"]) {
_loading = [WXConvert BOOL:attributes[@"loading"]];
[self _updateRefreshFooter];
}
if ([attributes.allKeys containsObject:@"showLoading"]) {
_showLoading = [WXConvert BOOL:attributes[@"showLoading"]];
[self _updateLoadingState];
}
}
#pragma mark - Private Method
- (void)_addRefreshHeader
{
if (!_refreshHeader) {
__weak typeof(self) weakSelf = self;
_refreshHeader = [MJRefreshGifHeader headerWithRefreshingBlock:^{
[weakSelf refreshData];
}];
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.header = _refreshHeader;
}
}
- (void)refreshData
{
if (_refreshEvent) {
[self fireEvent:@"refresh" params:nil];
}
}
- (void)_removeRefreshHeader
{
if (_refreshHeader) {
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.header = nil;
_refreshHeader = nil;
}
}
- (void)_addRefreshFooter
{
if (!_refreshFooter) {
__weak typeof(self) weakSelf = self;
_refreshFooter = [MDRefreshAutoFooter footerWithRefreshingBlock:^{
[weakSelf loadingData];
}];
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.footer = _refreshFooter;
[self _updateLoadingState];
}
}
- (void)loadingData
{
if (_loadingEvent) {
[self fireEvent:@"loading" params:nil];
}
}
- (void)_removeRefreshFooter
{
if (_refreshFooter) {
UIScrollView *scrollView = (UIScrollView *)self.view;
scrollView.footer = nil;
_refreshFooter = nil;
}
}
- (void)_updateRefreshHeader
{
if (_refresh) {
[self _addRefreshHeader];
}
else {
[self _removeRefreshHeader];
}
}
- (void)_updateRefreshFooter
{
if (_loading) {
[self _addRefreshFooter];
}
else {
[self _removeRefreshFooter];
}
}
- (void)_updateLoadingState
{
self.refreshFooter.hidden = !_showLoading;
}
#pragma mark - JS call method
- (void)endRefreshing
{
if (_refreshHeader) {
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView.header endRefreshing];
[UIView animateWithDuration:0.4 animations:^{ [(UIScrollView *)self.view setContentOffset:CGPointZero]; }];
}
}
- (void)endLoading
{
if (_refreshFooter) {
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView.footer endRefreshing];
scrollView.footer.hidden = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
scrollView.footer.hidden = NO;
});
}
}
- (void)noticeNoMoreData
{
if (_refreshFooter) {
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView.footer noticeNoMoreData];
}
}
@end
refresh {boolean}
: 可选值为true/false
,默认是false
。此值决定 list 是否开启下拉刷新功能。
loading {boolean}
:可选值为true/false
,默认是false
。此值决定 list 是否开启上拉加载功能。
下拉刷新的时候,会触发list
组件的refresh
方法,上拉加载的时候,会触发list
组件的loading
方法。当数据返回的时候,我们需要手动去关闭刷新或者加载,因为数据什么时候回来只有weex端才知道,所以扩展了一下几个方法:
WX_EXPORT_METHOD(@selector(endRefreshing)); // 停止刷新
- (void)endRefreshing
{
if (_refreshHeader) {
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView.header endRefreshing];
// weex在调用停止刷新后,scroll并不会回到顶部,需要我们手动进行设置一下。
[UIView animateWithDuration:0.4 animations:^{ [(UIScrollView *)self.view setContentOffset:CGPointZero]; }];
}
}
WX_EXPORT_METHOD(@selector(endLoading)); // 停止上拉加载
- (void)endLoading
{
if (_refreshFooter) {
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView.footer endRefreshing];
scrollView.footer.hidden = YES;
// 此处的处理因为数据返回时候,weex的render需要一定的时间,所有绝大部分时候,是先看到footer,然后cell才会一个一个进行渲染,所以此处先隐藏footer,0.25s之后再显示
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
scrollView.footer.hidden = NO;
});
}
}
当没有更多数据的时候,我们可以调用这个方法进行设置
WX_EXPORT_METHOD(@selector(noticeNoMoreData)); // 提示没有更多数据了
- (void)noticeNoMoreData
{
if (_refreshFooter) {
UIScrollView *scrollView = (UIScrollView *)self.view;
[scrollView.footer noticeNoMoreData];
}
}
前端
在设置样式的模块代码如下
ref
为了拿到list组件,上述代码开启了上拉加载和下拉刷新的功能,下面是script模块的代码
通过上述操作,就可以将原生的代码桥接到weex中使用啦。