android - 调整主要布局内容以适合 Bottom Sheet ?

标签 android android-layout android-coordinatorlayout bottom-sheet

我正在尝试创建一个布局,其中包含一个固定 Bottom Sheet 和一个提供主要内容的 NestedScrollView。不幸的是,当 Bottom Sheet 展开时,它会遮挡 NestedScrollView 并且无法访问它的底部项目。 ?

调整主 NestedScroll 大小以适应展开的 Bottom Sheet 的最佳方法是什么?

举个例子:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <!-- I want this view resized dependent on bottom sheet expansion -->
    <android.support.v4.widget.NestedScrollView
        android:id="@+id/layout_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

       <!-- content goes here -->

    </android.support.v4.widget.NestedScrollView>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/layout_bottomsheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

        <!-- bottom sheet content goes here -->

    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

最佳答案

有一种方法可以获取您需要避免遮挡 Bottom Sheet 后面的 View 并处理其触摸的内容。

您需要扩展一个 BottomSheetDialogFragment

public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment {

    @Override
    public void onStart() {
        super.onStart();
        if(getDialog() != null && getDialog().getWindow() != null) {
        getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    }

在 onStart() 中,您可以移除暗淡的背景,以便您的后 View 清晰。

@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    View rootView = View.inflate(getContext(), R.layout.your_bottom_sheet_layout, null);
    dialog.setContentView(rootView);
    dialog.setCanceledOnTouchOutside(false);
    // set your children views

    dialog.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialogInterface) {
            // Do stuff when the dialog is visible on screen
            Window window = getDialog().getWindow();
            if(window != null) {
                window.setCallback(windowCallback);
            }
        }
    });
}

在 setupDialog() 中扩充您的布局并避免通过设置窗口回调来处理的任何外部触摸关闭对话框,如下所示:

private Window.Callback windowCallback = new Window.Callback() {

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            onBackPressed();
        }
        return false;
    }

    @Override
    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return onDispatchTouchEvent(event);
    }

    @Override
    public boolean dispatchTrackballEvent(MotionEvent event) {
        return false;
    }

    @Override
    public boolean dispatchGenericMotionEvent(MotionEvent event) {
        return false;
    }

    @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        return false;
    }

    @Nullable
    @Override
    public View onCreatePanelView(int featureId) {
        return null;
    }

    @Override
    public boolean onCreatePanelMenu(int featureId, Menu menu) {
        return false;
    }

    @Override
    public boolean onPreparePanel(int featureId, View view, Menu menu) {
        return false;
    }

    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        return false;
    }

    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        return false;
    }

    @Override
    public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {

    }

    @Override
    public void onContentChanged() {

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {

    }

    @Override
    public void onAttachedToWindow() {

    }

    @Override
    public void onDetachedFromWindow() {

    }

    @Override
    public void onPanelClosed(int featureId, Menu menu) {

    }

    @Override
    public boolean onSearchRequested() {
        return false;
    }

    @Override
    public boolean onSearchRequested(SearchEvent searchEvent) {
        return false;
    }

    @Nullable
    @Override
    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
        return null;
    }

    @Nullable
    @Override
    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
        return null;
    }

    @Override
    public void onActionModeStarted(ActionMode mode) {

    }

    @Override
    public void onActionModeFinished(ActionMode mode) {

    }
};

通过设置此回调,您需要管理每一次触摸。这有点棘手,但你可以获得目标。 onDispatchTouchEvent(event) 必须是这样的:

@Override
protected boolean onDispatchTouchEvent(MotionEvent event) {

    // check if the dialog is being touched
    float eventX = event.getRawX();
    float eventY = event.getRawY();
    int location[] = new int[2];
    yourDialogRootView.getLocationOnScreen(location);
    boolean isDialogBeingTouched = eventX > (location[0] - leftPaddingX)
            && eventX < (location[0] + view.getWidth())
            && eventY > (location[1])
            && eventY < (location[1] + view.getHeight());

    if (isDialogBeingTouched) {
        checkForAClick(event);
    }

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (isDialogBeingTouched) {
                // the dialog is touched so handle its touch
                return true;
            }
    }

    // dialog not touched so let the activity handle the touch normally
    return getActivity().dispatchTouchEvent(event);
}

checkForAClick(event) 应该是这样的:

protected boolean checkForAClick(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getX();
            startY = event.getY();
            break;
        case MotionEvent.ACTION_UP:
            float endX = event.getX();
            float endY = event.getY();
            if (isAClick(startX, endX, startY, endY)) {
                // handle click on your dialog as you prefer
                onBottomSheetClick(event);
            }
            break;
    }
    return false;
}

private static int CLICK_ACTION_THRESHOLD = 50;

public boolean isAClick(float startX, float endX, float startY, float endY) {
    float differenceX = Math.abs(startX - endX);
    float differenceY = Math.abs(startY - endY);
    if (differenceX > CLICK_ACTION_THRESHOLD || differenceY > CLICK_ACTION_THRESHOLD) {
        return false;
    }
    return true;
}

我知道这有点长,但我希望它能有所帮助。

关于android - 调整主要布局内容以适合 Bottom Sheet ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47580358/

相关文章:

java - 在没有主节点的情况下,在 android 中从 JSON 生成 ListView ?

android - 你如何在android中实现大滚动条

android - 带 SnackBar 和 CoordinatorLayout 的 FloatingActionButton 不适用于混淆器

android - 没有找到类 "com.google.android.material.appbar.CollapsingToolbarLayout"

android - 在 Android 应用程序中使用套接字的正确方法

java - 在 Android ListView 中的文本旁边显示来自 REST 服务的图像

android - 在 RecyclerView 中平均分配项目

java - Android ViewPager 未加载

java - 回收站 View - 滚动时调整项目 View 的大小(用于类似轮播的效果)

android - AppBarLayout with RecyclerView 可以在不让 RecyclerView 过度滚动的情况下工作吗?