AutoScrollTopBottomView

来源:互联网 发布:软件开发常用模型 编辑:程序博客网 时间:2024/06/05 10:52

这个自定义控件是用来展示底层view的,当顶层的view滑动到底层view的中间时,顶层view会自动滚动到底层view的顶部或者底部,顶层view可以是scrollview,listview等. 希望对大家有帮助.

github地址:https://github.com/X-FAN/AutoScrollTopBottomView 欢迎star

下面附上源码,代码思路很简单利用Scroller进行滚动处理.

public class AutoScrollTopBottomView extends RelativeLayout {    private final int ANI_TIME = 800;    private float mLastActionDownY;    private View mBottomView;    private ViewGroup mTopView;    private Scroller mScroller;    private MotionEvent mLastMoveEvent;    public AutoScrollTopBottomView(Context context) {        super(context);        mScroller = new Scroller(context);    }    public AutoScrollTopBottomView(Context context, AttributeSet attrs) {        super(context, attrs);        mScroller = new Scroller(context);    }    public AutoScrollTopBottomView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mScroller = new Scroller(context);    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() != 2) {            throw new IllegalStateException("only and should contain two child view");        }        mBottomView = getChildAt(0);        if (!(getChildAt(1) instanceof ViewGroup)) {            throw new IllegalStateException("top view should be contained by a viewgroup");        }        mTopView = (ViewGroup) getChildAt(1);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        View view = null;        if (mTopView.getChildCount() > 0) {            view = mTopView.getChildAt(0);        }        if (view != null) {            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    mLastActionDownY = ev.getRawY();                    break;                case MotionEvent.ACTION_MOVE:                    float distance = ev.getRawY() - mLastActionDownY;                    mLastActionDownY = ev.getRawY();                    mLastMoveEvent = ev;                    if (!mScroller.computeScrollOffset()) {                        if (distance > 0 && isViewAtTop(view)) {//pull down                            if (Math.abs(mTopView.getScrollY() - distance) > mBottomView.getMeasuredHeight()) {//avoid out of bottom boundary                                mTopView.scrollBy(0, -mTopView.getScrollY() - mBottomView.getMeasuredHeight());                            } else {                                mTopView.scrollBy(0, (int) -distance);                            }                            sendCancelEvent();                            return true;                        } else if (distance < 0 && !isViewAtTop(mTopView)) {//pull up                            if ((distance - mTopView.getScrollY()) < 0) {//avoid out of top boundary                                mTopView.scrollBy(0, -mTopView.getScrollY());                            } else {                                mTopView.scrollBy(0, (int) -distance);                            }                            sendCancelEvent();                            return true;                        }                    } else {                        sendCancelEvent();                        return true;                    }                    break;                case MotionEvent.ACTION_CANCEL:                case MotionEvent.ACTION_UP:                    if (isInUp()) {//prepare scroll to top                        mScroller.startScroll(mTopView.getScrollX(), mTopView.getScrollY(), 0, -mTopView.getScrollY(), ANI_TIME);                    } else if (isInDown()) {//prepare scroll to bottom                        mScroller.startScroll(mTopView.getScrollX(), mTopView.getScrollY(), 0, -mTopView.getScrollY() - mBottomView.getMeasuredHeight(), ANI_TIME);                    }                    invalidate();                    break;                default:                    break;            }        }        return super.dispatchTouchEvent(ev);    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            mTopView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            invalidate();        }    }    /**     * detect top view in top half of bottom view     *     * @return     */    private boolean isInUp() {//在上半部分内        int y = -mTopView.getScrollY();        if (y > 0 && y < mBottomView.getMeasuredHeight() / 2) {            return true;        }        return false;    }    /**     * detect top view in bottom half of bottom view     *     * @return     */    private boolean isInDown() {//在下半部分内        int y = -mTopView.getScrollY();        if (y >= mBottomView.getMeasuredHeight() / 2 && y < mBottomView.getMeasuredHeight()) {            return true;        }        return false;    }    private boolean isViewAtTop(View view) {        if (view instanceof AbsListView) {//这里可以自己更改代码,判断listview等在什么情况下为拉到顶部,默认为第一个item可见的时候            final AbsListView absListView = (AbsListView) view;            return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() == 0 && absListView.getChildAt(0).getTop() >= absListView.getPaddingTop());        } else {            return view.getScrollY() == 0;        }    }    /**     * 滑动过程调用,解决滑动与其他事件冲突     * solve conflict move event between other event     */    private void sendCancelEvent() {        if (mLastMoveEvent == null) {            return;        }        MotionEvent last = mLastMoveEvent;        MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, last.getX(), last.getY(), last.getMetaState());        super.dispatchTouchEvent(e);    }}

效果图

auto.gif

0 0
原创粉丝点击