Project we often have pull up, pull down refresh demand, almost all listView, RecyclerView will be accompanied by pull up, pull down refresh demand, if we use some open source controls, changed controls we will update, now we roll up our sleeves to write a general refresh control

Ideas:

Write a RefreshLayout that inherits from a RelativeLayout. Add the header and tail controls as the refresh control to perform the refresh via event distribution. Animate the control’s movement to make it available to all of its children, even a TextView

public class RefreshLayout extends RelativeLayout {

/** * Ratio of speed to pull when sliding control */
private final int V_REFRESH = 2;
/** * whether the refresh process * true is * false not * is false can be refreshed */
private boolean mIsRefreshDuring;
/** * can be pulled down to refresh */
private boolean mCanDownPull;
/** * can be pulled up to refresh */
private boolean mCanUpPull;
/** * Determine if the touch is the first move */
private boolean mIsFirstMove;
/** ** how far is the Y-axis shifted */
private int mDistanceY;
/** * Refresh the interface object */
private OnRefresh mOnRefresh;
/** * The variable used to control event interception */
private boolean mCanIntercept;
private int mTouchSlop;
private int mDistance;
private LayoutParams mHeaderParams;
private View mHeaderView;
private View mFootView;
private int mHeaderMaxHeight;
private int mStartY;
private LayoutParams mFootParams;
private int mFootMaxHeight;
private PullCallBack mCallBack;
private View mChildView;
private ObjectAnimator mAnimator;

public RefreshLayout(Context context) {
    super(context);
    initData();
}

public RefreshLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initData();
}

public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initData();
}

/** * the interface that must be implemented by the header and tail controls */
public interface HeadAndFootCallBack {
    // Set the properties
    void setAttribute(a);

    // Start refreshing
    void startPull(a);

    // Stop the refresh
    void stopPull(a);
}

/** * the dragged control subclass must implement */
public interface PullCallBack {
    boolean canDownPull(a);

    boolean canUpPull(a);
}

private void initData(a) {
    // Cannot draw without calling this method
    setWillNotDraw(false);
}

/** * This method must be used after the pull-down refresh is complete
public void downPullFinish(a) {
    mAnimator.setFloatValues(mChildView.getTranslationY(), 0);
    mAnimator.start();
    ((HeadAndFootCallBack) mHeaderView).stopPull();
}

/** * The method */ must be called after the pull-up is complete
public void upPullFinish(a) {
    mAnimator.setFloatValues(mChildView.getTranslationY(), 0);
    mAnimator.start();
    ((HeadAndFootCallBack) mFootView).stopPull();
}

/** * Automatically pull down to refresh */
public void autoDownPullForHead(a) {
    postDelayed(new Runnable() {
        @Override
        public void run(a) {
            mCanDownPull = true;
            mCanUpPull = false;
            mAnimator.setFloatValues(10, mHeaderMaxHeight); mAnimator.start(); ((HeadAndFootCallBack) mHeaderView).startPull(); mOnRefresh.onDownPullRefresh(); }},500);
}

/** * Automatically pull down to refresh */
public void autoUpPullForHead(a) {
    postDelayed(new Runnable() {
        @Override
        public void run(a) {
            mCanDownPull = false;
            mCanUpPull = true;
            mAnimator.setFloatValues(0, mFootMaxHeight); mAnimator.start(); ((HeadAndFootCallBack) mFootView).startPull(); mOnRefresh.onUpPullRefresh(); }},500);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return mCanIntercept;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    return true;
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.e("shen"."mIsRefreshDuring=" + mIsRefreshDuring);
    if (mIsRefreshDuring)/* MotionEvent*/ will not be retrieved if a refresh is in progress {
        return super.dispatchTouchEvent(event);
    }
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mStartY = (int) event.getY();
            initPull();
            break;
        case MotionEvent.ACTION_MOVE:
            if (event.getPointerCount() == 1) {
                int moveY = (int) event.getY();
                mDistanceY = (moveY - mStartY) / V_REFRESH;
                if(! mIsFirstMove && mDistanceY ! =0 && mDistanceY < mTouchSlop) {
                    mCanDownPull = mDistanceY > 0; mCanUpPull = ! mCanDownPull; mIsFirstMove =true;
                }
                if (mCanDownPull && mCallBack.canDownPull()) {
                    upDataForDownPull();// Drop refresh
                    mChildView.setEnabled(false);
                    mCanIntercept = true;
                }
                if (mCanUpPull && mCallBack.canUpPull()) {
                    upDataForUpPull();// Pull up load
                    mChildView.setEnabled(false);
                    mCanIntercept = true;
                }
                mStartY = moveY;
            }
            break;
        case MotionEvent.ACTION_UP:
            mIsRefreshDuring = true;
            mIsFirstMove = false;
            if (mHeaderParams.height >= mHeaderMaxHeight)/* Can pull down to refresh */ {
                ((HeadAndFootCallBack) mHeaderView).startPull();
                mOnRefresh.onDownPullRefresh();
            } else if (mFootParams.height >= mFootMaxHeight)/* Can be pulled up to refresh */ {
                ((HeadAndFootCallBack) mFootView).startPull();
                mOnRefresh.onUpPullRefresh();
            } else if (mHeaderParams.height > 0 && mHeaderParams.height < mHeaderMaxHeight)/* Cannot be pulled down to refresh {
                releaseForDownFinished();
            } else if (mFootParams.height > 0 && mFootParams.height < mFootMaxHeight)/* Cannot be pulled down to refresh {
                releaseForUpFinished();
            } else {
                mIsRefreshDuring = false;
                mCanIntercept = false;
            }
            break;
    }
    super.dispatchTouchEvent(event);
    return true;
}

/** * each touch needs to be initialized */
private void initPull(a) {
    mCanDownPull = false;
    mCanUpPull = false;
}

/** * no pull-up refresh is required */
private void releaseForUpFinished(a) {
    mAnimator.setFloatValues(mChildView.getTranslationY(), 0);
    mAnimator.start();
}

/** * does not require a pull-down refresh */
private void releaseForDownFinished(a) {
    mAnimator.setFloatValues(mChildView.getTranslationY(), 0);
    mAnimator.start();
}

/** * Handle gestures when pulling up */
private void upDataForUpPull(a) {
    if(mDistanceY ! =0) {
        mFootParams.height -= mDistanceY;
        if (mFootParams.height <= 0) {
            mFootParams.height = 0;
        }
        if(mFootParams.height >= mFootMaxHeight) { mFootParams.height = mFootMaxHeight; } mChildView.setTranslationY(-mFootParams.height); mFootView.requestLayout(); }}/** * Handle gestures when pulling down */
private void upDataForDownPull(a) {
    if(mDistanceY ! =0) {
        mHeaderParams.height += mDistanceY;
        if (mHeaderParams.height >= mHeaderMaxHeight) { / / the biggest
            mHeaderParams.height = mHeaderMaxHeight;
        }
        if (mHeaderParams.height <= 0) { / / minimum
            mHeaderParams.height = 0; } mChildView.setTranslationY(mHeaderParams.height); mHeaderView.requestLayout(); }}@Override
protected void onAttachedToWindow(a) {
    super.onAttachedToWindow();
}

@Override
protected void onFinishInflate(a) {
    super.onFinishInflate();
    / / load the head
    mHeaderView = getChildAt(0);
    if(! (mHeaderViewinstanceof HeadAndFootCallBack)) {
        new IllegalStateException("HeaderView must implement the HeadAndFootCallBack interface.");
    }
    ((HeadAndFootCallBack) mHeaderView).setAttribute();
    mHeaderParams = (LayoutParams) mHeaderView.getLayoutParams();
    mHeaderParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);

    / / load the tail
    mFootView = getChildAt(2);
    if(! (mFootViewinstanceof HeadAndFootCallBack)) {
        new IllegalStateException("FootView must implement the HeadAndFootCallBack interface");
    }
    ((HeadAndFootCallBack) mFootView).setAttribute();
    mFootParams = (LayoutParams) mFootView.getLayoutParams();
    mFootParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);

    mChildView = getChildAt(1);
    if(! (mChildViewinstanceof HeadAndFootCallBack)) {
        new IllegalStateException("ChildView must implement the PullCallBack interface");
    }
    mCallBack = (PullCallBack) getChildAt(1);

    // Set the animation
    mAnimator = ObjectAnimator.ofFloat(mChildView, "translationY".0);
    mAnimator.setInterpolator(new DecelerateInterpolator());
    mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int translationY = (int) mChildView.getTranslationY();
            if (mCanUpPull) { // Slide from the position moved to
                mFootParams.height = Math.abs(translationY);
                mFootView.requestLayout();
            } else if (mCanDownPull) {
                mHeaderParams.height = Math.abs(translationY);
                mHeaderView.requestLayout();
            }
            Log.e("shen"."translationY=" + translationY);
            Log.e("shen"."mHeaderParams.height=" + mHeaderParams.height);
            if (translationY == 0) {
                mChildView.setEnabled(true);
                mDistanceY = 0; / / reset
                mIsRefreshDuring = false; / / reset
                mCanIntercept = false;
            } else {
                mIsRefreshDuring = true; }}}); }@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    mDistance = mTouchSlop * 5;
    // Set the initial properties of the pull-down header
    mHeaderMaxHeight = mHeaderParams.height;
    mHeaderParams.height = 0;
    mHeaderView.requestLayout();
    // Set the pull-up initial property
    mFootMaxHeight = mFootParams.height;
    mFootParams.height = 0;
    mFootView.requestLayout();
}

/** * Pull down/pull up events listen */
public interface OnRefresh {
    /** * drop down refresh */
    void onDownPullRefresh(a);

    /** * pull up load */
    void onUpPullRefresh(a);
}

public void setOnRefresh(OnRefresh onRefresh) {
    mOnRefresh = onRefresh;
}
Copy the code

Add three controls to him, the first is the refresh head, tail, the second is the normal display of the control. You must have the header and tail implement the HeadAndFootCallBack interface to set the properties and notify the start and end of the refresh

Difficulties: Now let’s talk about the difficulties encountered during development

The result of a dispatchTouchEvent is that if neither the control nor its children consume the event, the event will not be sent to it, because if the DOWN event is not consumed, all subsequent events will not be received. When the child control does not consume the event, it will return the consumption by the control. It will not cause the event cannot be received because the DOWN event does not consume the event, resulting in the dispatchTouchEvent does not consume the event animation, animation is my pain. Recently in the learning estimator this control thinks to write well, through he can help us learn event distribution, animation, interface callback, but also has a certain learning significance



+ QQ group 457848807:. Get the above high definition technical mind map and free video learning materials on related technologies