HorizontalScrollView滚动到具体位置

前言

最近接手的项目比较老,有一个水平滚动的效果是用HorizontalScrollView实现的,嗯就是很老,换我什么Recycleview、tablayout、viewpager、我都不会想到用它。然后产品改需求说要滚动到具体某个位置,于是我大概看了一下API有:

       horizontal_scroll.smoothScrollBy();
        horizontal_scroll.scrollTo();
        horizontal_scroll.smoothScrollTo();

设置一下发现居然不行。一下把我郁闷了。看了一下源码没有问题啊

Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
相比较scrollBy 是有一个滑动过渡的过程,而scroolTo 最终调用的也是scrollBy

在最无助的时候猜测一下是不是因为HorizontalScrollView要去测量子布局而我设置后测量没有完成被覆盖了呢,于是试了一下延迟设置居然可以了。

        int offset = ((int) DensityUtils.dpToPx(610f) - DensityUtils.getDisplayWidth(mContext)) / 2;
        new Handler().postDelayed(() -> horizontal_scroll.smoothScrollTo(offset, 0), 80);

问题是解决了但是我还是想证明一下自己的猜测于是决定大干一场看源码。

Rect方法

rect 方法可以简单理解为绘制规则形状的,而HorizontalScrollView很显然通过测量完内部布局后来完成绘制的,于是决定从它下手。

public class HorizontalScrollView extends FrameLayout {
    private static final int ANIMATED_SCROLL_GAP = ScrollView.ANIMATED_SCROLL_GAP;

    private static final float MAX_SCROLL_FACTOR = ScrollView.MAX_SCROLL_FACTOR;

    private static final String TAG = "HorizontalScrollView";

    private long mLastScroll;

    private final Rect mTempRect = new Rect();
    private OverScroller mScroller;
    private EdgeEffect mEdgeGlowLeft;
    private EdgeEffect mEdgeGlowRight;

一看我们的HorizontalScrollView 是继承自FrameLayout的这也说明了为什么它的直接布局必须是一个(不然肯定堆叠在一起了)
看了一下mTempRect也没有做什么特殊处理(没太看懂好像只是做了一些测量相关的)。
而自己猜测的绘制子布局导致时间的问题也被自己给pass掉了看了一下onMeasure方法


        if (getChildCount() > 0) {
            final View child = getChildAt(0);
            final int widthPadding;
            final int heightPadding;
            final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
            if (targetSdkVersion >= Build.VERSION_CODES.M) {
                widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
                heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
            } else {
                widthPadding = mPaddingLeft + mPaddingRight;
                heightPadding = mPaddingTop + mPaddingBottom;
            }

            int desiredWidth = getMeasuredWidth() - widthPadding;
            if (child.getMeasuredWidth() < desiredWidth) {
                final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                        desiredWidth, MeasureSpec.EXACTLY);
                final int childHeightMeasureSpec = getChildMeasureSpec(
                        heightMeasureSpec, heightPadding, lp.height);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }

很明显是测量父布局的我们知道HorizontalScrollView最外层是只有一个直接子布局测量它就OK了。
刚开始的两个猜想都被推翻了,于是决定曲线救国,看看smoothScrollTo是怎么实现的。

smoothScrollTo 调用了smoothScrollBy()而smoothScrollBy()则是加了动画的 scrollBy(dx, dy),最后scrollBy则是调用了scrollTo()

于是搜了一下scrollTo()瞬间顿悟了

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    .......
  }
            // Don't forget to clamp
            if (mScrollX > scrollRange) {
                mScrollX = scrollRange;
            } else if (mScrollX < 0) {
                mScrollX = 0;
            }
        }

        // Calling this with the present values causes it to re-claim them
        scrollTo(mScrollX, mScrollY);

是的我们设置的滑动的方法最后在onlayout里面设置了,这里就不难理解了,我们初始化HorizontalScrollView时会调用默认的 scrollTo(mScrollX, mScrollY);所以我们设置的horizontal_scroll.smoothScrollTo();并不会起作用。

你可能感兴趣的:(HorizontalScrollView滚动到具体位置)