仿美团滑动后头部固定源码分析

public class StickyScrollView extends ScrollView {
    private static final String STICKY = "sticky";
    private View mCurrentStickyView;
    private Drawable mShadowDrawable;
    /**
     * 存放tagstickyView集合
     */
    private List mStickyViews;
    private int mStickyViewTopOffset;
    private int defaultShadowHeight = 10;
    private float density;
    private boolean redirectTouchToStickyView;

    /**
     * 当点击Sticky的时候,实现某些背景的渐变
     */
    private Runnable mInvalidateRunnable = new Runnable() {

        @Override
        public void run() {
            if (mCurrentStickyView != null) {
                int left = mCurrentStickyView.getLeft();
                int top = mCurrentStickyView.getTop();
                int right = mCurrentStickyView.getRight();
                int bottom = getScrollY() + (mCurrentStickyView.getHeight() + mStickyViewTopOffset);
                invalidate(left, top, right, bottom);
            }
            postDelayed(this, 50);
        }
    };

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

    public StickyScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mShadowDrawable = context.getResources().getDrawable(R.drawable.sticky_shadow_default);
        mStickyViews = new LinkedList();
        density = context.getResources().getDisplayMetrics().density;
    }

    /**
     * 找到设置tagView
     *
     * @param viewGroup
     */
    private void findViewByStickyTag(ViewGroup viewGroup) {
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = viewGroup.getChildAt(i);

            if (getStringTagForView(child).contains(STICKY)) {
                mStickyViews.add(child);
            }

            if (child instanceof ViewGroup) {
                findViewByStickyTag((ViewGroup) child);
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            findViewByStickyTag((ViewGroup) getChildAt(0));
        }
        showStickyView();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        showStickyView();
    }

    /**
     * 总结:
     * 只有一个StickyView的情况下
     * topOffset > 0(getScrollY() < stickyView.getTop()) curStickyView = null;
     * topOffset <= 0(getScrollY() >= stickyView.getTop()) curStickyView = view;
     */
    private void showStickyView() {
        View curStickyView = null;
        for (View v : mStickyViews) {
            int topOffset = v.getTop() - getScrollY();
            // 滑动距离开始大于v.getTop(),StickyView即将被滑出屏幕,CurrentStickyView即将被绘制出来
            if (topOffset <= 0) {
                if (curStickyView == null) {
                    curStickyView = v;
                }
            }
        }
        if (curStickyView != null) {
            // 悬浮框出现之前curStickyView一直为null, 出现之后不为null
            mStickyViewTopOffset = 0;
            mCurrentStickyView = curStickyView;
            post(mInvalidateRunnable);
        } else {
            mCurrentStickyView = null;
            removeCallbacks(mInvalidateRunnable);
        }
    }

    private String getStringTagForView(View v) {
        Object tag = v.getTag();
        return String.valueOf(tag);
    }

    /**
     * sticky画出来
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mCurrentStickyView != null) {
            //先保存起来
            canvas.save();
            //将坐标原点移动到(0, getScrollY() + mStickyViewTopOffset)
            canvas.translate(0, getScrollY() + mStickyViewTopOffset);
            // 绘制StickyView
            canvas.clipRect(0, mStickyViewTopOffset, mCurrentStickyView.getWidth(), mCurrentStickyView.getHeight());
            mCurrentStickyView.draw(canvas);
            // 绘制阴影
            if (mShadowDrawable != null) {
                int left = 0;
                int top = mCurrentStickyView.getHeight() + mStickyViewTopOffset;
                int right = mCurrentStickyView.getWidth();
                int bottom = top + (int) (density * defaultShadowHeight + 0.5f);
                mShadowDrawable.setBounds(left, top, right, bottom);
                mShadowDrawable.draw(canvas);
            }
            //重置坐标原点参数
            canvas.restore();
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            redirectTouchToStickyView = true;
        }
        // 按下事件开始处理
        if (redirectTouchToStickyView) {
            // 头部布局是否为空
            redirectTouchToStickyView = mCurrentStickyView != null;
            // 判断是否点击的头部布局的范围
            if (redirectTouchToStickyView) {
                redirectTouchToStickyView = ev.getY() <= (mCurrentStickyView
                        .getHeight() + mStickyViewTopOffset)
                        && ev.getX() >= mCurrentStickyView.getLeft()
                        && ev.getX() <= mCurrentStickyView.getRight();
            }
        }
        if (redirectTouchToStickyView) {
            // 事件触摸位置设置到原StickyView的对应位置上
            ev.offsetLocation(0, -1 * ((getScrollY() + mStickyViewTopOffset) - mCurrentStickyView.getTop()));
        }
        return super.dispatchTouchEvent(ev);
    }

}

你可能感兴趣的:(android-UI)