关于自定义父控件实现右滑动最后,继续滑动,加载刷新更多的另一种实现方案

关于自定义父控件实现右滑动最后,继续滑动,加载刷新更多

本次采用继承ReleativeLayout作为RecyclerView父控件实现,与前文处理不一样的地方这是弹性实现上文采用设置margin,本次是view的width。

实现过程,既然作为父控件,那么手势操作的处理,我们不用关心处理的过程,即onTouchEvent,我们关注事件的分发,即onInterceptTouchEvent
和dispatchTouchEvent。
那么onInterceptTouchEvent我们不拦截事件,让分发的流程正常向下,就是——

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /**
         * 不拦截,直接传递给子的view。
         */
        return false;
    }

我们在dispatchTouchEvent中做处理。首先需要知道此函数返回值用处——

/**
     * 事件分发
     *  false-->转给父类onTouchEvent
     *  dispatchTouchEvent(ev)-->事件向下分发onInterceptTouchEvent
     *  true-->事件被自身消耗
     */
     @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mChildView == null) {
            return super.dispatchTouchEvent(ev);
        }


        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                startDownX = ev.getX();
                isReleaseFinger = false;

            case MotionEvent.ACTION_MOVE:

                float nowX = ev.getX();
                int scrollX = (int) (nowX - startDownX);
                isReleaseFinger = false;

                if ((isCanPullToLeftDirector() && scrollX < 0)) {// 继续向左边做移动

                    int delta = (int) (scrollX * DRAG_RATE);
                    mIRefreshMovedViewLayout.doToMove(delta);

                    if (mIRefreshMovedViewLayout.getVisibleWidth() > 0
                            && mIRefreshMovedViewLayout.getRefreshState() < IPullToLeftRefreshState.STATE_REFRESHING) {

                        return super.dispatchTouchEvent(ev);

                    } else if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
                        mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
                    }

                    return true;

                } else {
                    startDownX = ev.getX();
                    if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
                        mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
                        return true;
                    }

                    return super.dispatchTouchEvent(ev);
                }
            case MotionEvent.ACTION_UP:
                isReleaseFinger = true;

            default:

                    isReleaseFinger = false;
                    if (mIRefreshMovedViewLayout.releaseAction()) {

                        if (mOnPullToLeftListener != null) {
                            mOnPullToLeftListener.onPullToLeftRefresh();

                        } else {
                            new Handler().postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    doWhatCompleteToRefresh();
                                }
                            }, 1200);
                        }

                    } else {
                        return super.dispatchTouchEvent(ev);
                    }
        }
        return super.dispatchTouchEvent(ev);
    }


    isCanPullToLeftDirector()方法作用为:是否滑动至最右端
    具体如下:
    @Override
    public boolean isCanPullToLeftDirector() {
        final RecyclerView.Adapter adapter = ((RecyclerView) mChildView).getAdapter();

        if (null == adapter) {
            return true;
        }

        final int lastItemPosition = adapter.getItemCount() - 1;
        if (((RecyclerView) mChildView)
                .getLayoutManager() instanceof LinearLayoutManager) {

            final int lastVisiblePosition = ((LinearLayoutManager) ((RecyclerView) mChildView).getLayoutManager()).findLastVisibleItemPosition();

            if (lastVisiblePosition >= lastItemPosition) {

                final int childIndex = lastVisiblePosition - ((LinearLayoutManager) ((RecyclerView) mChildView)
                        .getLayoutManager()).findFirstVisibleItemPosition();

                final int childCount = ((RecyclerView) mChildView).getChildCount();
                final int index = Math.max(childIndex, childCount - 1);
                final View lastVisibleChild = ((RecyclerView) mChildView).getChildAt(index);
                if (lastVisibleChild != null) {
                    boolean isArriveToMostRight = lastVisibleChild.getRight() <= mChildView.getRight() - mChildView.getLeft();
                    return isArriveToMostRight;
                }
            }
        }
        return false;
    }
本类实现的全部代码:
public class PullToLeftRefreshLinearLayout extends RelativeLayout
        implements ViewTreeObserver.OnGlobalLayoutListener, IPullToLeftRefresh{


    private static final float DRAG_RATE = 0.50f;
    /**
     * 必须是RecyclerView
     */
    private View mChildView;

    /**
     * 第二个控件,必须实现IRefreshMovedViewLayout接口
     */
    private IRefreshMovedViewLayout mIRefreshMovedViewLayout;

    /**
     *  用于记录childView(RecyclerView)正常的布局位置
     */
    private Rect originalRect = new Rect();

    /**
     * 是否松开了手指
     */
    private boolean isReleaseFinger;

    /**
     * 按下时候的X坐标
     */
    private float startDownX;

    /**
     * 刷新监听
     */
    OnPullToLeftListener mOnPullToLeftListener;

    /**
     * 设置监听
     * @param leftListener
     */
    public void setOnPullToLeftListener(OnPullToLeftListener leftListener){
        this.mOnPullToLeftListener = leftListener;
    }

    /**
     * 回调接口
     */
    public interface OnPullToLeftListener{
        void onPullToLeftRefresh();
    }

    public PullToLeftRefreshLinearLayout(Context context) {
        super(context);
    }

    public PullToLeftRefreshLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public PullToLeftRefreshLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        /**
         * 初始值设置
         */
        originalRect.set(mChildView.getLeft(), mChildView.getTop(), mChildView.getRight(), mChildView.getBottom());

    }

    /**
     * 得到子孩子RecyclerView
     * @return
     */
    public RecyclerView getChildView(){
        if (mChildView == null){
            return null;
        }
        return  (RecyclerView) mChildView;
    }


    /**
     * 加载布局后初始化,这个方法会在加载完布局后调用
     */
    @Override
    protected void onFinishInflate() {

        if (getChildCount() > 0) {

            if (getChildCount() != 2){
                throw new RuntimeException("必须有两个子控件且仅能有两个子控件");
            }

            for (int i = 0; i < getChildCount(); i++) {
                if (getChildAt(i) instanceof RecyclerView) {
                    if (mChildView == null) {
                        mChildView = getChildAt(i);
                    } else {
                        throw new RuntimeException("只能存在一个RecyclerView");
                    }
                } else if (getChildAt(i) instanceof IRefreshMovedViewLayout) {
                    if (this.mIRefreshMovedViewLayout == null) {
                        this.mIRefreshMovedViewLayout = (IRefreshMovedViewLayout) getChildAt(i);
                    }else {
                        throw new RuntimeException("只能存在一个实现IRefreshMovedViewLayout接口的控件");
                    }
                }
            }
        }

        if (mChildView == null) {
            throw new RuntimeException("子容器中必须有一个RecyclerView");
        }
        if (this.mIRefreshMovedViewLayout == null) {
            throw new RuntimeException("子容器中必须有一个实现IRefreshMovedViewLayout接口的控件");
        }

        getViewTreeObserver().addOnGlobalLayoutListener(this);

        ((RecyclerView) mChildView).addOnScrollListener(new RecyclerView.OnScrollListener() {

            boolean isToLeftSliding = false;

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

                //if (newState == RecyclerView.SCROLL_STATE_IDLE && isToLeftSliding){
                //    recyclerView.smoothScrollBy(UiUtils.dip2px(50),0);
                //}

                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

                if (dx>0){
                    isToLeftSliding = true;
                } else {
                    isToLeftSliding = false;
                }

                super.onScrolled(recyclerView, dx, dy);
            }
        });


        super.onFinishInflate();
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onGlobalLayout() {
        requestLayout();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }

    @Override
    public boolean isCanPullToLeftDirector() {
        final RecyclerView.Adapter adapter = ((RecyclerView) mChildView).getAdapter();

        if (null == adapter) {
            return true;
        }

        final int lastItemPosition = adapter.getItemCount() - 1;
        if (((RecyclerView) mChildView)
                .getLayoutManager() instanceof LinearLayoutManager) {

            final int lastVisiblePosition = ((LinearLayoutManager) ((RecyclerView) mChildView).getLayoutManager()).findLastVisibleItemPosition();

            if (lastVisiblePosition >= lastItemPosition) {

                final int childIndex = lastVisiblePosition - ((LinearLayoutManager) ((RecyclerView) mChildView)
                        .getLayoutManager()).findFirstVisibleItemPosition();

                final int childCount = ((RecyclerView) mChildView).getChildCount();
                final int index = Math.max(childIndex, childCount - 1);
                final View lastVisibleChild = ((RecyclerView) mChildView).getChildAt(index);
                if (lastVisibleChild != null) {
                    boolean isArriveToMostRight = lastVisibleChild.getRight() <= mChildView.getRight() - mChildView.getLeft();
                    return isArriveToMostRight;
                }
            }
        }
        return false;
    }

    @Override
    public void doWhatCompleteToRefresh() {
        mIRefreshMovedViewLayout.refreshComplete();
    }

    @Override
    public void doWhatRecoverLayout() {
        mChildView.layout(originalRect.left, originalRect.top, originalRect.right, originalRect.bottom);
        requestLayout();
    }

    @Override
    public void setMoveViews(View movedView) {
        if (movedView instanceof IRefreshMovedViewLayout) {

            this.mIRefreshMovedViewLayout = (IRefreshMovedViewLayout) movedView;
            requestLayout();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        /**
         * 不拦截,直接传递给子的view。
         */
        return false;
    }

    /**
     * 事件分发
     *  false-->转给父类onTouchEvent
     *  dispatchTouchEvent(ev)-->事件向下分发onInterceptTouchEvent
     *  true-->事件被自身消耗
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mChildView == null) {
            return super.dispatchTouchEvent(ev);
        }


        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                startDownX = ev.getX();
                isReleaseFinger = false;

            case MotionEvent.ACTION_MOVE:

                float nowX = ev.getX();
                int scrollX = (int) (nowX - startDownX);
                isReleaseFinger = false;

                if ((isCanPullToLeftDirector() && scrollX < 0)) {// 继续向左边做移动

                    int delta = (int) (scrollX * DRAG_RATE);
                    mIRefreshMovedViewLayout.doToMove(delta);

                    if (mIRefreshMovedViewLayout.getVisibleWidth() > 0
                            && mIRefreshMovedViewLayout.getRefreshState() < IPullToLeftRefreshState.STATE_REFRESHING) {

                        return super.dispatchTouchEvent(ev);

                    } else if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
                        mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
                    }

                    return true;

                } else {
                    startDownX = ev.getX();
                    if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
                        mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
                        return true;
                    }

                    return super.dispatchTouchEvent(ev);
                }
            case MotionEvent.ACTION_UP:
                isReleaseFinger = true;

            default:

                    isReleaseFinger = false;
                    if (mIRefreshMovedViewLayout.releaseAction()) {

                        if (mOnPullToLeftListener != null) {
                            mOnPullToLeftListener.onPullToLeftRefresh();

                        } else {
                            new Handler().postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    doWhatCompleteToRefresh();
                                }
                            }, 1200);
                        }

                    } else {
                        return super.dispatchTouchEvent(ev);
                    }
        }
        return super.dispatchTouchEvent(ev);
    }
}

下面是跟随滑动有移动的View的实现,放在RecyclerView右侧,即父控件PullToLeftRefreshLinearLayout最右端对齐,
当中TextTip提供给使用者对四个状态的提示文案:

public class MovedViewLayout extends LinearLayout implements IRefreshMovedViewLayout{

    public class TextTip{
        public String mRefreshing = "正在刷新加载";
        public String mReleaseFingerToRefresh = "松开手指开始刷新加载";
        public String mRefreshComplete = "刷新加载完成";
        public String mRefreshNormal = "查看更多";
    }

    /**
     * 状态提示类,供给外部使用
     */
    TextTip mTextTip;

    public TextTip getTextTip(){
        return mTextTip;
    }

    /**
     * 默认状态是正常
     */
    private @IPullToLeftRefreshState int mRefreshState = IPullToLeftRefreshState.STATE_NORMAL;

    public TextView tv_moved_view;
    public ImageView iv_moved_view;
    public LinearLayout mContainer;

    public int mMeasuredWidth;

    IPullRefreshStateListener mIPullRefreshStateListener;

    public void setIPullRefreshStateListener(IPullRefreshStateListener zIPullRefreshStateListener){
        this.mIPullRefreshStateListener = zIPullRefreshStateListener;
    }

    public MovedViewLayout(Context context) {
        this(context, null);
    }

    public MovedViewLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MovedViewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initSetLayout(context);
    }

    private void initSetLayout(Context context){

        LayoutInflater.from(context).inflate(R.layout.look_more, this);


        mContainer = (LinearLayout) findViewById(R.id.ll_moved_view);
        iv_moved_view = (ImageView) findViewById(R.id.iv_moved_view);
        tv_moved_view = (TextView) findViewById(R.id.tv_moved_view);

        measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
        setGravity(Gravity.CENTER);

        mContainer.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
        this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));

        mMeasuredWidth = getMeasuredWidth();
        setVisibility(GONE);

        mTextTip = new TextTip();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
    }

    /**
     * 重置状态。
     */
    public void resetState() {
        /**
         * 移动至0会移到屏幕外,不可见
         */
        smoothScrollTo(0);
        setRefreshState(IPullToLeftRefreshState.STATE_NORMAL);
    }

    @Override
    public void doToMove(float delta) {


        if (getVisibleWidth() > 0 || delta < 0) {

            if(getVisibility() == View.GONE)
                setVisibility(VISIBLE);

            if (getVisibleWidth()>mMeasuredWidth*4){

                return;
            }

            setVisibleWidth((int) Math.abs(delta) + getVisibleWidth());

            if (mRefreshState <= IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH) {

                if (getVisibleWidth()>mMeasuredWidth * 2) {
                    setRefreshState(IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH);

                //} else if (mRefreshState == IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH){
                //    setRefreshState(IPullToLeftRefreshState.STATE_REFRESHING);

                } else {
                    setRefreshState(IPullToLeftRefreshState.STATE_NORMAL);
                }

            }
        }
    }

    @Override
    public boolean releaseAction() {
        boolean isOnRefresh = false;
        int visibleWidth = getVisibleWidth();
        if (visibleWidth == 0)
            isOnRefresh = false;


        if (getVisibleWidth() > mMeasuredWidth && mRefreshState < IPullToLeftRefreshState.STATE_REFRESHING) {
            setRefreshState(IPullToLeftRefreshState.STATE_REFRESHING);
            isOnRefresh = true;
        }

        if (mRefreshState == IPullToLeftRefreshState.STATE_REFRESHING && visibleWidth <= mMeasuredWidth) {
            //return;
        }

        int destWidth = 0;

        if (mRefreshState == IPullToLeftRefreshState.STATE_REFRESHING) {
            destWidth = mMeasuredWidth;
        }


        /**
         * destWidth = 0:移动至不可见
         */
        smoothScrollTo(destWidth);

        return isOnRefresh;
    }

    @Override
    public void refreshComplete() {
        setRefreshState(IPullToLeftRefreshState.STATE_DONE_FINISHED);
        new Handler().postDelayed(new Runnable() {
            public void run() {
                resetState();
            }
        }, 1000);
    }

    @Override
    public int getVisibleWidth() {

        int zWidth = mContainer.getWidth();
        return zWidth;
    }

    @Override
    public void setVisibleWidth(int value) {
        if (value < 0)
            value = 0;

        LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();//new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
        lp.width = value;
        mContainer.setLayoutParams(lp);
        invalidate();

    }



    private void smoothScrollTo(int destWidth) {

        ValueAnimator animator = ValueAnimator.ofInt(getVisibleWidth(), destWidth);
        animator.setDuration(300).start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setVisibleWidth((int) animation.getAnimatedValue());
            }
        });
        animator.start();
    }


    @Override
    public void setRefreshState(@IPullToLeftRefreshState int state) {

        if (state == mRefreshState)
            return;

        switch (state){
            case IPullToLeftRefreshState.STATE_NORMAL:
                if(getVisibility() == View.GONE)
                    setVisibility(VISIBLE);
                tv_moved_view.setText(mTextTip.mRefreshNormal);

                break;
            case IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH:
                if(getVisibility() == View.GONE) setVisibility(VISIBLE);
                tv_moved_view.setText(mTextTip.mReleaseFingerToRefresh);

                break;
            case IPullToLeftRefreshState.STATE_REFRESHING:
                if(getVisibility() == View.GONE) setVisibility(VISIBLE);
                tv_moved_view.setText(mTextTip.mRefreshing);

                break;
            case IPullToLeftRefreshState.STATE_DONE_FINISHED:
                if(getVisibility() == View.GONE) setVisibility(VISIBLE);
                tv_moved_view.setText(mTextTip.mRefreshComplete);

                break;
        }

        if (mIPullRefreshStateListener != null){
            mIPullRefreshStateListener.onPullRefreshState(mRefreshState, state);
        }

        mRefreshState = state;
    }

    @Override
    public int getRefreshState() {
        return mRefreshState;
    }

    @Override
    public void doInitRefreshState(@IPullToLeftRefreshState int state, boolean isArriveToMostRight) {


        if (isArriveToMostRight)
            smoothScrollTo(mMeasuredWidth);
        else
            smoothScrollTo(0);

        this.mRefreshState = state;
    }
}

当中涉及的四种状态的接口定义:

@Retention(RetentionPolicy.SOURCE)
@IntDef({
        IPullToLeftRefreshState.STATE_DONE_FINISHED,
        IPullToLeftRefreshState.STATE_NORMAL,
        IPullToLeftRefreshState.STATE_REFRESHING,
        IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH
})
//@Documented()
public @interface IPullToLeftRefreshState {
    /**
     * 正常状态下
     */
    int STATE_NORMAL = 10;
    /**
     * 松开手指去刷新
     */
    int STATE_RELEASE_TO_REFRESH = 11;
    /**
     * 正在刷新
     */
    int STATE_REFRESHING = 12;
    /**
     * 刷新完成
     */
    int STATE_DONE_FINISHED = 13;
}

跟随滑动而移动View需要实现的接口定义:

public interface IRefreshMovedViewLayout {

    void doToMove(float delta);

    boolean releaseAction();

    void refreshComplete();

    int getVisibleWidth();

    void setVisibleWidth(int value);

    void setRefreshState(@IPullToLeftRefreshState int state);// 设置状态

    @IPullToLeftRefreshState int getRefreshState();// 获取刷新状态


    void doInitRefreshState(@IPullToLeftRefreshState int state, boolean isArriveToMostRight);

}

父控件需要实现的接口定义:

public interface IPullToLeftRefresh {
    /**
     * 是否在最后能够进行向左拉
     * @return
     */
    boolean isCanPullToLeftDirector();

    /**
     * 完成刷新
     */
    void doWhatCompleteToRefresh();

    /**
     * 还原布局
     */
    void doWhatRecoverLayout();

    /**
     * 跟随移动的view
     */
    void setMoveViews(View movedView);
}

状态发生变化时调用的回调接口:

public interface IPullRefreshStateListener {

    void onPullRefreshState(@IPullToLeftRefreshState int fromState, @IPullToLeftRefreshState int toState);
}

你可能感兴趣的:(Android)