前言
最近接手的項目比較老,有一個水平滾動的效果是用HorizontalScrollView實現(xiàn)的,嗯就是很老,換我什么Recycleview、tablayout、viewpager、我都不會想到用它。然后產(chǎn)品改需求說要滾動到具體某個位置,于是我大概看了一下API有:
horizontal_scroll.smoothScrollBy();
horizontal_scroll.scrollTo();
horizontal_scroll.smoothScrollTo();
設(shè)置一下發(fā)現(xiàn)居然不行。一下把我郁悶了??戳艘幌略创a沒有問題啊
Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
相比較scrollBy 是有一個滑動過渡的過程,而scroolTo 最終調(diào)用的也是scrollBy
在最無助的時候猜測一下是不是因為HorizontalScrollView要去測量子布局而我設(shè)置后測量沒有完成被覆蓋了呢,于是試了一下延遲設(shè)置居然可以了。
int offset = ((int) DensityUtils.dpToPx(610f) - DensityUtils.getDisplayWidth(mContext)) / 2;
new Handler().postDelayed(() -> horizontal_scroll.smoothScrollTo(offset, 0), 80);
問題是解決了但是我還是想證明一下自己的猜測于是決定大干一場看源碼。
Rect方法
rect 方法可以簡單理解為繪制規(guī)則形狀的,而HorizontalScrollView很顯然通過測量完內(nèi)部布局后來完成繪制的,于是決定從它下手。
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也沒有做什么特殊處理(沒太看懂好像只是做了一些測量相關(guān)的)。
而自己猜測的繪制子布局導(dǎo)致時間的問題也被自己給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是怎么實現(xiàn)的。
smoothScrollTo 調(diào)用了smoothScrollBy()而smoothScrollBy()則是加了動畫的 scrollBy(dx, dy),最后scrollBy則是調(diào)用了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);
是的我們設(shè)置的滑動的方法最后在onlayout里面設(shè)置了,這里就不難理解了,我們初始化HorizontalScrollView時會調(diào)用默認的 scrollTo(mScrollX, mScrollY);所以我們設(shè)置的horizontal_scroll.smoothScrollTo();并不會起作用。