AutoScrollTopBottomView

这个自定义控件是用来展示底层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);
    }

}

效果图

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货园

Android自定义View之TitleBar,通用标题栏

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/49...

2322
来自专栏学海无涯

Android开发之ViewPager+Fragment+FragmentTabHost实现底部菜单

在Android开发中,底部菜单是经常要使用的,如微信、微博、支付宝等,实现底部菜单有好几种方式,大致分为: 通过TabWidget实现 隐藏TabWidget...

4904
来自专栏Android干货

Android项目实战(五):TextView自适应大小

38112
来自专栏潇涧技术专栏

Android Heroes Reading Notes 2

《Android群英传》读书笔记 (2) 第三章 控件架构与自定义控件详解 + 第四章 ListView使用技巧 + 第五章 Scroll分析

971
来自专栏Jack的Android之旅

模仿企鹅FM播放主页面滑动动态改变各视图的大小

国庆的一个任务就是把自己之前写的代码搬到博客。这次给各位带来的是通过滑动来动态改变各个View的大小进而达到企鹅FM播放页面的滑动效果(仅仅是滑动效果),老规矩...

1042
来自专栏向治洪

android galley实现画廊效果

今天在做一个软件界面时用到了ImageSwitcher和Gallery控件,在看API时,感觉上面的例子讲的不是很具体,效率并不高。在这里我就以一个图片浏览功能...

2049
来自专栏Android Note

Android - ViewDragHelper实现京东、淘宝拖拽详情

1644
来自专栏项勇

笔记25 | 通过自定义VIEW实现一个圆盘转动UI

2067
来自专栏向治洪

ListView专题

ListView专题 1.ListView属性: fadingEdge属性 ListView上边和下边有黑色的阴影,android : fadingEdge ...

1938
来自专栏Android干货

关于安卓开发实现进度条对话框

5187

扫码关注云+社区

领取腾讯云代金券