Использование метода BottomSheetBehavior с внутренним координаторомLayout
В библиотеке поддержки дизайна v. 23.2
представлен BottomSheetBehavior
, который позволяет дочерним элементам координатора действовать как нижние листы (просмотры перетаскиваются из нижней части экрана).
Идентификатор должен иметь как представление нижнего листа, следующее представление (типичный координатор + сворачивающийся материал):
<CoordinatorLayout
app:layout_behavior="@string/bottom_sheet_behavior">
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
<NestedScrollView>
<LinearLayout>
< Content ... />
</LinearLayout>
</NestedScrollView>
</CoordinatorLayout>
К сожалению, представления нижнего листа должны реализовывать вложенную прокрутку, иначе они не будут получать события прокрутки. Если вы попытаетесь выполнить основное действие, а затем загрузите это представление в качестве нижнего листа, вы увидите, что события прокрутки действуют только на "листе" бумаги с некоторым странным поведением, так как вы можете видеть, продолжаете ли вы читать.
Я уверен, что это может быть обработано путем подклассификации CoordinatorLayout
или даже лучше с помощью подкласса 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;
}
Вопросы
Излишне говорить, что я не могу сделать эту работу прямо сейчас. Я не могу поймать события, когда условия соблюдены, и не поймать их в других случаях. На изображении ниже вы можете увидеть, что происходит со стандартным координаторомLayout:
-
Лист отклоняется, если вы прокручиваете панель приложений, но не прокручиваете вниз вложенное содержимое. Кажется, что вложенные события прокрутки не распространяются на поведение Координатора,
-
Существует также проблема с внутренней панелью приложения: вложенное содержимое прокрутки не следует за панелью приложения, когда оно сбрасывается.
![введите описание изображения здесь]()
Я установил образец проекта на github, который показывает эти проблемы.
Чтобы быть ясным, желаемое поведение:
-
Корректное поведение видов приложений/прокрутки внутри листа;
-
Когда лист расширяется, он может свернуться на прокрутку вниз, но , только если внутренняя панель приложения полностью развернута.. Сейчас он рушится без учета состояния панели приложений, и только если вы перетащите панель приложения,
-
Когда лист свернут, прокручивание жестов расширит его (без влияния на внутреннюю панель приложений).
Пример из приложения контактов (который, вероятно, не использует метод BottomSheetBehavior, но это то, что я хочу):
![введите описание изображения здесь]()
Ответы
Ответ 1
Я наконец выпустил свою реализацию. Найдите в Github или непосредственно из jcenter:
compile 'com.otaliastudios:bottomsheetcoordinatorlayout:1.0.0’
Все, что вам нужно сделать, это использовать BottomSheetCoordinatorLayout
в качестве корневого представления для вашего нижнего листа. Он автоматически раздует рабочее поведение для себя, поэтому не беспокойтесь об этом.
Я использую это в течение длительного времени и не должен иметь проблем с прокруткой, поддерживает перетаскивание на ABL и т.д.
Ответ 2
Я только что пошел по тому, как вы задали вышеуказанный вопрос, и придумайте решение, которое может потребовать больше объяснений. Пожалуйста, следуйте своему образцу кода и интегрируйте дополнительную часть в свой xml, чтобы заставить ее вести себя как поведение BottomSheeet.
<CoordinatorLayout>
<AppBarLayout>
<Toolbar
app:layout_collapseMode="pin">
</Toolbar>
</AppBarLayout>
<NestedScrollView
app:layout_behavior="@string/bottom_sheet_behavior" >
<include layout="@layout/items" />
</NestedScrollView>
<!-- Bottom Sheet -->
<BottomSheetCoordinatorLayout>
<AppBarLayout
<CollapsingToolbarLayout">
<ImageView />
<Toolbar />
</CollapsingToolbarLayout>
</AppBarLayout>
<NestedScrollView">
<include layout="@layout/items" />
</NestedScrollView>
</BottomSheetCoordinatorLayout>
</CoordinatorLayout>
Примечание. Решение, которое сработало для меня, уже объяснено в последнем комментарии к вашему вопросу
Лучшее объяснение:
https://github.com/laenger/BottomSheetCoordinatorLayout
Ответ 3
Если первый ребенок nestedscroll
, это вызовет некоторые другие проблемы. Это решение исправлено моей проблемой, я надеюсь, также исправить вашу.
<CoordinatorLayout
app:layout_behavior="@string/bottom_sheet_behavior">
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
</LinearLayout>
<NestedScrollView>
<LinearLayout>
< Content ... />
</LinearLayout>
</NestedScrollView>
</LinearLayout>
</CoordinatorLayout>
Ответ 4
Попытайтесь не использовать NestedScrollView
с LinearLayout
, это также вызывает проблемы в моем приложении. Просто используйте только LinearLayout
вместо этого, отлично работает для меня.
Попробуйте следующее:
<CoordinatorLayout
app:layout_behavior="@string/bottom_sheet_behavior">
<AppBarLayout>
<CollapsingToolbarLayout>
<ImageView />
</CollapsingToolbarLayout>
</AppBarLayout>
<LinearLayout>
<!--don't forget to addd this line-->
app:layout_behavior="@string/appbar_scrolling_view_behavior">
< Content ... />
</LinearLayout>
Ответ 5
Я следил за проектом тестового проекта jiant initial github по этой проблеме, и я рад поделиться с вами решением для некоторых из его проблем, так как мне тоже нужно было это поведение в моем приложении.
это решение его проблемы
: ❌ панель инструментов иногда рушится слишком рано
чтобы предотвратить это, вам нужно создать свой пользовательский 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);
}
}
это может быть хорошим началом того, как мы решаем другие проблемы:
❌ панель инструментов не может быть снесена путем перетаскивания
❌ расположение главного координатора расходует несколько прокруток
Я на самом деле не хороший пользовательский интерфейс/анимация, но трудолюбие окупается, иногда понимая код, находя правильную функцию обратного вызова/переопределения.
задайте это как поведение для 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">
Ответ 6
Макет для полного экрана макета панели приложений выглядит следующим образом: -
<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 в своем классе и установить смещение экрана.