美文网首页
使用android-Ultra-Pull-To-Refresh实

使用android-Ultra-Pull-To-Refresh实

作者: flycode | 来源:发表于2018-05-30 18:04 被阅读0次

    官方地址

    https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh

    简单使用

    PtrClassicDefaultHeader header = new PtrClassicDefaultHeader(this);        
    ptrFrameLayout.addPtrUIHandler(header);
    ptrFrameLayout.setHeaderView(header);
    ptrFrameLayout.setPtrHandler(new PtrDefaultHandler() {
          @Override
          public void onRefreshBegin(PtrFrameLayout frame) {
              refresh();
          }
     });
    

    PtrUIHandler接口

    它的实现类为PtrClassicDefaultHeader、MaterialHeader、StoreHouseHeader、PtrUIHandlerHolder。

    1、前三者为下拉刷新不同样式的实现类,可通过此接口自定义下拉刷新样式,接口方法如下:

    • 开始下拉,此方法之后status = 2(PTR_STATUS_PREPARE)
    public void onUIRefreshPrepare(PtrFrameLayout frame);
    
    • 开始刷新,此方法之后status = 3(PTR_STATUS_LOADING)
    public void onUIRefreshBegin(PtrFrameLayout frame);
    
    • 刷新完成,此方法之后status = 4(PTR_STATUS_COMPLETE)
    public void onUIRefreshComplete(PtrFrameLayout frame);
    
    • 恢复UI ,此方法之后status = 1(PTR_STATUS_INIT)
    public void onUIReset(PtrFrameLayout frame);
    
    • 下拉刷新过程中位置回调
    public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);
    

    2、PtrUIHandlerHolder是PtrUIHandler的持有者。内置静态方法addHandler、removeHandler,用于添加和移除PtrUIHandler。一个PtrUIHandlerHolder对象可以看作是链表结构的一个节点,在实现PtrUIHandler上述5个方法时,遍历所有的链表节点,调用对应的接口方法。

    PtrFrameLayout

    自定义属性
    // 头部
    <attr name="ptr_header" format="reference" />
    // 内容
    <attr name="ptr_content" format="reference" />
    // 阻尼系数,默认1.7f,越大感觉下拉时越吃力
    <attr name="ptr_resistance" format="float" />
    // 触发刷新时移动的位置比例,默认1.2f,移动达到头部高度1.2倍时可触发刷新操作
    <attr name="ptr_ratio_of_header_height_to_refresh" format="float" />
    // 回弹延时,默认 200ms,回弹到刷新高度所用时间
    <attr name="ptr_duration_to_close" format="integer" />
    
    // 头部回弹时间,默认1000ms
    <attr name="ptr_duration_to_close_header" format="integer" />
    // 下拉刷新/释放刷新,默认释放刷新
    <attr name="ptr_pull_to_fresh" format="boolean" />
    // 刷新时保持头部,默认true
    <attr name="ptr_keep_header_when_refresh" format="boolean" />
    
    onMeasure方法
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 测量headerView的宽高
        if (mHeaderView != null) {
            measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
            MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
            mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            mPtrIndicator.setHeaderHeight(mHeaderHeight);
        }
        // 对ContentView进行测量
        if (mContent != null) {
            measureContentView(mContent, widthMeasureSpec, heightMeasureSpec);
        }
    }
    
    private void measureContentView(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                getPaddingTop() + getPaddingBottom() + lp.topMargin, lp.height);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    

    调用measureChildWithMargins方法对HeaderView进行测量,获得headerView高度
    调用getChildMeasureSpec(int spec, int padding, int childDimension)获得子View的MeasureSpec,调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec),对ContentView进行测量

    onLayout方法
    protected void onLayout(boolean flag, int i, int j, int k, int l) {
        layoutChildren();
    }
    
    private void layoutChildren() {
        int offset = mPtrIndicator.getCurrentPosY();
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        // 对HeaderView进行layout,主要是top的计算
        if (mHeaderView != null) {
            MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
            final int left = paddingLeft + lp.leftMargin;
            // enhance readability(header is layout above screen when first init)
            final int top = -(mHeaderHeight - paddingTop - lp.topMargin - offset);
            final int right = left + mHeaderView.getMeasuredWidth();
            final int bottom = top + mHeaderView.getMeasuredHeight();
            mHeaderView.layout(left, top, right, bottom);
        }
        // 对ContentView进行layout
        if (mContent != null) {
            if (isPinContent()) {
                offset = 0;
            }
            MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
            final int left = paddingLeft + lp.leftMargin;
            final int top = paddingTop + lp.topMargin + offset;
            final int right = left + mContent.getMeasuredWidth();
            final int bottom = top + mContent.getMeasuredHeight();
            mContent.layout(left, top, right, bottom);
        }
    }
    
    dispatchTouchEvent方法
    public boolean dispatchTouchEvent(MotionEvent e) {
        if (!isEnabled() || mContent == null || mHeaderView == null) {
            return dispatchTouchEventSupper(e);
        }
        int action = e.getAction();
        switch (action) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mPtrIndicator.onRelease();
                if (mPtrIndicator.hasLeftStartPosition()) {
                    // 松手的位置大于初始位置
                    onRelease(false);
                    if (mPtrIndicator.hasMovedAfterPressedDown()) {
                        sendCancelEvent();
                        return true;
                    }
                    return dispatchTouchEventSupper(e);
                } else {
                    return dispatchTouchEventSupper(e);
                }
    
            case MotionEvent.ACTION_DOWN:
                mHasSendCancelEvent = false;
                // 记录按下的位置
                mPtrIndicator.onPressDown(e.getX(), e.getY());
                mScrollChecker.abortIfWorking();
                // 重置水平方向阻止标记
                mPreventForHorizontal = false;
                // The cancel event will be sent once the position is moved.
                // So let the event pass to children.
                // fix #93, #102
                dispatchTouchEventSupper(e);
                return true;
                
            case MotionEvent.ACTION_MOVE:
                mLastMoveEvent = e;
                mPtrIndicator.onMove(e.getX(), e.getY());
                // 相对上次位置偏移量(阻尼系数转化后)
                float offsetX = mPtrIndicator.getOffsetX();
                float offsetY = mPtrIndicator.getOffsetY();
                // 水平方向移动较大,事件可以继续分发下去
                if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
                    if (mPtrIndicator.isInStartPosition()) {
                        mPreventForHorizontal = true;
                    }
                }
                if (mPreventForHorizontal) {
                    return dispatchTouchEventSupper(e);
                }
                boolean moveDown = offsetY > 0;
                boolean moveUp = !moveDown;
                boolean canMoveUp = mPtrIndicator.hasLeftStartPosition();
                // disable move when header not reach top
                if (moveDown && mPtrHandler != null && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) {
                    return dispatchTouchEventSupper(e);
                }
                if ((moveUp && canMoveUp) || moveDown) {
                    movePos(offsetY);
                    return true;
                }
        }
        return dispatchTouchEventSupper(e);
    }
    
    位置更新方法
    private void updatePos(int change) {
        if (change == 0) {
            return;
        }
        boolean isUnderTouch = mPtrIndicator.isUnderTouch();
        // once moved, cancel event will be sent to child
        if (isUnderTouch && !mHasSendCancelEvent && mPtrIndicator.hasMovedAfterPressedDown()) {
            mHasSendCancelEvent = true;
            sendCancelEvent();
        }
        // leave initiated position or just refresh complete
        if ((mPtrIndicator.hasJustLeftStartPosition() && mStatus == PTR_STATUS_INIT) ||
                (mPtrIndicator.goDownCrossFinishPosition() && mStatus == PTR_STATUS_COMPLETE && isEnabledNextPtrAtOnce())) {
            mStatus = PTR_STATUS_PREPARE;
            mPtrUIHandlerHolder.onUIRefreshPrepare(this);
        }
        // back to initiated position
        if (mPtrIndicator.hasJustBackToStartPosition()) {
            tryToNotifyReset();
            // recover event to children
            if (isUnderTouch) {
                sendDownEvent();
            }
        }
        // Pull to Refresh
        if (mStatus == PTR_STATUS_PREPARE) {
            // reach fresh height while moving from top to bottom
            if (isUnderTouch && !isAutoRefresh() && mPullToRefresh
                    && mPtrIndicator.crossRefreshLineFromTopToBottom()) {
                tryToPerformRefresh();
            }
            // reach header height while auto refresh
            if (performAutoRefreshButLater() && mPtrIndicator.hasJustReachedHeaderHeightFromTopToBottom()) {
                tryToPerformRefresh();
            }
        }
        // 核心移动方法
        mHeaderView.offsetTopAndBottom(change);
        if (!isPinContent()) {
            mContent.offsetTopAndBottom(change);
        }
        invalidate();
        if (mPtrUIHandlerHolder.hasHandler()) {
            mPtrUIHandlerHolder.onUIPositionChange(this, isUnderTouch, mStatus, mPtrIndicator);
        }
        onPositionChange(isUnderTouch, mStatus, mPtrIndicator);
    }
    

    相关文章

      网友评论

          本文标题:使用android-Ultra-Pull-To-Refresh实

          本文链接:https://www.haomeiwen.com/subject/ussxsftx.html