前言
在看了代码后,主要是会有以下的几个问题:
一、如何实现悬停操作的。
二、如何实现悬停后事件的点击的。
三、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:clipChildren | clipChildren表示是否限制子View在其范围内,在animations动画以及APP主界面底部导航栏中发挥很大的作用, 默认情况下,clipChild为true。 也就是不允许进行扩展绘制。那么视图的显示就会受到限制。我们将其值设置为false后,当子控件的高度高于父控件时也会完全显示,而不会被压缩 |
android:clipToPadding | clipToPadding用来定义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