web-dev-qa-db-ja.com

ビューページャー/タブスライダーを使用したFABアニメーション

私はそのような効果を達成する方法を探しています https://material-design.storage.googleapis.com/publish/material_v_4/material_ext_publish/0B6Okdz75tqQsVlhxNGJCNTEzNFU/components-buttons-fab-behavior_04_xhdpi_009.webm

Android.support.v4.view.ViewPagerを使用しています

ヒントや助力に感謝します

25
ray20

最初に、フローティングアクションボタンがViewPagerに固定されたレイアウトが必要です。

<Android.support.design.widget.CoordinatorLayout
    Android:id="@+id/main_content"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <Android.support.design.widget.AppBarLayout
        Android:id="@+id/appbar"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/action_bar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

        <Android.support.design.widget.TabLayout
            Android:id="@+id/tabs"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:background="@Android:color/white"
            app:tabGravity="center"
            app:tabIndicatorColor="?attr/colorAccent"
            app:tabMode="scrollable"
            app:tabSelectedTextColor="?attr/colorAccent"
            app:tabTextColor="@Android:color/black" />

    </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="@string/appbar_scrolling_view_behavior" />

     <Android.support.design.widget.FloatingActionButton
            Android:id="@+id/fab"
            app:layout_anchor="@id/viewpager"
            app:layout_anchorGravity="bottom|right|end"
            Android:layout_width="56dp"
            Android:layout_height="56dp"
            Android:src="@drawable/ic_add_white"
            app:borderWidth="@dimen/fab_border_width"
            app:fabSize="normal" />

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

FABをViewPagerに固定したので(注:viewpagerのタブであるフラグメントでこれを試しましたが、適切に動作していないようです)、次のようにViewPagerにOnPageChangeListenerを追加します。

 viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {

            switch (position) {
                case INDEX_OF_TAB_WITH_FAB:
                    fab.show();
                    break;

                default:
                    fab.hide();
                    break;
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

タブを切り替える際の「ポップ」と「シュリンク」のアニメーションは、サポートライブラリの新しいバージョンで自動的に処理されます。

38
Chantell Osejo

別の解決策があります。私にとって他の問題は、ViewPagerを含むアクティビティにFABの制御を与えると、コーディングが難しくなることです。

FABアイコンを簡単に変更し、特定のリスナーを設定するには、フラグメントによるFABの多くのコントロールが必要です。

大まかに、手順は次のとおりです。

  1. フラグメントではなく、ViewPagerを含むメインアクティビティで1つのFABを宣言します(これは必須の手順であり、回避したかったのですが、魔法が後になります。そうしないと、良いアニメーションのために物事が難しくなります)
  2. ViewPagerに表示される各フラグメントで、public void shareFab(FloatingActionButton sharedFab);のようなFABを渡すことができるメソッドを作成します。このメソッド内で、各フラグメントはFABアイコンを設定し、必要なリスナーを追加します
  3. ViewPager.OnPageChangeListenerアニメーションをトリガーし、shareFabメソッドを呼び出すViewPagerのリスナー。

そのため、フラグメントが表示されるたびに、包含アクティビティは表示されたフラグメントにFABの参照を提供します。次に、フラグメントは、ニーズに合わせてFABをリセット/リサイクルします。

以下に例を示します。

表示するフラグメント

public void Fragment1 extends Fragment {

    private FloatingActionButton mSharedFab;

    @Override
    public void onDestroy() {
        super.onDestroy();
        mSharedFab = null; // To avoid keeping/leaking the reference of the FAB
    }

    public void shareFab(FloatingActionButton fab) {
        if (fab == null) { // When the FAB is shared to another Fragment
            if (mSharedFab != null) {
                mSharedFab.setOnClickListener(null);
            }
            mSharedFab = null;
        }
        else {
            mSharedFab = fab;
            mSharedFab.setImageResource(R.drawable.ic_search);
            mSharedFab.setOnClickListener((fabView) -> {
                // Your code
            });
        }
    }
}

コンテナアクティビティ

public class ActivityMain extends AppCompatActivity {

    FloatingActionButton mSharedFab;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSharedFab = (FloatingActionButton) findViewById(R.id.shared_fab);

        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        Fragment1 fragment1 = new Fragment1();
        Fragment2 fragment2 = new Fragment2();
        adapter.addFragment(fragment1, "Fragment1");
        adapter.addFragment(fragment2, "Fragment2");
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        viewPager.setAdapter(adapter);

        fragment1.shareFab(mSharedFab); // To init the FAB
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
            @Override
            public void onPageSelected(int position) {  }
            @Override
            public void onPageScrollStateChanged(int state) {
                switch (state) {
                    case ViewPager.SCROLL_STATE_DRAGGING:
                        mSharedFab.hide(); // Hide animation
                        break;
                    case ViewPager.SCROLL_STATE_IDLE:
                        switch (viewPager.getCurrentItem()) {
                            case 0:
                                fragment2.shareFab(null); // Remove FAB from fragment
                                fragment1.shareFab(mSharedFab); // Share FAB to new displayed fragment
                                break;
                            case 1:
                            default:
                                fragment1.shareFab(null); // Remove FAB from fragment
                                fragment2.shareFab(mSharedFab); // Share FAB to new displayed fragment
                                break;
                        }
                        mSharedFab.show(); // Show animation
                        break;
                }
            }
        });
    }
}

...およびメインアクティビティの古典的なレイアウト

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

        <Android.support.design.widget.TabLayout
            Android:id="@+id/tabs"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabGravity="fill"/>

    </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="@string/appbar_scrolling_view_behavior"  />

    <Android.support.design.widget.FloatingActionButton
        Android:id="@+id/shared_fab"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_gravity="bottom|end"
        Android:layout_margin="@dimen/fab_margin" />

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

長所は次のとおりです。

  • マテリアルデザインガイドラインで説明されているようなアニメーション!
  • 各フラグメントによるFABの完全な制御

短所は次のとおりです。

  • fABは別のフラグメントと共有できるため、FABの参照を毎回チェックして、nullでないことを確認する必要があります
  • viewPagerのレベルでのFABの宣言、少し面倒
10

かくれんぼの良いアニメーションには、これを使用してください:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {
                switch (state){
                    case ViewPager.SCROLL_STATE_IDLE:
                        fab.show();
                        break;
                    case ViewPager.SCROLL_STATE_DRAGGING:
                    case ViewPager.SCROLL_STATE_SETTLING:
                        fab.hide();
                        break;
                }

            }
        });
6
NickUnuchek

私が発見した解決策は次のとおりです。

  1. ViewPager内に表示されるフラグメントは、インターフェイス(私の場合はFloatingActionButtonFragment)を実装します。このインターフェイスでは、フラグメントにhandleFABClick(View)メソッドが必要です。

  2. 私のViewPager変更リスナーは次のようになります:

    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
        }
    
        @Override
        public void onPageSelected(int position) {
    
    
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
    
            switch (state) {
                case ViewPager.SCROLL_STATE_SETTLING:
                    displayFloatingActionButtonIfNeeded(viewPager.getCurrentItem());
                    break;
    
                case ViewPager.SCROLL_STATE_IDLE:
                    displayFloatingActionButtonIfNeeded(viewPager.getCurrentItem());
                    break;
    
                default:
                    mFloatingActionButton.hide();
            }
    
        }
    });
    
    private void displayFloatingActionButtonIfNeeded(int position) {
        if (mFragmentStatePagerAdapter.getItem(position) instanceof FloatingActionButtonFragment) {
    
            final FloatingActionButtonFragment floatingActionButtonFragment = (FloatingActionButtonFragment) mFragmentStatePagerAdapter.getItem(position);
    
            mFloatingActionButton.show();
            mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    floatingActionButtonFragment.handleFABClick(v);
                }
            });
        }
    }
    

これにより、ユーザーがビューを変更しているときにFABが「非表示」になりますが、ビューステートがアイドル(解決済み)または解決中の場合にshowメソッドをトリガーしますアイドル)。

2
Gavin Harris

私の問題では、最初のタブにフローティングアクションボタンを表示したくありませんでした。これを達成するために、私は追加しました

Android:visibility="invisible"

フローティングアクションバーで。

<Android.support.design.widget.FloatingActionButton
        Android:id="@+id/fab"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_gravity="end|bottom"
        Android:layout_margin="@dimen/fab_margin"
        Android:visibility="invisible"
        app:srcCompat="@Android:drawable/ic_input_add" />

そして、@ chantellが示唆するように、アクティビティにViewpagerListnerを追加しました

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {

                switch (position) {
                    case 0:
                        addFloatingActionBt.hide();
                        break;
                    default:
                        addFloatingActionBt.show();
                        break;

                }

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
0
Zohra Khan

私は最近、私が満足しているはるかに優れたソリューションを見つけました。 FABのhideメソッドには、パラメーターを設定することもできます。リスナーは、ボタンが非表示になったときにトリガーされます(showメソッドにもこれがあります)。唯一の欠点は、viewpagerとtablayoutの変更を手動で実装する必要があることです。

@Override
public void onPageSelected(int position) {
    onToolbarTitleChanged(adapter.getPageTitle(position).toString());
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}

@Override
public void onPageScrollStateChanged(int state) {
}

@Override
public void onTabSelected(TabLayout.Tab tab) {
    viewPager.setCurrentItem(tab.getPosition());
}

@Override
public void onTabUnselected(final TabLayout.Tab tab) {
    fab.hide(new FloatingActionButton.OnVisibilityChangedListener() { // Hide FAB
        @Override
        public void onHidden(FloatingActionButton fab) {
            fab.show(); // After FAB is hidden show it again
        }
    });
}

@Override
public void onTabReselected(TabLayout.Tab tab) {
}

特定のタブでのみFABを表示する場合は、onTabSelectedメソッドを次のように変更できます。

@Override
public void onTabSelected(TabLayout.Tab tab) {
    if (tab.getPosition() == 2) {
        createButton.hide();
    }
    viewPager.setCurrentItem(tab.getPosition());
}
0