UI实现之下拉刷新:SwipeRefreshLayout PullToRefres

来源:互联网 发布:2017移动互联网数据 编辑:程序博客网 时间:2024/06/02 10:56

UI实现之下拉刷新:SwipeRefreshLayout PullToRefresh


第一个:SwipeRefreshLayout

这里写图片描述
SwipeRefreshLayout是一个ViewGroup,使用的时候需要在布局文件里添加子View(mTarget),一般是ListView;下拉的时候把ListView整个往下移动并播放动画;
SwipeRefreshLayout继承ViewGroup;onMeasure()、onLayout()中都没有太多内容,跳过直接看onTouchEvent的ACTION_MOVE代码

 if(this.mDownEvent != null && !this.mReturningToStart) {                float eventY = event.getY();                float yDiff = eventY - this.mDownEvent.getY();                //此处分两步                //第一步:当刚开始下拉的时候会移动mTarget并且动画慢慢出场,                //第二步:当下拉到一定程度则mTarget回到原处播放动画                if(yDiff > (float)this.mTouchSlop) {                    if(yDiff > this.mDistanceToTriggerSync) {                    //此为第二步                        this.startRefresh();//调用播放动画                        handled = true;                    } else {                    //此为第一步            this.setTriggerPercentage(this.mAccelerateInterpolator.getInterpolation(yDiff / this.mDistanceToTriggerSync));//更新mProgressBar                        float offsetTop = yDiff;                        if(this.mPrevY > eventY) {                            offsetTop = yDiff - (float)this.mTouchSlop;                        }                        this.updateContentOffsetTop((int)offsetTop);//移动mTarget                        if(this.mPrevY > eventY &&this.mTarget.getTop() < this.mTouchSlop) {                            this.removeCallbacks(this.mCancel);                        } else {                            this.updatePositionTimeout();//当手指一定时间不动则重置mTarget和动画                        }                        this.mPrevY = event.getY();                        handled = true;                    }                }            }

简单来说,这几行代码已经可以把SwipeRefreshLayout的大致运行过程表达清楚了。第一步,用户滑动屏幕的时候,判定用户下滑的距离。小于mDistanceToTriggerSync的时候会去下移mTarget(xml布局中,SwipeRefreshLayout下的子View,一般ListView),同时根据下滑距离改变mProgressBar(即顶部不断变长的颜色条)的进度;同时这里还判断了如果下滑时手指不动一定时间了,则重置mTarget、mProgressBar。第二步,当用户下滑距离大于mDistanceToTriggerSync则mTarget重置,同时播放动画。在这里我们只看onTouchEvent,深入一层层的代码具体实现想知道的可以自己跳进去看;具体mTarget是怎么移动的(最终是调用offsetTopAndBottom方法实现移动的)?mProgressBar是怎么运行动画的(在自定义SwipeProgressBar类里,在onDraw里画出来的)?


最后聊下触摸事件的传递,在SwipeRefreshLayout的onInterceptTouchEvent中会把根据相关状态值(是否可以播放动画或者正在播放中)以及是否是下拉来决定是否拦截改事件。


第二:PushToReresh

https://github.com/MarkMjw/PullToRefresh

这个的原理比较容易理解,XListView继承ListView,通过addHeaderView加入XHeaderView,同时设置其高度为0;下拉时改变XHeaderView的高度,同时让XHeaderView播放动画。XScrollView的原理也是一样,但是XScrollView继承自ScrollView没有HeaderView,所以他加了一个布局,相信大家看下就理解了,三个LinearLayout依次就类似于ListView的HeaderView、ListView、FooterView

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent">    <LinearLayout        android:id="@+id/header_layout"        android:layout_gravity="center_horizontal|top"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical" />    <LinearLayout        android:id="@+id/content_layout"        android:layout_gravity="center"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical" />    <LinearLayout        android:id="@+id/footer_layout"        android:layout_gravity="center_horizontal|bottom"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical" /></LinearLayout>

下面来看看XListView的onTouchEvent的ACTION_MOVE部分

 case MotionEvent.ACTION_MOVE:                final float deltaY = ev.getRawY() - mLastY;                mLastY = ev.getRawY();                //if分支是下拉刷新,else分支是加载更多(不关心)                if (getFirstVisiblePosition() == 0 && (mHeader.getVisibleHeight() > 0 ||                        deltaY > 0)) {                    // the first item is showing, header has shown or pull down.                    //关键就是这句,改变XHeaderView的高度                    updateHeaderHeight(deltaY / OFFSET_RADIO);                    //没找到OnXScrollListener的实现,不知道实际干嘛的;注释掉后没发现有啥不对劲                    invokeOnScrolling();                } else if (getLastVisiblePosition() == mTotalItemCount - 1 && (mFooterView                        .getBottomMargin() > 0 || deltaY < 0)) {                    // last item, already pulled up or want to pull up.                    updateFooterHeight(-deltaY / OFFSET_RADIO);                }                break;

现在跟进去updateHeaderHeight看看

    private void updateHeaderHeight(float delta) {        //调用XHeaderView的方法改变它的高度        mHeader.setVisibleHeight((int) delta + mHeader.getVisibleHeight());        //设置XHeaderView的状态,告诉它应该执行哪个动画了        if (mEnablePullRefresh && !mPullRefreshing) {            // update the arrow image unrefreshing            if (mHeader.getVisibleHeight() > mHeaderHeight) {                //Release to refresh部分                mHeader.setState(XHeaderView.STATE_READY);            } else {                //Pull to refresh部分                mHeader.setState(XHeaderView.STATE_NORMAL);            }        }        // scroll to top each time        //这个地方大家可以注释掉,看看效果;注释掉后下拉之后再往上拉动的时候不止是XHeaderView的高度会变小,ListView也会往上滑动;而它的作用就是把ListView滚动到顶部,这样就可以避免这个问题的发生        setSelection(0);    }

相信到这里,整个下拉的大致过程大家已经差不多清楚了,具体还是要自己去看代码。此外想说的是,XListView继承自ListView两种滑动一个View没有办法通过事件onInterceptTouchEvent来解决所以此处采用了setSelection(0)解决;同样的XScrollView是通过下面这段代码来解决的(原理一样,调用函数不同而已),下面看看XScrollView的updateHeaderHeight函数

  private void updateHeaderHeight(float delta) {        mHeader.setVisibleHeight((int) delta + mHeader.getVisibleHeight());        if (mEnablePullRefresh && !mPullRefreshing) {            // update the arrow image unrefreshing            if (mHeader.getVisibleHeight() > mHeaderHeight) {                mHeader.setState(XHeaderView.STATE_READY);            } else {                mHeader.setState(XHeaderView.STATE_NORMAL);            }        }        // scroll to top each time        //就是它了        post(new Runnable() {            @Override            public void run() {                XScrollView.this.fullScroll(ScrollView.FOCUS_UP);            }        });    }

再者

还有一个地方在ACTION_UP等地方将一切归于最原始的状态等待下一次的下拉刷新;这里就贴代码了,无非就是重置属性值,将mTarget或XHeaderView重置成原始的位置、状态
最后要说的一个地方就是Animation、Scroller、Interpolator的加入,让整个过程更加自然流畅


小白一枚,学习记录,轻喷~~~


附(开始学UML类图的绘制了。。。):
这里写图片描述

0 0
原创粉丝点击