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:

WordPress

Они сделали это с помощью следующего кода - это код для 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 когда этот щелчок происходит.

Другие вопросы по тегам