android 悬停控件 StickyScrollView 源码分析

前言

在看了代码后,主要是会有以下的几个问题:

一、如何实现悬停操作的。

二、如何实现悬停后事件的点击的。

三、scrollview都提供了什么方法,来支持这些内容的实现。

四、view的基础知识getPaddingTop、getPaddingTop、getScrollY的学习的使用

一、定义的变量

redirectTouchesToStickyView 用来定义是否触摸stickView
getTopForViewRelativeOnlyChild,获取代码的的top
private int getTopForViewRelativeOnlyChild(View v) {
    int top = v.getTop();
    while (v.getParent() != getChildAt(0)) {
        v = (View) v.getParent();
        top += v.getTop();
    }
    return top;
}

二、滚动时的操作

2.1定义了两个view

一个是viewThatShouldStick 这个是用来悬停显示的,一个是 approachingView ,是用来替换悬停显示的。

2.2 对两个view进行赋值操作

   首先会循环stickyViews,来获取当前的需要悬停的控件,然后根据

int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop());

来获取当前滚动的悬停控制距离顶部的top,如果是小于0的,表示已经移动到了顶部,这时赋值为第一个悬停控件viewThatShouldStick ,否则为第二个要悬停的控件approachingView

2.3 viewThatShouldStick 进行绘制操作

如果viewThatShouldStick不能为空,调用startStickingView方法,在这里把viewThatShouldStick 赋值给了currentlyStickingView,并调用了post的方法进行重新绘制,从而进入了第三步的分析。

用来获取父控件的top,在这里学到了一个技巧,就是如果获取一个子控件相对屏幕的top的大小。

private int getTopForViewRelativeOnlyChild(View v) {
    int top = v.getTop();
    while (v.getParent() != getChildAt(0)) {
        v = (View) v.getParent();
        top += v.getTop();
    }
    return top;
}

完整的代码块

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

    private void doTheStickyThing() {
        View viewThatShouldStick = null;
        View approachingView = null;
        for (View v : stickyViews) {
            int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop());
            if (viewTop <= 0) {
                if (viewThatShouldStick == null || viewTop > (getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) {
                    viewThatShouldStick = v;
                }
            } else {
                if (approachingView == null || viewTop < (getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) {
                    approachingView = v;
                }
            }
        }
        if (viewThatShouldStick != null) {
            stickyViewTopOffset = approachingView == null ? 0 : Math.min(0, getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight());
            if (viewThatShouldStick != currentlyStickingView) {
                if (currentlyStickingView != null) {
                    stopStickingCurrentlyStickingView();
                }
                // only compute the left offset when we start sticking.
                stickyViewLeftOffset = getLeftForViewRelativeOnlyChild(viewThatShouldStick);
                startStickingView(viewThatShouldStick);
            }
        } else if (currentlyStickingView != null) {
            stopStickingCurrentlyStickingView();
        }
    }

 

三、绘画时的操作

3.1 重写了dispatchDraw方法

重写了dispatchDraw方法,查资料得知,viewgroup一般是重写这个方法,是用来重画子控件的,如果是重写 onDraw,有时间viewgroup可能不会调用。

3.2实现悬停

这里是实现悬停的主要代码。在这里我们可以看到,canvas.translate就是实现悬停的代码。

   canvas.translate(getPaddingLeft() + stickyViewLeftOffset, getScrollY() + 
stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0));

3.2.1 基础知识 getPaddingLeft,getPaddingTop,stickyViewLeftOffset

getPaddingLeft,getPaddingTop,是用来获取系统的边距的,getScrollY()是用来滚动距离的。stickyViewLeftOffset是

getLeftForViewRelativeOnlyChild(viewThatShouldStick);getScrollY 是view控件中的一个获取view滚动的y的距离的一个方法,这个也是一个重要的方法,可以获取到控件滚动的距离。

3.2.2 基础知识 clippingToPadding,摒弃父控件padding

  clippingToPadding 这里也对是否在padding绘制进行了判断,这个不熟悉的可以查查资料,一个非常有用的,这里我直接引用了网上的资源。有感兴趣的伙伴可以详情学习下,感觉现在这种用法还是挺多的。

属性文字描述
android:clipChildrenclipChildren表示是否限制子View在其范围内,在animations动画以及APP主界面底部导航栏中发挥很大的作用, 默认情况下,clipChild为true。 也就是不允许进行扩展绘制。那么视图的显示就会受到限制。我们将其值设置为false后,当子控件的高度高于父控件时也会完全显示,而不会被压缩
android:clipToPaddingclipToPadding用来定义ViewGroup是否允许在padding中绘制。默认情况下,cliptopadding被设置为ture, 也就是把padding中的值都进行裁切了,如图片超出边界后被裁剪。

 3.2.3 currentlyStickingView.draw(canvas) 完成悬停重绘

完整的代码如下

protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (currentlyStickingView != null) {
            canvas.save();
            canvas.translate(getPaddingLeft() + stickyViewLeftOffset, getScrollY() + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0));

            canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0),
                    getWidth() - stickyViewLeftOffset,
                    currentlyStickingView.getHeight() + mShadowHeight + 1);

            if (mShadowDrawable != null) {
                int left = 0;
                int right = currentlyStickingView.getWidth();
                int top = currentlyStickingView.getHeight();
                int bottom = currentlyStickingView.getHeight() + mShadowHeight;
                mShadowDrawable.setBounds(left, top, right, bottom);
                mShadowDrawable.draw(canvas);
            }

            canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(), currentlyStickingView.getHeight());
            if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) {
                showView(currentlyStickingView);
                currentlyStickingView.draw(canvas);
                hideView(currentlyStickingView);
            } else {
                currentlyStickingView.draw(canvas);
            }
            canvas.restore();
        }
    }

四、事件拦截机制

暂时没有写,以后补充

 

 

 

引用:https://blog.csdn.net/wangjiang_qianmo/article/details/54604378

https://blog.csdn.net/linmiansheng/article/details/17767795

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值