文档章节

PullToRefresh的学习研究(一)

j
 jscoolstar
发布于 2015/06/16 15:45
字数 2610
阅读 119
收藏 1
点赞 0
评论 0

其实以前在看和学习源码的时候都是在源码上做关键的备注,后面看的时候能慢慢回忆起来,但是对比较复杂的逻辑,光靠备注还是不行,即使能回忆起来,也是十分耗费时间的。所以感觉还是要把学习时的逻辑、感受都记录一下,这样在很久以后再看的时候,能迅速记起。另外写成博客,势必有人会来看,也许是新手,那么很荣幸能帮助大家。也许是大神,那么很庆幸您的到来,留下建议和教诲。我想博客就应该是这样需要交流的,不能完全只是记录。


好了,作为处女的开篇博客,难免开局要啰嗦一下,另外。。。我不太会写博客,到这会我还没找到怎么上传zip源码包,所以大家直接搜PullToRefresh来搜索下载吧。。。还有就是,我现在是看了一天但并没看完,说起来,我自己的技术也很薄弱,写博客并不是以师者的姿态来写的,所以望大家不要乱喷,而打消个人的积极性。写的不好不清楚大家可以在评论里继续讨论,我也可以按大家的指点继续完善。本来博客写出来更多的还是为了自己的学习和成长,顺便能帮到人那是最好的了。并不是为了喷子服务的:)


功能特点与大体原理:

1、能实现listview/gridview/scrollview/webview../hscrollview/viewpager等等的下(左)拉刷新、上(右)拉加载更多。

        目前我看到的源码,是能支持多种view的刷新的。甚至你可以参考代码自定义一个Linearlayout都可以进行同样的UI体验            它支持纵向和横向。  

2、多种模式:可以只使用下拉或上拉或both或禁用

3、有一个特效类似微信朋友圈的刷新,即下拉的时候有个小图标会按下拉的程度慢慢旋转,松手时会快速旋转,加载完成后,消失。我觉得可以叫它“发条效果”。。。呵呵,下拉的时候就是在上紧,松手就呼啦啦的转

4、目前得到的粗粗的原理:。。。。继承LinearLayout,基类是abstract的,listview为例,有一个PullToRefreshListview继承,并通过abstract getPullToRefreshScrollDirection()方式,来定位自己是 横向的还是纵向的,这里就是纵向了。原理就是add了一个listview(index是-1),然后add了一个header(index是0)和一个footer(index是-1);然后通过padding属性,定位到listview全部显示,header和footer上下隐藏。定位好后的操作,比如下拉等等,就是主要通过scrollto()来实现,松手弹回也是利用一个加速器和一个runnable来多次执行scrollto来实现。(这是当前看到的原理,我还要继续读继续写,后面如果发现错了再来改过,如果有大神熟悉它的话,发现我的错误,还望不吝赐教)。


源码解读开始吧

1、构造方法:

    public PullToRefreshBase(Context context) {
        super(context);
        init(context, null);
    }

    public PullToRefreshBase(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    主要就是init(context, attrs);好处就是attrs传了进去,这里是可以借鉴的地方(高手看到我这么说,不要嘘我,很多新手都注意不到这里的)。

  init中第一步就是取方向:switch (getPullToRefreshScrollDirection()) {
        case HORIZONTAL:
            setOrientation(LinearLayout.HORIZONTAL);
            break;
        case VERTICAL:
        default:
            setOrientation(LinearLayout.VERTICAL);
            break;
        }

getPullToRefreshScrollDirection()是由子类来实现的,也就是说如果要实现说的可以让listview/gridview等等实现下拉,要分别实现对应的子类,感觉上是有点累赘,但是方便进行特定化的实现。有些类似的组件是你中间放啥都可以,尤其有些原理都类似的组件,他直接在一个类里,去获得你中间放的是adapterview还是scrollview等等,然后touchevent里就开始了各种判断。并不是说那样的源码不好,只是单从我来看,那么长的源码,一下子我就恐惧了。

setGravity(Gravity.CENTER);、这里我没读懂,为啥一定要居中。在这里要强调另外一点,他重写了addview()

@Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (DEBUG) {
            Log.d(LOG_TAG, "addView: " + child.getClass().getSimpleName());
        }

        final T refreshableView = getRefreshableView();

        if (refreshableView instanceof ViewGroup) {
            ((ViewGroup) refreshableView).addView(child, index, params);
        } else {
            throw new UnsupportedOperationException(
                    "Refreshable View is not a ViewGroup so can't addView");
        }
    }可以看到当调用自定义类去addview的时候,其实他会往refreshableView上去添加,如果你不是ViewGroup类型的,是无法添加的


ViewConfiguration config = ViewConfiguration.get(context);
        mTouchSlop = config.getScaledTouchSlop();// 获取最小的滑动距离,手指要移动这个距离或以上才会被认为发生了滑动



TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.PullToRefresh);,拿到属性之后,基类自己记录了一些必要属性,然后感觉下面就是经典了。

mRefreshableView = createRefreshableView(context, attrs);
        addRefreshableView(context, mRefreshableView);  createRfreshableView(context,attrs)这个是要由子类实现的方法,即子类来决定自己是实现listview还是gridview等等。另外这里传入了attrs。也就是说,虽然自己继承的是LinearLayout,但是你的子类可以起名为xxxxxxListView,然后xml中就把它当作listview,可以设置drawableCache等Listview的属性,然后子类的createRfreshableView就可以拿到attrs来构建自己的listview了,然后再实现什么setAdater(){mListView.setAdapter()}等listview的常用方法,通过自己这个桥梁直接调用listview的对应方法。这样使用者就可以完全当作listview来使用了。基类提供了@Override
    public final T getRefreshableView() {
        return mRefreshableView;
    },使用者也可以直接get到之后,然后直接转成对应的view,就可以直接进行操作了,比如这套代码中的xxxxxlistview虽然有setAdapter,onItemClickListerner等常用设置,但是没有addHeader,addFooter等等。毕竟这个例子中的下拉后的顶部hearder并不是真正的listview的header,当然也没有验证加了header之后能不能行,后面还要继续看,标红一下回头看。

    接下来:mHeaderLayout = createLoadingLayout(context, Mode.PULL_FROM_START, a);
        mFooterLayout = createLoadingLayout(context, Mode.PULL_FROM_END, a);、、createLoadingLayout这里是根据xml配置rotatol或flip,即常见的箭头反转动画或者是微信拿样的旋转发条动画。例子中是写死了两种LoadingLayout,xml只能配置1个,目前不知道能否在子类 重写,毕竟基类中只是操作了mHeaderLayout的几种刷新周期中的属性,比如onFresh,over啊等等,然后比如RotatotLoadingLayout会在onFresh啊等等周期方法里去setLable以展示信息,或者旋转或反转动画,所以应该是可以自定义继承LoadingLayout的,这里也标红,后面再看。


        init的后面执行了handleStyledAttributes(a);//这里就是说基类把一些必要的属性都用掉了,需要额外使用的属性,子类通过这个abstract方法去解析和实现
        a.recycle();

        最后执行了updateUIForMode()方法,内部

        if (mMode.showHeaderLoadingLayout()) {//这个方法其实就是一开始说的配置的model,如果是fromstart或者是both,就返回true,代码翻译成中文就是,如果用户设置的加载模式是顶部下拉刷新,或者both(上拉下拉都可以),那么就返回true就会add这个view
            addViewInternal(mHeaderLayout, 0, lp);
        }

     add headerlayout和footlayout之后,执行了refreshLoadingViewsSize();这里面就是计算header(一说header,footer就包含了,后面只用header说话,也只用listview说话,后面研究子类的时候再单独用相应的子view来说)的高度,以及padding,这里就是最亮的那个点了。这里在赘述下原理,即listview和自己(LinearLayout)一样高,header只有0.6倍的高度,然后自己的paddingtop是-0.6被高度。正好把header顶出自己的显示区域,然后根据子类的临界值判断,使用srcollto方法来让header滚动出来或滑动回去。(这里也是我担心的listview的真正header的问题,因源码中并未涉及到,所以在判断临界值的时候,有head和无head是一样的情况还是不通的情况呢?后面再看)


protected final void refreshLoadingViewsSize() {
        final int maximumPullScroll = (int) (getMaximumPullScroll() * 1.2f);

        int pLeft = getPaddingLeft();
        int pTop = getPaddingTop();
        int pRight = getPaddingRight();
        int pBottom = getPaddingBottom();

        switch (getPullToRefreshScrollDirection()) {
        case HORIZONTAL:
            if (mMode.showHeaderLoadingLayout()) {
                mHeaderLayout.setWidth(maximumPullScroll);
                pLeft = -maximumPullScroll;
            } else {
                pLeft = 0;
            }

            if (mMode.showFooterLoadingLayout()) {
                mFooterLayout.setWidth(maximumPullScroll);
                pRight = -maximumPullScroll;
            } else {
                pRight = 0;
            }
            break;

        case VERTICAL:
            if (mMode.showHeaderLoadingLayout()) {
                mHeaderLayout.setHeight(maximumPullScroll);
                pTop = -maximumPullScroll;
            } else {
                pTop = 0;
            }

            if (mMode.showFooterLoadingLayout()) {
                mFooterLayout.setHeight(maximumPullScroll);
                pBottom = -maximumPullScroll;
            } else {
                pBottom = 0;
            }
            break;
        }

        if (DEBUG) {
            Log.d(LOG_TAG, String.format(
                    "Setting Padding. L: %d, T: %d, R: %d, B: %d", pLeft, pTop,
                    pRight, pBottom));
        }
        setPadding(pLeft, pTop, pRight, pBottom);


与其相映的一个方法protected final void refreshRefreshableViewSize(int width, int height) {
        // We need to set the Height of the Refreshable View to the same as
        // this layout
        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mRefreshableViewWrapper
                .getLayoutParams();

        switch (getPullToRefreshScrollDirection()) {
        case HORIZONTAL:
            if (lp.width != width) {
                lp.width = width;
                mRefreshableViewWrapper.requestLayout();
            }
            break;
        case VERTICAL:
            if (lp.height != height) {
                lp.height = height;
                mRefreshableViewWrapper.requestLayout();
            }
            break;
        }
    }这个方法就是把refreshview整的和参数的w/h相等。额源码的refreshableview并不是加载自己上的,而是加在了mRefreshableViewWrapper(FrameLayout)之上,又把mRefreshableViewWrapper加在了自己上,然后是fillparent加上去的,也就是说以后mRefreshableViewWrapper有多大,refreshview就有多大。而这个方法在基类只有一个地方那个使用了:@Override
    protected final void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (DEBUG) {
            Log.d(LOG_TAG, String.format("onSizeChanged. W: %d, H: %d", w, h));
        }

        super.onSizeChanged(w, h, oldw, oldh);

        // We need to update the header/footer when our size changes
        refreshLoadingViewsSize();

        // Update the Refreshable View layout
        refreshRefreshableViewSize(w, h);

        /**
         * As we're currently in a Layout Pass, we need to schedule another one
         * to layout any changes we've made here
         */
        post(new Runnable() {
            @Override
            public void run() {
                requestLayout();
            }
        });
    }

其实代码都是人写的,不同的人读就可能挑刺,比如我觉得为啥  refreshLoadingViewsSize();不用传w,h而是方法内部用getWidth等拿到之后再乘0.6(/2*1.2),而refreshRefreshableViewSize(w, h);方法就要传w,h进去呢?



到这里,基本上用户在xml配置好之后,源码的第一步解析就到此为止了。该解析的都解析了,mFreshableView是啥东西也知道,也add了。接下来就是onTouch事件了,也是实现的主逻辑了,接下来就边看边研究边写了。另外本篇的疑问,大家也都要有印象,后面会慢慢的填坑。。



© 著作权归作者所有

共有 人打赏支持
j
粉丝 0
博文 3
码字总数 6889
作品 0
海淀
程序员
腾讯 AI Lab 主任张潼博士:机器学习里的优化问题

雷锋网 AI 科技评论按,日前,在由上海财经大学交叉科学研究院(RIIS)主办,杉数科技有限公司协办的「现代运筹学发展讨论会」上,腾讯 AI Lab(腾讯人工智能实验室)主任张潼博士发表了精彩...

思颖
01/10
0
0
智能卡开发的相关总结

1.EMV技术学习和研究(一)开篇(http://blog.csdn.net/xuture/article/details/9208259) 2.EMV技术学习和研究(二)应用选择(http://blog.csdn.net/xuture/article/details/9250067) 3.EMV技......

IT追寻者
2016/09/08
4
0
学习东西总结:

一.JAVA 基础 1、JAVA并发编程 2.JAVA NIO系列 3.JVM深入了解 4.JAVA性能优化相关研究 5.JAVA7,8新特性 二、数据库 1.数据库优化研究 2.MYSQL高可用性研究 3.MYSQL源码研究 三、网络 1.HTTP...

QH_C
2016/03/25
5
0
Android-PullToRefresh使用

接上篇,很多人看到有好源码,但是在使用时碰到了问题。在此简单介绍一下,希望能够对那些不会的童鞋们有所帮助。 首先下载源码,源码地址:https://github.com/chrisbanes/Android-PullToR...

崔同亮
2013/10/23
0
0
跟风Google Brain,Facebook AI研究机构启动见习项目

2015年,Google Brain公布了其帮助机器学习研究者进阶的见习项目,研究内容灵活、薪资福利又高、发展机会应有尽有,瞬间吸引了大量的申请者,其中甚至还有Node.js之父Ryan Dahl,AI科技大本营...

dqcfkyqdxym3f8rb0
2017/12/06
0
0
“谷歌大脑”年度官方总结:9个基础方向研究、6大具体领域成果定义谷歌 AI 进展

年度订阅用户可加入科技英语学习社区,每周科技英语直播讲堂,详情查看“阅读原文” 作为谷歌 AI 帝国的重要部分,谷歌大脑团队一直致力于通过研究和系统工程来推动人工智能领域的发展。去年...

DeepTech深科技
01/14
0
0
这些人你还不认识?!一文带你有姿势地侃深度学习大佬

大数据文摘作品 编译:余志文,笪洁琼,钱天培 近几年间,深度学习的兴起造就了一批超级巨星。一向在学术界默默无闻的大佬们,相继成为了业界的领头人,而他们的声名也再从业界一路传至大众耳...

uwr44uouqcnsuqb60zk2
2017/11/03
0
0
京东和斯坦福达成合作 共同推进AI研究

中关村在线消息:美国当地时间11月27日,京东与斯坦福大学人工智能实验室正式达成战略合作关系,启动了京东-斯坦福大学联合人工智能研究计划。此后,京东将会与斯坦福大学人工智能实验室围绕...

张泓杨
2017/11/30
0
0
计算机视觉与图像处理、模式识别、机器学习学科之间的关系

在我的理解里,要实现计算机视觉必须有图像处理的帮助,而图像处理倚仗与模式识别的有效运用,而模式识别是人工智能领域的一个重要分支,人工智能与机器学习密不可分。纵观一切关系,发现计算...

小金子
2014/07/07
0
0
我们听不到的海豚的声音,让AI替我们聆听

硅谷Live / 实地探访 / 热点探秘 / 深度探讨 听,海豚的声音! 科学家们开发出了一套可以监测海豚在水下交谈的算法,其主要依靠的是机器学习的技术。 使用自动的水下传感器,Gulf of Mexico的...

kxyona63ag9zqtxx0
2017/12/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

spring boot中swagger2使用

1.pom.xml中添加 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version>......

说回答
8分钟前
0
0
tomcat虚拟路径的几种配置方法

tomcat虚拟路径的几种配置方法 一般我们都是直接引用webapps下面的web项目,如果我们要部署一个在其它地方的WEB项目,这就要在TOMCAT中设置虚拟路径了,Tomcat的加载web顺序是先加载 $Tomcat_ho...

Helios51
20分钟前
1
0
Mac 安装jupyter notebook的过程

MAC台式机 python:mac下自带Python 2.7.10 1.先升级了pip安装工具:sudo python -m pip install --upgrade --force pip 2.安装setuptools 工具:sudo pip install setuptools==33.1.1 3.安装......

火力全開
25分钟前
0
0
导航守卫解释与例子

“导航”表示路由正在发生改变。 正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。 记住...

tianyawhl
25分钟前
0
0
Java日志框架-logback配置文件多环境日志配置(开发、测试、生产)(原始解决方法)

说明:这种方式应该算是最通用的,原理是通过判断标签实现。 <!-- if-then form --> <if condition="some conditional expression"> <then> ... </then> </if> ......

浮躁的码农
40分钟前
1
0
FTP传输时的两种登录方式和区别

登录方式 匿名登录 用户名为: anonymous。 密码为:任何合法 email 地址。 授权登录 用户名为:用户在远程系统中的用户帐号。 密码为:用户在远程系统中的用户密码。 区别 匿名登录 只能访问...

寰宇01
41分钟前
0
0
plsql developer 配置监听(不安装oracle客户端)

plsql developer 配置监听(不安装oracle客户端)

微小宝
48分钟前
1
0
数据库(分库分表)中间件对比

本人的宗旨就是,能copy的,绝对不手写。 分区:对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm。 根据一定的规则把数据文件(MYD)和索...

奔跑吧代码
52分钟前
2
0
Netty与Reactor模式详解

在学习Reactor模式之前,我们需要对“I/O的四种模型”以及“什么是I/O多路复用”进行简单的介绍,因为Reactor是一个使用了同步非阻塞的I/O多路复用机制的模式。 I/O的四种模型 I/0 操作 主要...

hutaishi
58分钟前
1
0
【2018.07.16学习笔记】【linux高级知识 20.16-20.19】

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析

lgsxp
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部