Android support 23.2 BottomSheetBehaviorを使ったピット

4914 ワード

dim.red新しく出たdesign:23.0新しいBehaviorを導入しました:BottomSheetBehavior
に質問
設定BottomSheetBehaviorがPeekHeightまたはPeekHeightを0に設定していない場合.下部のBottomSheetBehaviorビューがスライドしません.
ぶんせき
ソースに入ることによって、具体的な問題は


@Override
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
    // First let the parent lay it out
    if (mState != STATE_DRAGGING && mState != STATE_SETTLING) {
        parent.onLayoutChild(child, layoutDirection);
    }
    // Offset the bottom sheet
    mParentHeight = parent.getHeight();
    mMinOffset = Math.max(0, mParentHeight - child.getHeight());
    mMaxOffset = mParentHeight - mPeekHeight;
    if (mState == STATE_EXPANDED) {
        ViewCompat.offsetTopAndBottom(child, mMinOffset);
    } else if (mHideable && mState == STATE_HIDDEN) {
        ViewCompat.offsetTopAndBottom(child, mParentHeight);
    } else if (mState == STATE_COLLAPSED) {
        ViewCompat.offsetTopAndBottom(child, mMaxOffset);
    }
    if (mViewDragHelper == null) {
        mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
    }
    mViewRef = new WeakReference<>(child);
    mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
    return true;
}

最初のviewの位置はスクリーンの中です.mStateの初期状態がSTATE_なのでCOLLAPSED、mPeekHeightは0、mMaxOffsetはrvの高さになったので、viewはすぐにスクリーンから外された.そして後ろのViewは全行程透明になった.奇抜だ.
ソリューション
ソリューション1
強制再描画を開始します

behavior.setBottomSheetCallback(new BottomSheet.BottomSheetCallback() {
    public boolean hasRequest;

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {

        if (!hasRequest && behavior.getPeekHeight() == 0 && slideOffset > 0) {
            hasRequest = true;
            bottomSheet.requestLayout();
        }
    }
});

欠点はrequestLayoutメソッドを使用することです.
ソリューション2
これはラッキーScienceが提供しています
 

bottomSheet.setTranslationY(statusbarheight);

behavior.setBottomSheetCallback(new BottomSheet.BottomSheetCallback() {
    public boolean hasRequest;

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {

        if (!hasRequest && behavior.getPeekHeight() == 0 && slideOffset > 0) {
            hasRequest = true;
            bottomSheet.setTranslationY(0);
        }
    }
});
TranslationYの差分値を設定ことにより.彼に再描画させる.Google Designパッケージの下に似たような解決策があることをぼんやり覚えています.同じようにTranslationYの差を使っています.
クラス:android.support.design.widget.ViewOffsetHelperメソッド:updateOffsets
 
private void updateOffsets() {
    ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));
    ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));

    // Manually invalidate the view and parent to make sure we get drawn pre-M
    if (Build.VERSION.SDK_INT < 23) {
        tickleInvalidationFlag(mView);
        final ViewParent vp = mView.getParent();
        if (vp instanceof View) {
            tickleInvalidationFlag((View) vp);
        }
    }
}

private static void tickleInvalidationFlag(View view) {
    final float y = ViewCompat.getTranslationY(view);
    ViewCompat.setTranslationY(view, y + 1);
    ViewCompat.setTranslationY(view, y);
}

23以下に存在する可能性のあるバグであることがわかる.同時に、ViewOffsetHelperは私有のクラスであり、updateOffsetsは私有の方法であることに注意しなければならない.だからcopyしか使えないし、直接使うことはできません.
最終案
シナリオ2に基づいてちょっとした修正をしてgoogleを使うシナリオです.
 
behavior.setBottomSheetCallback(new BottomSheet.BottomSheetCallback() {
    public boolean hasRequest;

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {

        if (!hasRequest && behavior.getPeekHeight() == 0 && slideOffset > 0) {
            hasRequest = true;
            updateOffsets(bottomSheet);
        }
    }
});

private void updateOffsets(View view) {

    // Manually invalidate the view and parent to make sure we get drawn pre-M
    if (Build.VERSION.SDK_INT < 23) {
        tickleInvalidationFlag(view)
        final ViewParent vp = view.getParent();
        if (vp instanceof View) {
            tickleInvalidationFlag((View) vp);
        }
    }
}

private static void tickleInvalidationFlag(View view) {
    final float y = ViewCompat.getTranslationY(view);
    ViewCompat.setTranslationY(view, y + 1);
    ViewCompat.setTranslationY(view, y);
}

尻尾
Googleはこれを開発する際にこのような応用シーンを考慮していないはずだ.シーンがそうであればBottomSheetDialogという代替を用いることができる.