zoukankan      html  css  js  c++  java
  • mjrefresh源码分析

      最近想自己写个下拉刷新的库,但是始终感觉无从下手,想想总是容易的。原理也很简单,真正要下手写的时候,呵呵。不得不说ios封装得很好,网上可以用的成熟的库也很多,也正是因为如此很多开发者也忽略了很多底层的东西,真正要去说清楚这些库的原理也说不清楚。以至于这两天说想自己写个下拉,蛋疼。。听很多从c++转到其他开发诸如c#或者ios或者java的朋友都说其实这些代码写起来都没有c++或者c写起来爽,不过开发的速度的确是c++没法比的。呵呵答。

      今天研究了下mjrefresh,认真看了一下午,各处打断点,总算是清楚了它的流程。以下细细道来。

    1、mjrefreshbaseview:

    这个类是下拉视图的基类,包含三个对象,提示文字,下拉指示箭头和刷新旋转的视图。当正在拖拽时显示箭头,拖拽达到一定位置箭头转向,并且提示文字更改,松开后旋转动画开启,箭头图片隐藏。这个类的拓展类是mjrefreshfooterview和mjrefreshheaderview,分别表示scrollview的上拉刷新尾和下拉刷新头。

    mjrefreshheaderview来说,mjfreshreshheaderview设置键值监听,监听拖拽过程中contentoffset这个值,通过判断不同拖拽状态下scrollview的值(代码中主要判断了contentoffset和scrollvieworiginalinset两个值)来判断拖拽的状态是正在拖拽,还是拖拽即将完成或者拖拽结束的,并且设置这个时候的状态state(mjrefreshbaseview成员变量,纪录此时uiscrollview拖拽状态)。看代码:

    - (void)adjustStateWithContentOffset
    {
        // 当前的contentOffset
        CGFloat currentOffsetY = self.scrollView.contentOffsetY;
        // 头部控件刚好出现的offsetY
        CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
        
        // 如果是向上滚动到看不见头部控件,直接返回
        if (currentOffsetY >= happenOffsetY) return;
        
        if (self.scrollView.isDragging) {
            // 普通 和 即将刷新 的临界点
            CGFloat normal2pullingOffsetY = happenOffsetY - self.height;
            
            if (self.state == MJRefreshStateNormal && currentOffsetY < normal2pullingOffsetY) {
                // 转为即将刷新状态
                self.state = MJRefreshStatePulling;
            } else if (self.state == MJRefreshStatePulling && currentOffsetY >= normal2pullingOffsetY) {
                // 转为普通状态
                self.state = MJRefreshStateNormal;
            }
        } else if (self.state == MJRefreshStatePulling) {// 即将刷新 && 手松开
            // 开始刷新
            self.state = MJRefreshStateRefreshing;
        }
    }
    

    self.state代表这个contentoffset上一个值的uiscorllview状态,根据uiscrollview的值判断状态后并设置self.state,并修改刷新视图的提示文字,并修改箭头的指向。

    - (void)setState:(MJRefreshState)state
    {
        // 1.一样的就直接返回
        if (self.state == state) return;
        
        // 2.保存旧状态
        MJRefreshState oldState = self.state;
        
        // 3.调用父类方法
        [super setState:state];
        
        // 4.根据状态执行不同的操作
        switch (state) {
            case MJRefreshStateNormal: // 下拉可以刷新
            {
                // 设置文字
                self.statusLabel.text = MJRefreshHeaderPullToRefresh;
                
                // 刷新完毕
                if (MJRefreshStateRefreshing == oldState) {
                    self.arrowImage.transform = CGAffineTransformIdentity;
                    // 保存刷新时间
                    self.lastUpdateTime = [NSDate date];
                    
                    [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{
                        self.scrollView.contentInsetTop = self.scrollViewOriginalInset.top;
                    }];
                } else {
                    // 执行动画
                    [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
                        self.arrowImage.transform = CGAffineTransformIdentity;
                    }];
                }
                break;
            }
                
            case MJRefreshStatePulling: // 松开可立即刷新
            {
                // 设置文字
                self.statusLabel.text = MJRefreshHeaderReleaseToRefresh;
                // 执行动画
                [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
                    self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI);
                }];
                break;
            }
                
            case MJRefreshStateRefreshing: // 正在刷新中
            {
                // 设置文字
                self.statusLabel.text = MJRefreshHeaderRefreshing;
                
                // 执行动画
                [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
                    // 1.增加滚动区域
                    CGFloat top = self.scrollViewOriginalInset.top + self.height;
                    self.scrollView.contentInsetTop = top;
                    
                    // 2.设置滚动位置
                    self.scrollView.contentOffsetY = - top;
                }];
                break;
            }
                
            default:
                break;
        }
    }

    当然,这里面包含了contentinsettop和contentinset的各种值的含义,可以谷歌下。

    2、mjfreshheader ,mjrefreshfooter分别表示下拉头和上拉尾,原理都一样,这里就拿mjrefreshheader解释。mjrefresh里面有两种添加刷新头的方式,一种是指定对象和对象函数,另一种是设置block,其实两者都一样,楼主比较习惯使用第二种,设置block。在使用

    addHeaderWithCallback这个函数后,mj将这个block纪录在mj的headerblock这个成员变量里,在状态为“正在刷新”时,调用这个block。即代码:

        case MJRefreshStateRefreshing:
            {
                // 开始转圈圈
                [self.activityView startAnimating];
                // 隐藏箭头
                self.arrowImage.hidden = YES;
                
                // 回调
                if ([self.beginRefreshingTaget respondsToSelector:self.beginRefreshingAction]) {
                    objc_msgSend(self.beginRefreshingTaget, self.beginRefreshingAction, self);
                }
                
                if (self.beginRefreshingCallback) {
                    self.beginRefreshingCallback();
                }
                break;
            }

    这在父类的setstate里面操作。

      mj大致原理就是这样,通过监听contentoffset并比较contentoffset的新值和旧值来判断scorllview的状态:下拉刷新,正在刷新,松开刷新等,并修改指示箭头,提示文字,在状态为正在刷新时执行相应的操作,大致就是这么个过程,作为个学习笔记,有哪些不正确的,望大家指正。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 连接字符串
    大多云盘都挤兑在了企业级市场
    115能做到100个亿(2016年05月31日),2013营收两亿元
    Dropbox 有哪些鲜为人知的使用技巧?
    Qt 添加外部库文件(四种方法)
    百度全新的ARM架构服务器,一个2U机箱装6台,每台4个3T硬盘,每个机箱共72TB
    EntityFramework中支持BulkInsert扩展
    基于A2DFramework的事件机制实现
    事件机制与消息机制的架构设计区别
    .net平台下socket异步通讯(代码实例)
  • 原文地址:https://www.cnblogs.com/symen/p/4921613.html
Copyright © 2011-2022 走看看