文档章节

PullToRefresh的学习研究(二)

j
 jscoolstar
发布于 2015/06/16 18:39
字数 2250
阅读 27
收藏 0

注意到里面好多类似这样的方法public void setReleaseLabel(CharSequence releaseLabel, Mode mode) {
        getLoadingLayoutProxy(mode.showHeaderLoadingLayout(),
                mode.showFooterLoadingLayout()).setReleaseLabel(releaseLabel);
    },这些都是ILoadingLayout这个接口的方法,看来以前代码设计者是把状态改变时的lable改变写在了基类里,然后子类直接调用方法来改变的。但是现在应该没有什么用了,因为headlayout是继承的LoadingLayout,而LoadingLayout实现了IloadingLayout接口。只需要在自定义的LoadingLayout里实现方法就可以了,基类会在适当的时机调用headlayout.setxxxlabel方法,而不是调用自己的setxxxLable了。全部删除后demo中只有一个ListAct有错误mPullRefreshListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
            @Override
            public void onRefresh(PullToRefreshBase<ListView> refreshView) {
                String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),
                        DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);

                // Update the LastUpdatedLabel
//                refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);
                refreshView.getHeaderLayout().setLastUpdatedLabel(label);
                // Do work to refresh the list here.
                new GetDataTask().execute();
            }
        });

    他在回调中还是使用的refreshView.getLoadingLayoutProxy().这里建议将基类原来的getHeaderLayout()设置为public,这样也为自定义的LoadingLayout做准备,也可以封装一部分常用的属性在基类中,比如setLastUpdatedLabel(label){内部调用headlayout去处理}其实LoadingLayout和基类之间的关系还是有点“深”,后面尝试分离一下,否则这样虽然有 一定灵活度,但其实还是定死了几个东西,比如你即使自定义,也必须有一堆很必须的值,比如updateText,可能有些时候你并不需要它,demo中在不需要的时候,其实只是没有设置text,所以就看不到。但不代表控件本身不存在。我觉得可以在LoadingLayout中只实现state周期必须的几个东西,然后在子类中去设置文本之类的操作,嗯,这些读者们自行考虑吧,其实感觉最重要的是接下来要看的ontouch事件,以及各个子类对临界值的判断。如此则可以写出更多灵活的代码,比如有些app,下拉的时候,从上面出来个发条,但是listview本身并不动,这样的情况使用博文介绍的组件是不能实现的,当然也许可以通过变动来实现,比如上篇说了,是通过padding定位header,然后通过layout自己的scrollto属性来呈现下拉的。那么问题来了,挖掘技术哪家强?。。我去,是想实现listview不动的情况应该怎么办?学会了这里的逻辑,完全可以继承一个RelativeLayout了,或者尝试改变lsitview和hear之间的margain试试?(但线性布局,它会盖着header。。。)感觉应该使用RelativeLayout,这样感觉好一点,方便操控,学着学着就想推倒了。言归正传,先把核心学到手,后面随意优化(其实也不难,使用layout(int top,int left...)来替换掉scrollto就可以了,scrollto在这里其实占了个大便宜而已,带来的劣势也看到了,不方便重叠布局Head)。

大家自行复习  onInterceptTouchEvent 和onTouch吧,或者以后我会补一篇相关文章。总结就是onInterceptTouchEvent从父类向子类传递,遇到true则停止并交由当时的子类来执行onTouch,ontouch开始反传,遇true则自己消耗掉事件及以后的事件。

@Override
    public final boolean onInterceptTouchEvent(MotionEvent event) {

        if (!isPullToRefreshEnabled()) {
            return false;// 如果设置为不能fresh,则不拦截,交给子view处理,即所有touch事件交给了listview,上下的headlayout等也一直invisiable
        }

        final int action = event.getAction();

        if (action == MotionEvent.ACTION_CANCEL
                || action == MotionEvent.ACTION_UP) {
            mIsBeingDragged = false;
            return false;
        }

        // 动作不是按下,不是松手(差不多也就剩下move了)并且当前在拖动状态,则拦截给自己的ontouch处理
        if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
            return true;//这里就会跳转到自己的ontouch,同时所有的子view都不再可能接收ontouch事件了
        }

        // mIsBeingDragged默认是false的,所以,当第一次move并且未将mIsBeingDragged设置true时,就会执行这里

        switch (action) {
        case MotionEvent.ACTION_MOVE: {
            // If we're refreshing, and the flag is set. Eat all MOVE events
            // 如何设置了 刷新中不能滑动,并且当前在刷新中,则拦截处理,这样listview就接收不到事件了,自然也就不能滑动了
            if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
                return true;
            }

            if (isReadyForPull()) {
                final float y = event.getY(), x = event.getX();
                final float diff, oppositeDiff, absDiff;

                // We need to use the correct values, based on scroll
                // direction
                switch (getPullToRefreshScrollDirection()) {
                case HORIZONTAL:
                    diff = x - mLastMotionX;
                    oppositeDiff = y - mLastMotionY;
                    break;
                case VERTICAL:
                default:
                    diff = y - mLastMotionY;
                    oppositeDiff = x - mLastMotionX;
                    break;
                }
                absDiff = Math.abs(diff);

                // 移动距离超过了判断为滑动的最小距离mtouchslop..并且(按纵向分析)(纵向的移动距离大于横向 ||
                // !mFilterTouchEvents)
                if (absDiff > mTouchSlop
                        && (!mFilterTouchEvents || absDiff > Math
                                .abs(oppositeDiff))) {
                    if (mMode.showHeaderLoadingLayout() && diff >= 1f
                            && isReadyForPullStart()) {// 如果当前可以显示header(其实就是看mModel是可以下拉刷新或者上下都可以拉的情况),并且移动差距>1f(向下拉动),并且子类判断已经准备好可以开始下拉了(比如listivew的话第一个item已经到头了)//这里重点就两个,一是分辨出当前是在向上还是向下做运动,二是子类要知道现在是否到顶或底了
                        mLastMotionY = y;
                        mLastMotionX = x;
                        mIsBeingDragged = true;
                        if (mMode == Mode.BOTH) {
                            mCurrentMode = Mode.PULL_FROM_START;
                        }
                    } else if (mMode.showFooterLoadingLayout() && diff <= -1f
                            && isReadyForPullEnd()) {
                        mLastMotionY = y;
                        mLastMotionX = x;
                        mIsBeingDragged = true;
                        if (mMode == Mode.BOTH) {
                            mCurrentMode = Mode.PULL_FROM_END;
                        }
                    }
                }
            }
            break;
        }
        case MotionEvent.ACTION_DOWN: {
            if (isReadyForPull()) {
                mLastMotionY = mInitialMotionY = event.getY();
                mLastMotionX = mInitialMotionX = event.getX();
                mIsBeingDragged = false;
            }
            break;
        }
        }

        return mIsBeingDragged;
    }


@Override
    public final boolean onTouchEvent(MotionEvent event) {

        if (!isPullToRefreshEnabled()) {
            return false;
        }

        // If we're refreshing, and the flag is set. Eat the event
        if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
            return true;
        }

        if (event.getAction() == MotionEvent.ACTION_DOWN
                && event.getEdgeFlags() != 0) {
            //getEdgeFlags():当事件类型是ActionDown时可以通过此方法获得,手指触控开始的边界. 如果是的话,有如下几种值:EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
            return false;
        }

        switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE: {
            if (mIsBeingDragged) {
                mLastMotionY = event.getY();
                mLastMotionX = event.getX();
                pullEvent();
                return true;
            }
            break;
        }

        case MotionEvent.ACTION_DOWN: {
            if (isReadyForPull()) {
                mLastMotionY = mInitialMotionY = event.getY();
                mLastMotionX = mInitialMotionX = event.getX();
                return true;
            }
            break;
        }

        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP: {
            if (mIsBeingDragged) {
                mIsBeingDragged = false;

                if (mState == State.RELEASE_TO_REFRESH
                        && (null != mOnRefreshListener || null != mOnRefreshListener2)) {
                    setState(State.REFRESHING, true);
                    return true;
                }

                // If we're already refreshing, just scroll back to the top
                if (isRefreshing()) {
                    smoothScrollTo(0);
                    return true;
                }

                // If we haven't returned by here, then we're not in a state
                // to pull, so just reset
                setState(State.RESET);

                return true;
            }
            break;
        }
        }

        return false;
    }

ontouch里面最重要的两个个方法就是:(擦,粘两个方法的代码就超出字数限制了)

1、//基本就是按滑动的位置来改变状态,不同的state也会在LoadingLayout里有相关的state设置,比如protected void onPullToRefresh() {
        switch (mCurrentMode) {
        case PULL_FROM_END:
            mFooterLayout.pullToRefresh();
            break;
        case PULL_FROM_START:
            mHeaderLayout.pullToRefresh();
            break;
        default:
            // NO-OP
            break;
        }
    }

在调用这个方法之前的move里面会调用onpullevent(),里面会调用 mFooterLayout.onPull(scale);这个是把当前的拖动百分比给Loadinglayout,这样就可以根据百分比显示旋转或翻转的动画了。onpullevent里面有个注意点,它是根据当前拖拽的距离和Loadinglayout的实际内容距离做的比较,它有个innerLayout,我们知道LoadingLayout有listview的0.6倍,而header的实际大小是xml定义的,而且他放在了LoadingLayout的底部(foot反之),根据这样来显示“向下拉动刷新”和“释放开始刷新”之类的字样提醒。同时会调用setState(State.PULL_TO_REFRESH);setState(State.RELEASE_TO_REFRESH);,当然字样提醒就是在对应的onReleaseToRefresh事件里,去调用mHeaderLayout.releaseTorefresh去实现的。(其实这里就是另一个方法了:onpullevent,不过贴代码就超了)

final void setState(State state, final boolean... params) {
        mState = state;
        if (DEBUG) {
            Log.d(LOG_TAG, "State: " + mState.name());
        }

        switch (mState) {
        case RESET:
            onReset();
            break;
        case PULL_TO_REFRESH:
            onPullToRefresh();
            break;
        case RELEASE_TO_REFRESH:
            onReleaseToRefresh();
            break;
        case REFRESHING:
        case MANUAL_REFRESHING:
            onRefreshing(params[0]);
            break;
        case OVERSCROLLING:
            // NO-OP
            break;
        }

        // Call OnPullEventListener
        if (null != mOnPullEventListener) {
            mOnPullEventListener.onPullEvent(this, mState, mCurrentMode);
        }
    }

ps:其实分离Loadinglayout还是挺容易的,因为现在LoadingLayout,它管的事有点多,他管的事就是有相同布局风格的代表,比如都有updatelabel,都有mHeaderImage等等。其实他应该还有个父类,或者或他应该去掉多余的方法,再拥有个子类(因为fresh的base基类里面的headLayout和footLayout都是LoadingLayout类型,所以不适合再做父类,或者做了父类后就要把基类的head foot的对应类型改成父类),比如叫做 standerLoadinLayout(标准型Loading),然后Rotato和Flip两个都继承stantder,stander中,可以做一些原来的LoadingLayout的事情,比如拥有mHeaderText控件,在reset中可以处理settext等。我们需要自定义的时候,就可以继承简化的LoadingLayout,来做一个非常随意的独特布局,比如一个animations的帧动画。(比如下拉过程中,小人在走,松手后,小人开始跑步。比如下拉的时候火箭开始点火,松手就开始飞,加载完就飞出屏幕了等等)

到这里,基类基本就看完了,其实边写边看,感觉印象很深,写着写着也容易停下来思考问题,下边就开始看各个子类了,大体看了下,都非常简单,因为父类做了不少事情,子类只是提供了不同的临界判断标准而已。再有基类的方法有onSaveInstanceState,存储状态就不讲了,他自己存完必须的状态后,会调用子类的ab方法onPtrsave,方便不通的子类执行不通的存储,比如Listview可能希望存储当前是第几个index。webview更希望存储当前加载的哪个url。:)

© 著作权归作者所有

共有 人打赏支持
j
粉丝 0
博文 3
码字总数 6889
作品 0
海淀
程序员
私信 提问
Android-PullToRefresh使用

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

崔同亮
2013/10/23
0
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
智能卡开发的相关总结

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
步入研二,迷茫中,搞底层框架研究(或者linux内核)还是做上层应用前途哪个好呢!?

本人现已步入研二,想抓紧再学一些新技术,不知道往什么方向啊? 我研究生的方向是,移动终端的性能和能耗的评测。现在对android的整体框架已有点了解,但是要全部搞懂感觉压力好大;对linux...

wangxigui
2013/09/23
3.5K
11
科学匠人 | 用数学逻辑来优化工作和人生

     编者按:从数学博士到投身计算机科学研究,微软亚洲研究院机器学习组主管研究员陈薇将理性严谨的数学思维融入到了工作与生活的方方面面:在研究领域,陈薇以她坚实的数学背景与研究...

微软亚洲研究院
08/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

tomcat编译超过64k大小的jsp文件报错原因

  今天遇到一个问题,首先是在tomcat中间件上跑的web项目,一个jsp文件,因为代码行数实在是太多了,更新了几个版本之后编译报错了,页面打开都是报500的错误,500的报错,知道http协议返回...

SEOwhywhy
13分钟前
0
0
flutter http 请求客户端

1、pubspec文件管理Flutter应用程序的assets(资源,如图片、package等)。 在pubspec.yaml中,通过网址“https://pub.dartlang.org/packages/http#-installing-tab-”确认版本号后,将http(0...

渣渣曦
14分钟前
0
0
Django基本命令及moduls举例

一、Django基本命令 1.创建项目 django-admin.py startproject mysite 创建后的项目结构:- mysite - mysite #对整个程序进行配置 - init #导入包专用- settings ...

枫叶云
29分钟前
4
0
zabbix安装

rpm -ivh http://repo.webtatic.com/yum/el6/latest.rpm 安装jdk rpm -ivh (自行在网上下载rpm包) 安装php并修改相应参数 yum -y install php56w php56w-gd php56w-mysqlnd php56w-bcmath......

muoushi
30分钟前
3
0
MySQL自增属性auto_increment_increment和auto_increment_offset

MySQL的系统变量或会话变量auto_increment_increment(自增步长)和auto_increment_offset(自增偏移量)控制着数据表的自增列ID。 mysql> show tables;Empty set (0.00 sec)mysql> CREATE TA......

野雪球
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部