Android - FAB, чтобы скрыть при переходе между различными фрагментами в окне просмотра
Я пытаюсь сделать что-то действительно простое.
Я хотел бы, чтобы FAB отображался только на одной вкладке в моем TabLayout и был скрыт при переходе на другую вкладку. Так, например, одна вкладка позволит вам добавлять новые элементы в FAB, но следующая вкладка не позволит вам добавлять элементы.
Я придерживался "типичного" макета XML-дизайна:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
xmlns:tools="http://schemas.android.com/tools"
>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways">
<LinearLayout
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/search_view"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:layout_weight="1"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:hint="Search"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:paddingLeft="2dp"
android:singleLine="true"
android:textColor="#ffffff"
android:textColorHint="#b3ffffff" />
<ImageView
android:id="@+id/search_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="@drawable/ic_action_cancel" />
</LinearLayout>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:tabMode="scrollable"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.example.simon.behaviours.PatchedScrollingViewBehavior"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
app:borderWidth="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_new"
android:layout_margin="16dp"
android:layout_gravity="bottom|right"
app:layout_anchor="@+id/viewPager"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="com.example.simon.behaviours.ScrollingFABBehavior"
android:visibility="gone"
app:fabSize="normal">
</android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>
Я использовал следующее поведение для FAB. Это приводит к тому, что при любом повышении прокрутки FAB исчезает и возвращается на экран при уменьшении прокрутки:
public class ScrollingFABBehavior extends FloatingActionButton.Behavior {
private int toolbarHeight;
public ScrollingFABBehavior(Context context, AttributeSet attrs) {
super();
this.toolbarHeight = getToolbarHeight(context);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
return super.layoutDependsOn(parent, fab, dependency) || (dependency instanceof AppBarLayout);
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
boolean returnValue = super.onDependentViewChanged(parent, fab, dependency);
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
int distanceToScroll = fab.getHeight() + fabBottomMargin;
float ratio = (float)dependency.getY()/(float)toolbarHeight;
fab.setTranslationY(-distanceToScroll * ratio);
}
return returnValue;
}
public static int getToolbarHeight(Context context) {
final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
new int[]{R.attr.actionBarSize});
int toolbarHeight = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
return toolbarHeight;
}
}
Я добавил viewPager addOnPageChangeListener:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (position == 0) {
FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab);
floatingActionButton.setVisibility(View.VISIBLE);
}
else
{
FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab);
floatingActionButton.setVisibility(View.GONE);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
Я только хочу, чтобы FAB появлялся на первой странице и исчезал на всех других страницах.
Код работает, но когда я провожу вниз на следующей странице, FAB появляется после того, как видимость установлена как пропавшая. Я думаю, что это как-то связано с поведением, установленным для FAB. Кто-нибудь знает, почему FAB по-прежнему становится видимым при смахивании вниз, если видимость отключена?
6 ответов
Это не было тем, что я хочу реализовать в своем приложении, но мне удалось найти ответ в конце, с некоторой помощью, посмотрев, как они реализовали то же самое в приложении WordPress.
В приложении WordPress мы видим плавающую кнопку действия на первой странице приложения, которая исчезает, если вы проведете пальцем по любой другой странице в ViewPager:
Они сделали это с помощью следующего кода - это код для Activity, который содержит viewpager. Вы можете увидеть соответствующую часть кода в методе onPageScrolled, который содержит шину событий, которая отправляет событие при каждой прокрутке страницы. Событие содержит только одну переменную с именем positionOffset, которая является целочисленным значением от 0 до 1. Если вы прокручиваете страницу, а страница находится на полпути между двумя окнами просмотра, positionOffset равен 0,5, вы получите идею:
WPMainActivity.java
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
AppPrefs.setMainTabIndex(position);
switch (position) {
case WPMainTabAdapter.TAB_NOTIFS:
new UpdateLastSeenTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
break;
}
trackLastVisibleTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
// noop
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// fire event if the "My Site" page is being scrolled so the fragment can
// animate its fab to match
if (position == WPMainTabAdapter.TAB_MY_SITE) {
EventBus.getDefault().post(new MainViewPagerScrolled(positionOffset));
}
}
});
Событие подбирается во фрагменте, который содержит следующий код. Событие вызовет метод translationY, который анимирует FAB по вертикали, когда страница прокручивается в соответствии с тем, насколько далеко страница прокручивается вне поля зрения, как определено positionOffset:
MySiteFragment
/*
* animate the fab as the users scrolls the "My Site" page in the main activity's ViewPager
*/
@SuppressWarnings("unused")
public void onEventMainThread(CoreEvents.MainViewPagerScrolled event) {
mFabView.setTranslationY(mFabTargetYTranslation * event.mXOffset);
}
Наконец, макет в my_site_fragment.xml показывает, что FAB фактически помещается в фрагменты xml вместо xml активности.
<!-- this coordinator is only here due to https://code.google.com/p/android/issues/detail?id=175330 -->
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_marginRight="@dimen/fab_margin"
android:src="@drawable/gridicon_create_light"
app:borderWidth="0dp"
app:rippleColor="@color/fab_pressed" />
</android.support.design.widget.CoordinatorLayout>
Эй, у меня была точно такая же проблема, как и у тебя.
Я нашел два метода, которые работали для меня.
Способ 1:
Добавьте статический флаг в ваш MainActivity: public static boolean fabVisible;
,
Я заметил, что у вас есть слушатель для вашего viewPager
чтобы определить текущий фрагмент страницы, так что вы можете добавить это:
public void onPageSelected(int position) {
switch (position) {
case 0:
fabVisible = true;
mFloatingActionButton.show();
break;
default:
fabVisible = false;
mFloatingActionButton.hide();
break;
}
}
И внутри вашего собственного потрясающего поведения добавьте следующий код:
if (dyConsunmed > 0 && child.getVisibility() == View.VISIBLE ) {
child.hide();
} else if (dyConsunmed < 0 && child.getVisibility() != View.VISIBLE && MainActivity.fabVisible) {
child.show();
}
Смысл этого метода состоит в том, чтобы убедиться, что fab должен быть видимым, прежде чем вы реализуете метод hide & show
Способ 2:
Вам не нужно добавлять какой-либо флаг, как "fabVisible" в вашем MainActivity
больше. Хотя этот метод требует от вас установить viewPager
как статичный в вашем MainActivity
потому что нам нужно иметь доступ к нему в нашем собственном классном поведенческом классе.
В вашем ScrollAwareFABBehavior
класс, добавьте следующий код:
if (MainActivity.viewPager.getCurrentItem() == 0) {
if (dyConsunmed > 0 && child.getVisibility() == View.VISIBLE ) {
child.hide();
} else if (dyConsunmed < 0 && child.getVisibility() != View.VISIBLE) {
child.show();
}
}
Ключевым моментом этого метода является создание метода show and hide только тогда, когда вы находитесь внутри фрагмента, который вы хотите (в моем случае это фрагмент один, так что currentItem == 0). Конечно, чтобы удалить потрясающий элемент во втором фрагменте, вам все равно нужно скрыть его в слушателе viewPager.
Если вы все еще не уверены в реализации, не стесняйтесь проверить мой репозиторий на GitHub и поиграть с кодом. Адрес: https://github.com/Anthonyeef/FanfouDaily
Несмотря на то, что содержание канала полностью на китайском языке, код на английском языке.
Реализуйте ViewPager.onPageListener и переберите onPageSelected и покажите или скрыте FAB на вкладках в соответствии с вашими требованиями. Ниже приведен пример кода:
@Override
public void onPageSelected(int position) {
//FAB_PAGE is the index of the page on which you want to show FAB
if(position == FAB_PAGE)
{
fab.setVisibility(View.VISIBLE);
}
else
{
fab.setVisibility(View.GONE);
}
}
Вы также можете использовать fab.show(); и fab.hide();
Также проверьте это для более подробной информации.
Вы можете сделать это программно, когда переходите к следующей вкладке.
FloatingActionButton fab = (FloatingActionButton) view.findViewByID(R.id.fab)
fab.setVisibility(View.GONE);
И затем установите видимость View.VISIBLE
когда ты проводишь назад
Создайте статическое логическое значение в своей деятельности (см. YourActivity.isFABinCurrentTabVisible
ниже) и измените его из вашего ViewPager, соответственно, если на текущей вкладке должен быть ярлык или нет. Хотя это может быть грязное исправление... И подумайте об использовании fab.show()
а также fab.hide()
вместо setVisiblity()
, Они выполняют анимацию постепенного появления и исчезновения.
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
child.hide();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE && YourActivity.isFABinCurrentTabVisible) {
// User scrolled up and the FAB is currently not visible -> show the FAB
child.show();
}
}
}
Похоже, вы подходите к этому с неправильной точки зрения...
Так, например, одна вкладка позволит вам добавлять новые элементы в FAB, но следующая вкладка не позволит вам добавлять элементы.
Поместите FAB в Fragment
первой страницы вместо хостинга Fragment
/ Activity
, Таким образом, он автоматически исчезнет вместе с первым фрагментом страницы - он даже оживится вместе с ним. Он также будет там, где он принадлежит, поскольку он совсем не связан с другими страницами / вкладками.
Если вам действительно нужно обработать событие click на вашем хостинге Fragment
/ Activity
сделайте свой первый фрагмент страницы перезвоните на хостинг Fragment
/ Activity
когда этот щелчок происходит.