实现View的简单滑动2

来源:互联网 发布:威海志成网络 编辑:程序博客网 时间:2024/06/08 11:09

上一篇把主要的要点说明了,这里只是稍微改变
实现View的 上下滑动1–简单实现

pulllayout改为了MonitorLayout(名字什么的不好取啦)
这个类的用途是把手指滑动状态暴露出去,不涉及到UI的部分

/** * 只用于滑动状态的监听,不管视图的变化 * 目的:将手指的滑动状态暴露出去 */public class MonitorLayout extends RelativeLayout {    private static final String TAG = "PullLayout";    /**     * 标记的pointY     * 滑动距离以此为原点计算     */    private int markPointY = 0;    /**     * 滑动手指ID     */    private int mScrollPointerId = -1;    /**     * 偏移位置     */    private int offsetY = 0;    /**     * 初始偏移量     * 当多个手指按下状态变换时,初始偏移量改变     */    private int initOffsetY = 0;    /**     * 需要监控View的滑动状态     */    private View scrollView;    private IBindChildScrollListener mChildScrollListener;    private IScrollChangeListener mScrollChangeListener;    /**     * scrollEnable = false时会执行super.dispatchEvent()     */    private boolean scrollEnable = true;    /**     * 当前状态,0-未偏移状态  1-偏移量大于0  -1-偏移量小于0     * <p>     * 因为滑动的offset在快速往返滑动不一定会有为0的状态,故使用此字段标志滑动状态的切换,以此来初始化标记点markPointY{@link #markPointY}     */    private static int scrollState = 0;    public MonitorLayout(@NonNull Context context) {        super(context);    }    public MonitorLayout(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public MonitorLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public void setScrollChangeListener(IScrollChangeListener mScrollChangeListener) {        this.mScrollChangeListener = mScrollChangeListener;    }    public void setScrollEnable(boolean scrollEnable) {        this.scrollEnable = scrollEnable;    }    public boolean isScrollEnable() {        return scrollEnable;    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        if (mChildScrollListener == null || scrollView == null)            return super.dispatchTouchEvent(event);        //是否允许滑动        if (!isScrollEnable()) return super.dispatchTouchEvent(event);        final int action = MotionEventCompat.getActionMasked(event);        final int actionIndex = MotionEventCompat.getActionIndex(event);        switch (action) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "ACTION_DOWN");                mScrollPointerId = event.getPointerId(0);// 获取索引为0的手指id                markPointY = (int) (event.getY() + 0.5f);                initOffsetY = mScrollChangeListener.currentOffsetY();//当前偏移量                scrollState = 0;                Log.e(TAG, "markPointY->" + markPointY);                break;            case MotionEvent.ACTION_POINTER_DOWN:                Log.e(TAG, "ACTION_POINTER_DOWN");                mScrollPointerId = event.getPointerId(actionIndex);                markPointY = (int) (event.getY(actionIndex) + 0.5f);                initOffsetY = mScrollChangeListener.currentOffsetY();                Log.e(TAG, "markPointY->" + markPointY);                break;            case MotionEvent.ACTION_MOVE:                final int index = event.findPointerIndex(mScrollPointerId);                if (index < 0) {                    Log.e(TAG, "Error processing scroll; pointer index for id " + mScrollPointerId + " not found. Did any MotionEvents get skipped?");                    return false;                }                offsetY = (int) (event.getY(index) - markPointY);//偏移距离                Log.e(TAG, "---A--initOffsetY-->" + initOffsetY);                /*----- // TODO: 2017/6/9 0009 阻尼效果-简单除2,高级一点的自己去实现-----*/                offsetY = offsetY / 2 + initOffsetY;                /*-----------*/                Log.e(TAG, "---A--offsetY-->" + offsetY + "     ddd" + mChildScrollListener.canScrollDown());                if (offsetY > 0 && !mChildScrollListener.canScrollUp()) {                    //Log.e(TAG, "dispatchTouchEvent----A->" + offsetY);                    if (scrollState <= 0) {                        markPointY = (int) (event.getY(index) + 0.5f);                        scrollState = 1;                        //开始下拉刷新                        sendCancelEvent(event);                        return true;                    }                    sendScrollOffsetY(offsetY);                    return true;                } else if (offsetY < 0 && !mChildScrollListener.canScrollDown()) {                    Log.d(TAG, "dispatchTouchEvent----B->" + offsetY);                    if (scrollState >= 0) {                        markPointY = (int) (event.getY(index) + 0.5f);                        scrollState = -1;                        //开始上拉加载                        sendCancelEvent(event);                        return true;                    }                    Log.e(TAG, "---A--offsetY-->" + offsetY);                    sendScrollOffsetY(offsetY);                    return true;                } else if (scrollState != 0) {                    Log.d(TAG, "scrollState-->" + scrollState);                    scrollState = 0;                    initOffsetY = mScrollChangeListener.currentOffsetY();                    sendScrollOffsetY(0);                    sendDownEvent(event);                    return true;                }                break;            case MotionEvent.ACTION_POINTER_UP:                onPointerUp(event);                break;            case MotionEvent.ACTION_CANCEL:            case MotionEvent.ACTION_UP:                if (scrollState == 0) break;                if (mScrollChangeListener != null) mScrollChangeListener.scrollEnd(offsetY);                break;        }        return super.dispatchTouchEvent(event);    }    private void sendScrollOffsetY(int offsetY) {        Log.d(TAG, "sendScrollOffsetY-->" + offsetY);        if (mScrollChangeListener != null) mScrollChangeListener.scrollChanged(offsetY);    }    /*-------------重新设置targeView的aCTION_CANCEL和ACTION_DOWN事件- 这段代码copy自PtrFrameLayout-----------*/    private void sendCancelEvent(MotionEvent event) {        if (event == null) return;        MotionEvent last = event;        MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, last.getX(), last.getY(), last.getMetaState());        dispatchTouchEventSupper(e);    }    private void sendDownEvent(MotionEvent event) {        if (event == null) return;        final MotionEvent last = event;        MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(), last.getY(), last.getMetaState());        dispatchTouchEventSupper(e);    }    public boolean dispatchTouchEventSupper(MotionEvent e) {        return super.dispatchTouchEvent(e);    }    /*-----------------------------------*/    /**     * 这里不能使用onInterceptTouchEvent     * onInterceptTouchEvent在一次按下滑动 拦截事件后就不会被执行了。(可以理解为在action_move事件开始的很短时间内会执行,之后就不会执行了。)     */    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        Log.d(TAG, "onInterceptTouchEvent");        return super.onInterceptTouchEvent(ev);    }    /**     * 如果onInterceptTouchEvent 没有返回true,onTouchEvent是不会执行的     */    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d(TAG, "onTouchEvent");        return super.onTouchEvent(event);    }    /**     * 手指抬起,参考RecyclerView{@link android.support.v7.widget.RecyclerView#onPointerUp(MotionEvent)}     *     * @param e     */    private void onPointerUp(MotionEvent e) {        final int actionIndex = MotionEventCompat.getActionIndex(e);        if (e.getPointerId(actionIndex) == mScrollPointerId) {            // Pick a new pointer to pick up the slack.            final int newIndex = actionIndex == 0 ? 1 : 0;            mScrollPointerId = e.getPointerId(newIndex);            markPointY = (int) (e.getY(newIndex) + 0.5f);            initOffsetY = mScrollChangeListener.currentOffsetY();        }    }    /**     * 设置targeView     *     * @param scrollView     */    public void setScrollView(View scrollView) {        this.scrollView = scrollView;    }    /**     * 设置ChildScrollListener     *     * @param mChildScrollListener     */    public void setChildScrollListener(IBindChildScrollListener mChildScrollListener) {        this.mChildScrollListener = mChildScrollListener;    }    public View getScrollView() {        return scrollView;    }}

通过IScrollChangeListener 来检测view的滑动状态,并修改View的状态。这里面有一个currentOffsetY();这个东西我也觉得很danteng。不好设计了,它的用处不好说明,字面意思是当前的偏移量,至于记录的是那个的偏移量,还需要根据具体View来判断。

/** * Created by itzhu on 2017/6/9 0009. * desc */public interface IScrollChangeListener {    /**     * @param offsetY 偏移量     */    void scrollChanged(int offsetY);    void scrollEnd(int offsetY);    /**     * 当前偏移量     */    int currentOffsetY();}

刷新的视图部分,实现IRefreshView接口就好了

/** * Created by itzhu on 2017/6/13 0013. * desc */public interface IRefreshView {    void move(int offsetY);    void end(int offsetY);    void changeHeaderView(int state);    void changeFooterView(int state);    int getHeaderHeight();    int getFooterHeight();    View getHeaderView();    View getFooterView();    View getView();}

使用上面的代码很容易实现一个上下拉动的View

** * Created by itzhu on 2017/6/14 0014. * desc 重新编写pulllayout */public class RePullLayout extends MonitorLayout implements IScrollChangeListener {    private IScrollChangeListener mScrollChangeListener;    public RePullLayout(@NonNull Context context) {        super(context);        init();    }    public RePullLayout(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public RePullLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public void init() {        super.setScrollChangeListener(this);    }    @Override    public void setScrollChangeListener(IScrollChangeListener mStateChangeListener) {        this.mScrollChangeListener = mStateChangeListener;    }    @Override    public void scrollChanged(int offsetY) {        getScrollView().setTranslationY(offsetY);        if (this.mScrollChangeListener != null) this.mScrollChangeListener.scrollChanged(offsetY);    }    @Override    public void scrollEnd(int offsetY) {        AnimationUtil.smoothTo(getScrollView(), 0, 150);        if (this.mScrollChangeListener != null) this.mScrollChangeListener.scrollEnd(offsetY);    }    @Override    public int currentOffsetY() {        return (int) (getScrollView().getTranslationY() + 0.5f);    }}

这个的用处就是实现各种View的上下拉动。
也可以用来实现上拉刷新与下拉加载。
DEMO的话请看https://github.com/yanjingzj/commondemo

不讨论item的横向滑动,如果涉及到item的滑动菜单,又加上上下拉动,个人建议还是是直接继承recyclerView或者listView来实现。
demo里面有下拉刷新与上拉加载,类似https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh 这样的,但是不会有多个手指滑动的问题。

没有考虑item的左右滑动,后面有时间再看。

原创粉丝点击