android - Использование BottomSheetBehavior с внутренним CoordinatorLayout




android-support-library ontouchlistener (4)

В библиотеке поддержки проектирования v. 23.2 появился BottomSheetBehavior , который позволяет дочерним элементам координатора действовать как нижние листы (виды, перетаскиваемые из нижней части экрана).

То, что я хотел бы сделать, это иметь в виде нижнего листа следующий вид (типичный координатор + свертываемый материал):

<CoordinatorLayout
    app:layout_behavior=“@string/bottom_sheet_behavior”>

   <AppBarLayout>
        <CollapsingToolbarLayout>
           <ImageView />
        </CollapsingToolbarLayout>
    </AppBarLayout>

    <NestedScrollView>
        <LinearLayout>
            < Content ... />
        </LinearLayout>
    </NestedScrollView>

</CoordinatorLayout>

К сожалению, представления нижнего листа должны реализовывать вложенную прокрутку, иначе они не получат события прокрутки. Если вы попытаетесь выполнить основное задание, а затем загрузите это представление как нижний лист, вы увидите, что события прокрутки действуют только на «лист» бумаги, с некоторым странным поведением, как вы можете видеть, если продолжаете читать.

Я почти уверен, что это можно сделать с помощью BottomSheetBehavior подкласса CoordinatorLayout или, что еще лучше, путем BottomSheetBehavior подкласса BottomSheetBehavior . У вас есть подсказка?

Некоторые мысли

  • requestDisallowInterceptTouchEvent() должен использоваться для кражи событий от родителя в некоторых условиях:

    • когда смещение AppBarLayout > 0
    • когда смещение AppBarLayout == 0, но мы прокручиваем вверх (подумайте об этом на секунду, и вы увидите)
  • первое условие можно получить, установив OnOffsetChanged для внутренней панели приложения;

  • второй требует некоторой обработки событий, например:

    switch (MotionEventCompat.getActionMasked(event)) {
        case MotionEvent.ACTION_DOWN:
            startY = event.getY();
            lastY = startY;
            userIsScrollingUp = false;
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            userIsScrollingUp = false;
            break;
        case MotionEvent.ACTION_MOVE:
            lastY = event.getY();
            float yDeltaTotal = startY - lastY;
            if (yDeltaTotal > touchSlop) { // Moving the finger up.
                userIsScrollingUp = true;
            }
            break;
    }

вопросы

Само собой разумеется, я не могу заставить это работать прямо сейчас. Я не могу ловить события, когда выполняются условия, и не ловить их в других случаях. На изображении ниже вы можете увидеть, что происходит со стандартным CoordinatorLayout:

  • Лист отклоняется, если вы прокручиваете вниз на панели приложений, но не прокручиваете вложенный контент вниз. Кажется, что вложенные события прокрутки не распространяются на поведение Координатора;

  • Существует также проблема с внутренней панелью приложения: вложенное содержимое прокрутки не следует за панелью приложения при ее свертывании.

Я настроил пример проекта на GitHub, который показывает эти проблемы.

Просто чтобы быть ясно, желаемое поведение это:

  • Правильное поведение панелей приложений / прокрутки внутри листа;

  • Когда лист развернут, он может свернуться при прокрутке вниз, но только если внутренняя панель приложений тоже полностью развернута . Прямо сейчас он разваливается безотносительно к состоянию панели приложения, и только если вы перетаскиваете панель приложения;

  • Когда лист свернут, жесты прокрутки вверх развернут его (без влияния на внутреннюю панель приложения).

Пример из приложения контактов (которое, вероятно, не использует BottomSheetBehavior, но это то, что я хочу):


Если первый дочерний nestedscroll - nestedscroll другие проблемы. Это решение исправило мою проблему, я надеюсь также исправить вашу.

<CoordinatorLayout
    app:layout_behavior=“@string/bottom_sheet_behavior”>

   <AppBarLayout>
        <CollapsingToolbarLayout>
           <ImageView />
        </CollapsingToolbarLayout>
    </AppBarLayout>
</LinearLayout>
    <NestedScrollView>
        <LinearLayout>
            < Content ... />
        </LinearLayout>
    </NestedScrollView>
</LinearLayout>
</CoordinatorLayout>

Макет для полного экрана макета панели приложения выглядит следующим образом:

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/detail_backdrop_height"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:fitsSystemWindows="true">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:expandedTitleMarginStart="48dp"
        app:expandedTitleMarginEnd="64dp">

        <ImageView
            android:id="@+id/backdrop"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:fitsSystemWindows="true"
            app:layout_collapseMode="parallax" />

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:layout_collapseMode="pin" />

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

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

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingTop="24dp">

        <android.support.v7.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/card_margin">

            <LinearLayout
                style="@style/Widget.CardContent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Info"
                    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="@string/cheese_ipsum" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/card_margin"
            android:layout_marginLeft="@dimen/card_margin"
            android:layout_marginRight="@dimen/card_margin">

            <LinearLayout
                style="@style/Widget.CardContent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Friends"
                    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="@string/cheese_ipsum" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/card_margin"
            android:layout_marginLeft="@dimen/card_margin"
            android:layout_marginRight="@dimen/card_margin">

            <LinearLayout
                style="@style/Widget.CardContent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="Related"
                    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="@string/cheese_ipsum" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

    </LinearLayout>

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

<android.support.design.widget.FloatingActionButton
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    app:layout_anchor="@id/appbar"
    app:layout_anchorGravity="bottom|right|end"
    android:src="@drawable/ic_discuss"
    android:layout_margin="@dimen/fab_margin"
    android:clickable="true"/>

и после этого вы должны реализовать AppBarLayout.OnOffsetChangedListener в вашем классе и установить смещение экрана.


Я наконец выпустил свою реализацию. Найдите его на Github или непосредственно в jcenter:

compile 'com.otaliastudios:bottomsheetcoordinatorlayout:1.0.0’

Все, что вам нужно сделать, это использовать BottomSheetCoordinatorLayout в качестве корневого представления для вашего нижнего листа. Это автоматически раздувает рабочее поведение для себя, так что не беспокойтесь об этом.

Я использовал это в течение длительного времени, и у него не должно быть проблем с прокруткой, поддержка перетаскивания на ABL и т. Д.


Я следил за первоначальным тестовым проектом GitHab от Laenger по этой проблеме, и я рад поделиться с вами решением некоторых из его проблем, так как мне нужно было такое поведение и в моем приложении.

это решение его проблемы: ❌ панель инструментов иногда падает слишком рано

чтобы предотвратить это, вам нужно создать свой собственный AppBarLayout.Behavior , поскольку именно при прокрутке вверх и перетаскивании AppBarLayout.behavior получает движение прокрутки. Нам нужно определить, находится ли он в STATE_DRAGGING, и просто вернуться, чтобы избежать преждевременного скрытия / сворачивания панели инструментов.

public class CustomAppBarLayoutBehavior extends AppBarLayout.Behavior {

    private CoordinatorLayoutBottomSheetBehavior behavior;

    public CustomAppBarLayoutBehavior() {
    }

    public CustomAppBarLayoutBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
        behavior = CoordinatorLayoutBottomSheetBehavior.from(parent);
        return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
        if(behavior.getState() == CoordinatorLayoutBottomSheetBehavior.STATE_DRAGGING){
            return;
        }else {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    }

    @Override
    public void setDragCallback(@Nullable DragCallback callback) {
        super.setDragCallback(callback);
    }
}

это может быть хорошим началом того, как мы решаем другие вопросы:

❌ панель инструментов не может быть свернута с помощью перетаскивания

Layout макет главного координатора потребляет немного прокрутки

Я не очень хороший пользовательский интерфейс / анимация, но иногда трудолюбие окупается пониманием кода и поиском подходящей функции обратного вызова / переопределения.

установить это как поведение appbarlayout

<android.support.design.widget.AppBarLayout
    android:id="@+id/bottom_sheet_appbar"
    style="@style/BottomSheetAppBarStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="your.package.CustomAppBarLayoutBehavior">




android-support-design