Windows centos php 开源 mysql java Ubuntu apache 编程 wordpress 云计算 程序员 Python nginx Android google 微软 linux Firefox shell

AppBarLayout的越界滾動行為

  • 原文鏈接 : Overscroll AppBarLayout Behavior
    原文作者 : Nikola Despotoski
    譯文出自 : 開發技術前線 www.devtf.cn。未經允許,不得轉載!
    譯者 : liuling07
    校對者: desmond1121
    狀態 : 完成
    很不幸,Youtube音樂應用在我們國家不可使用,我嘗試著通過各種盜版網站來獲取該應用,但我仍然無法看到在這個應用上發生了什麽。感謝這位redditor,在我的請求下,他在/r/materialdesign打開了一個thread並且發表一段錄制的視頻,我才有機會看到這個行為。

Youtube视频app的真实截图,可能的行为Youtube视频app的真实截图,可能的行为

根據我所看到的,我首先想到的就是專輯封面是放到一個AppBarLayout裏面,並且在滾動區域拖到邊界的時候尺寸會發生變化。讓我們假定這個 猜想是正確的並且用“Behavior”這個術語表示它。依鄙人之見,如果我的猜想是正確的,谷歌應該會在Material Design文檔的滾動部分提供一個越界滾動的使用說明。

我們的目標就是保證AppBarLayout.Behavior的完整性,在此基礎上再創建一個擴展的行為。因此:

public class OverscrollScalingViewAppBarLayoutBehavior extends AppBarLayout.ScrollingViewBehavior
因為這是默認的AppBarLayout.Behavior,所以建議只有在依賴視圖是AppBarLayout的時候起作用。

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
 return dependency instanceof AppBarLayout;
}
接下來,我們需要獲取想要在拖到邊界時要改變尺寸的視圖的一個實例。最好的方法就是在onLayoutChild()方法中獲取:

@Override
public boolean onLayoutChild(CoordinatorLayout parent ....) {
    boolean superLayout = super.onLayoutChild(parent, abl, layoutDirection);
    if (mTargetScalingView == null) {
        mTargetScalingView = parent.findViewByTag(TAG);
        if(mTargetScalingView != null){
             mScaleImpl.obtainInitialValues();
         }
     }
    return superLayout;
}
而且我們需要保證只有在垂直滾動的時候起作用:

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,... int nestedScrollAxes) {
    return nestedScrollAxes == View.SCROLL_AXIS_VERTICAL;
}
如果我們先前沒有在程序中顯示設置,會設置ViewScaler為默認的Scaler。

在內容滾動的瞬間,真正重要的問題就有頭緒了。CoordinatorLayout.Behavior提供了一個onNestedScroll() 方法,當滾動進行的時候這個方法會被調用,並且當內容滾動到邊界的時候也會調用。最後兩個參數dyUnconsumed和dxUnconsumed提供了 未被該行為的目標視圖填滿的像素值。

這個方法對我們實現尺寸改變來說太重要了。所以我列出了哪些情況需要改變尺寸,哪些情況不需要:

需要改變尺寸

存在未填滿的像素,如dyUnconsumed小於0
AppBarLayout是展開的,getTopAndBottomOffset() >= mScaleImpl.getInitialParentBottom()
不需要改變尺寸

AppBarLayout中沒有子視圖可以改變尺寸
有填充的像素,如dyConsumed不等於

 

@Override
public void onNestedScroll(CoordinatorLayout ... int dxUnconsumed, int dyUnconsumed) {
    if (mTargetScalingView == null || dyConsumed != 0) {
        mScaleImpl.cancelAnimations();
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        return;
    }

    if (dyUnconsumed < 0 && getTopAndBottomOffset() >= mScaleImpl.getInitialParentBottom()) {
        int absDyUnconsumed = Math.abs(dyUnconsumed);
        mTotalDyUnconsumed += absDyUnconsumed;
        mTotalDyUnconsumed = Math.min(mTotalDyUnconsumed, mTotalTargetDyUnconsumed);
        mScaleImpl.updateViewScale();
    } else {
        mTotalDyUnconsumed = 0;
        mScaleImpl.setShouldRestore(false);
        if (dyConsumed != 0) {
            mScaleImpl.cancelAnimations();
        }
        super.onNestedScroll(coordinatorLayout, .... dxUnconsumed, dyUnconsumed);
    }
}

 

当嵌套的overscroll停止的时候,我们需要将视图的边界和大小重置到它们的原始值。

@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
    mScaleImpl.retractScale();
    super.onStopNestedScroll(coordinatorLayout, child, target);

}

ViewScaler

這個類實現了AppBarLayout應該如何改變它的底部以及視圖應該 如何改變尺寸的邏輯。大多數行為都依賴累積的未填充的像素。我們可以為最大累積值設置一個約束值,這樣可以很容的找到要如何改變AppBarLayout 底部和改變視圖的尺寸。ParentScaler是ViewScaler的父類,它能讓AppBarLayout近乎平滑的改變尺寸。我就不在這裏貼大量 代碼了,如果你有興趣,可以從這裏獲取代碼。

延伸阅读

    评论