Android 打造顶部停留控件,可用于所有可滚动的控件(ScrollView,ListView)

1、序言

现在很多App为了让一个页面可以有更多展示的东西。于是乎有一个界面就有几个tab进行切换页面,同时滚动的时候为了方便用户切换tab,这时tab需要悬浮在布局的顶部。所以这样就有了这篇blog咯…….

2、实现原理

控件的实现原理,相对来还是比较简单的:
1、首先自定义一个GroupView,实现滑动的效果,同时进行一些判断,比如:当满足一些条件时,把事件处理交给ChildView来处理;当ChildView满足一些条件时(比如ListView滚动到了第一条数据,ScrollView滚动到了顶部),让GroupView滚动,ChildView停止滚动。
2、然后自定义一个ChildView,这个可以是ListView、ScrollView等等可滚动的控件,重写onTouchEvent方法,进行判断查看是否可以滚动,因为是否可以滚动是由GroupView来控制的。
3、通过接口的方式把两者之间判断是否可以滚动联系起来。

3、实现代码

看逻辑不清楚可以跳过直接看代码:
首先是GroupView的代码:

public class SideGroupLayout extends ViewGroup {
   
    public static final String TAG = "android_xw";

    private int mTouchSlop;
    private float mLastMotionX;
    private float mLastMotionY;
    private boolean mIsBeingDragged;
    protected int mFirstItemHeight;
    private int mScrollY;
    public boolean mScrollToEnd;

    private VelocityTracker mVelocityTracker;
    private int mMinimumFlingVelocity;
    private int mMaximumFlingVelocity;

    private Scroller mScroller;
    private boolean mCanScroller;

    public SideGroupLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        mCanScroller = true;
        ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();

        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();

        mScroller = new Scroller(context);
        reset();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = 0;

        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                height += child.getMeasuredHeight();
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int height = 0;
        mFirstItemHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            if (view.getVisibility() != View.GONE) {
                view.layout(0, height, getWidth(), height + view.getMeasuredHeight());
                height += view.getMeasuredHeight();
                if (i == 0) {
                    mFirstItemHeight = height;
                }
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mFirstItemHeight == 0 || !mCanScroller) {
            mScrollToEnd = true;
            return super.onInterceptTouchEvent(ev);
        }

        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_MOVE: {
            final float x = ev.getX();
            final float y = ev.getY();
            final int xDiff = (int) Math.abs(x - mLastMotionX);
            final int yDiff = (int) Math.abs(y - mLastMotionY);
            // Log.i("TAG", "mScrollY == mFirstItemHeight:" + (mScrollY ==
            // mFirstItemHeight));
            if (mScrollY == mFirstItemHeight) {
                boolean isScrollY = yDiff > xDiff && y > mLastMotionY && mAction != null && mAction.isGroupScroll();
                return isScrollY;
            } else if (yDiff > mTouchSlop * 2 && yDiff >= xDiff) {
                mIsBeingDragged = true;
                mLastMotionY = y;
            }
            break;
        }
        case MotionEvent.ACTION_DOWN: {
            mLastMotionX = ev.getX();
            mLastMotionY = ev.getY();
            mIsBeingDragged = false;
            break;
        }
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            mIsBeingDragged = false;
            break;
        }
        return mIsBeingDragged;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mFirstItemHeight == 0 || !mCanScroller) {
            mScrollToEnd = true;
            return super.onTouchEvent(event);
        }
        addVelocityTracker(event);

        final int action = event.getAction();
        final float y = event.getY();
        final float x = event.getX();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            // 获取相对屏幕的坐标,即以屏幕左上角为原点
            break;
        case MotionEvent.ACTION_MOVE:
            final float scrollX = mLastMotionX - x;
            final float scrollY = mLastMotionY - y;
            onScroll((int) scrollX, (int) scrollY);
            scrollTo(
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值