web-dev-qa-db-ja.com

タブ間でFloatingActionButtonを変更するには?

実装しようとしていますFloatingActionButtonfromGoogle Design Support Libraryを3つのタブのうちの2つに、そして Material Design Guidelines-FloatingActionButton に従って:

複数の横画面(タブなど)にフローティングアクションボタンがある場合、各画面に入ると、それぞれに含まれるアクションが異なる場合、ボタンは表示および非表示になります。アクションが同じ場合、ボタンは画面に表示されたままになります(必要に応じて新しい位置に変換されます)。

Example of FAB animation in tabs

アプリのFABボタンにこの種のトランジションまたはアニメーションを作成するにはどうすればよいですか?

30
Andres Valencia

この機能は現在FloatingActionButtonに組み込まれていないため、自分でアニメーション化する必要があります。 FloatingActionButtonがメインアクティビティにあると仮定して、アクティビティに次の関数を追加します。

int[] colorIntArray = {R.color.walking,R.color.running,R.color.biking,R.color.paddling,R.color.golfing};
int[] iconIntArray = {R.drawable.ic_walk_white,R.drawable.ic_run_white,R.drawable.ic_bike_white,R.drawable.ic_add_white,R.drawable.ic_arrow_back_white};

protected void animateFab(final int position) {
    fab.clearAnimation();
    // Scale down animation
    ScaleAnimation shrink =  new ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    shrink.setDuration(150);     // animation duration in milliseconds
    shrink.setInterpolator(new DecelerateInterpolator());
    shrink.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            // Change FAB color and icon
            fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
            fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));

            // Scale up animation
            ScaleAnimation expand =  new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            expand.setDuration(100);     // animation duration in milliseconds
            expand.setInterpolator(new AccelerateInterpolator());
            fab.startAnimation(expand);
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });
    fab.startAnimation(shrink);
}

プロジェクトに合わせて色と描画可能リソースを更新します。 onCreateメソッドにタブ選択リスナーを追加し、タブが選択されたときにアニメーション関数を呼び出します。

tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        mViewPager.setCurrentItem(tab.getPosition());
        animateFab(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

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

    }
});

持っているタブの数に合わせて十分な色とアイコンを用意してください。

37
blackcj

以下は、希望する結果を得るための簡単な方法です

enter image description here

以下のようなメインアクティビティに2つ(またはタブアクションに相当)FloatingActionButtonを追加します

<Android.support.design.widget.AppBarLayout
    Android:id="@+id/appbar"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:paddingTop="@dimen/appbar_padding_top"
    Android:theme="@style/AppTheme.AppBarOverlay">

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

    </Android.support.v7.widget.Toolbar>

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

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

<Android.support.v4.view.ViewPager
    Android:id="@+id/container"
    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/fabChat"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="end|bottom"
    Android:layout_margin="@dimen/fab_margin"
    Android:src="@drawable/ic_fab_chat" />

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fabPerson"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="end|bottom"
    Android:layout_margin="@dimen/fab_margin"
    Android:src="@drawable/ic_fab_person"
    Android:visibility="gone" />

MainActivity.Javaで、Fabのデフォルト関数を使用して、以下のように各タブ選択で非表示と表示を行います

private void animateFab(int position) {
    switch (position) {
        case 0:
            fabChat.show();
            fabPerson.hide();
            break;
        case 1:
            fabPerson.show();
            fabChat.hide();
            break;

        default:
            fabChat.show();
            fabPerson.hide();
            break;
    }
}

以下のようにanimateFab関数を呼び出します

TabLayout.OnTabSelectedListener onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        animateFab(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

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

    }
};

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

    }

    @Override
    public void onPageSelected(int position) {
        animateFab(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
};
23
Nauman Zubair

Blackcjの回答を拡張し、説明したように、ソリューションは本当にうまくいきました。ただし、その中に何かを追加したいと思います。

私はそのビデオをスローモーションで見ました。ドロウアブルとファブのアニメーションは異なります。非表示の場合、fabとdrawableは同期しています。表示中に、fabが最初に戻ってきて、60〜70%が完了すると、描画可能な開始アニメーションが0から始まり、回転およびスケーリングがフルサイズになります。

しかし、私はドローアブルアニメーションを達成することができませんでした。しかし、さまざまなインターポレーターを使用して回転とスケーリングを行い、時間をわずかに変更しました。そのため、Googleデザインガイドラインにも示されているビデオのように見えます。

int[] colorIntArray = {R.color.red,R.color.gray,R.color.black};
int[] iconIntArray = {R.drawable.ic_btn1, R.drawable.ic_btn2, R.drawable.ic_btn3};

    protected void animateFab(final int position) {
        fab.clearAnimation();

        // Scale down animation
        ScaleAnimation shrink = new ScaleAnimation(1f, 0.1f, 1f, 0.1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        shrink.setDuration(100);     // animation duration in milliseconds
        shrink.setInterpolator(new AccelerateInterpolator());
        shrink.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // Change FAB color and icon
                fab.setBackgroundTintList(ContextCompat.getColorStateList(getApplicationContext(), colorIntArray[position]));
                fab.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), iconIntArray[position]));

                // Rotate Animation
                Animation rotate = new RotateAnimation(60.0f, 0.0f,
                        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                        0.5f);
                rotate.setDuration(150);
                rotate.setInterpolator(new DecelerateInterpolator());

                // Scale up animation
                ScaleAnimation expand = new ScaleAnimation(0.1f, 1f, 0.1f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                expand.setDuration(150);     // animation duration in milliseconds
                expand.setInterpolator(new DecelerateInterpolator());

                // Add both animations to animation state
                AnimationSet s = new AnimationSet(false); //false means don't share interpolators
                s.addAnimation(rotate);
                s.addAnimation(expand);
                fab.startAnimation(s);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        fab.startAnimation(shrink);
    }

そして、通常のタブタブ変更リスナー:

tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        mViewPager.setCurrentItem(tab.getPosition());
        animateFab(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

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

    }
});
3
kirtan403

最後に、非常に簡単で、添付のgifとまったく同じアニメーションを表示するソリューションを見つけました。@ NaumanのOder @Omidのソリューションでは、アニメーションの非表示が完了する前にアニメーションが表示されます。ただし、必ず最新のサポートライブラリを使用してください。バージョン23.2.1でテストしました。

使用事例:

  • タブ1にファブ1を表示(インデックス0)
  • タブ2にfab 2を表示(インデックス1)
  • タブ3にファブを表示しない(インデックス2)

Xmlで、一意のIDと可視性が非表示に設定されたファブに配置します。

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fab1"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="bottom|end"
    Android:layout_margin="16dp"
    Android:src="@drawable/some_icon"
    Android:visibility="invisible" />

<Android.support.design.widget.FloatingActionButton
    Android:id="@+id/fab2"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="bottom|end"
    Android:layout_margin="16dp"
    Android:src="@drawable/another_icon"
    Android:visibility="invisible" />

次に、Fabの2つのフィールドをアクティビティに追加します(ローカル変数を使用するか、findViewById(...)で毎回ビューを取得することもできます)。

public class MainActivity extends AppCompatActivity {
    private FloatingActionButton fab1;
    private FloatingActionButton fab2;

onCreate(...)関数で、これらのビューを見つけて、宣言されたフィールドに保存します。

fab1 = (FloatingActionButton) findViewById(R.id.fab1);
fab2 = (FloatingActionButton) findViewById(R.id.fab2);

次に、指定された位置に適切なファブを表示する関数を宣言します。デフォルトの場合(タブ3以上)は非常に簡単です。fabsでhide()メソッドを呼び出すだけです。 show()hide()は既にスケーリングアニメーションを実装しています。ただし、タブ1でfab2を非表示にすると、fab1を表示する前に終了するまで待つ必要があります。したがって、hide(...)メソッドのパラメーターとしてFloatingActionButton.OnVisibilityChangedListenerを設定し、そのリスナーのonHidden(...)メソッドで目的の新しいファブを表示します。結果は次のとおりです。

public void showRightFab(int tab) {
    switch (tab) {
        case 0:
            fab2.hide(new FloatingActionButton.OnVisibilityChangedListener() {
                @Override
                public void onHidden(FloatingActionButton fab) {
                    fab1.show();
                }
            });
            break;

        case 1:
            fab1.hide(new FloatingActionButton.OnVisibilityChangedListener() [
                @Override
                public void onHidden(FloatingActionButton fab) {
                    fab2.show();
                }
            });
            break;

        default:
            fab1.hide();
            fab2.hide();
            break;
    }
}

それが最も難しい部分でした!次に、選択したタブが変更されるたびにshowRightFab(...)関数を呼び出すリスナーをViewPagerに追加します。

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageSelected(int position) {
        showRightFab(position);
    }

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

    @Override
    public void onPageScrollStateChanged(int state) {}
});

最後に、ViewPager.OnPageChangeListeneronCreate(...)メソッドは起動時に呼び出されないため、デフォルトのタブでfabを表示するためにonPageSelected(...)メソッドで関数を手動で1回呼び出しますshowRightFab(...)関数が呼び出されたことがないため表示されます)。

showRightFab(viewPager.getCurrentItem());

その作業は私のアプリケーションでは完璧です!

2
Felix Edelmann

Blackcjとkirtan403の回答を拡張して、選択したタブ(この場合は1番目のタブ)でfabを非表示にする機能も追加しました。

これを達成するために、最初にint[]には3つのアイテムがあり、それぞれ3つのタブのfabに対応しています。それぞれの最初の項目を0に設定します。これは、リソースを必要としない最初のタブの非表示fabになるためです。

int[] colorIntArray = {0, R.color.fab_red, R.color.fab_green};
int[] iconIntArray = {0, R.drawable.fab_pencil, R.drawable.fab_chevron};

次に、ifとタブを備えたonCreateActivityメソッドにfabステートメントを設定します。このステートメントは、ファブを非表示にして縮小します。そのため、再び表示されるときに、不必要に縮小してから再び拡大するのではなく、拡大のみを行うことができます。 blackcjのスケールダウンアニメーションの最終スケールに一致するようにスケールを設定します。

    if (tabLayout.getSelectedTabPosition() == 0) {
        // if on the 1st tab
        fab.hide();
        // scale down to only scale up when switching from 1st tab
        fab.setScaleX(0.2f);
        fab.setScaleY(0.2f);
    }

次に、onCreateメソッドの外側に、blackcjのanimateFabメソッドを追加し、kirtan403のrotateメソッドを変更しました。ただし、animateFabメソッドを変更して、条件ステートメントも含めるようにしました。ここで、

  • 最初のタブに戻ると、fabは非表示になります(非表示になると自動的に縮小されます)。

  • ファブが既にフルサイズで表示されているタブから、表示されるはずの別のタブに切り替えると、フルスケールダウン、アニメーションの変更およびスケールアップが実行されます。

  • from非表示のfabを持つタブ(この場合は最初のタブ)を切り替えると、fabが表示された後、拡大されます(縮小ではなく、カスタムアニメーションで).

    protected void animateFab(final int position) {
        fab.clearAnimation();
        if (tabLayout.getSelectedTabPosition() == 0) {
            // if on the 1st tab
            fab.hide();
        } else if (fab.getScaleX() == 1f) {
            // if the fab is full scale
            // Scale down animation
            ScaleAnimation shrink =  new ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            shrink.setDuration(150);     // animation duration in milliseconds
            shrink.setInterpolator(new DecelerateInterpolator());
            shrink.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    // Change FAB color and icon
                    fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
                    fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));
    
                    // Scale up animation
                    ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                    expand.setDuration(100);     // animation duration in milliseconds
                    expand.setInterpolator(new AccelerateInterpolator());
    
                    // Rotate Animation
                    Animation rotate = new RotateAnimation(60.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                    rotate.setDuration(200);
                    rotate.setInterpolator(new DecelerateInterpolator());
    
                    // Add both animations to animation state
                    AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators
                    animationSet.addAnimation(expand);
                    animationSet.addAnimation(rotate);
                    fab.startAnimation(animationSet);
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            fab.startAnimation(shrink);
        } else {
            // if the fab is already scaled down
            // Change FAB color and icon
            fab.setBackgroundTintList(getResources().getColorStateList(colorIntArray[position]));
            fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));
            fab.show();
    
            // Scale up animation
            ScaleAnimation expand = new ScaleAnimation(0.2f, 1f, 0.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            expand.setDuration(100);     // animation duration in milliseconds
            expand.setInterpolator(new AccelerateInterpolator());
    
            // Rotate Animation
            Animation rotate = new RotateAnimation(60.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            rotate.setDuration(200);
            rotate.setInterpolator(new DecelerateInterpolator());
    
            // Add both animations to animation state
            AnimationSet animationSet = new AnimationSet(false); //false means don't share interpolators
            animationSet.addAnimation(expand);
            animationSet.addAnimation(rotate);
            fab.startAnimation(animationSet);
        }
    
    }
    

それから、blackcjの変更されていないタブ選択リスナーをonCreateメソッドに追加しました。

    tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            viewPager.setCurrentItem(tab.getPosition());
            animateFab(tab.getPosition());
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

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

        }
    });

これがお役に立てば幸いです、それは確かに私にとって完璧に機能します。 blackcj&kirtan403に感謝します。 :)

2
inferKNOX

ビューページャーにリスナーを追加し、ビューページャーのスクロールを開始したときにその状態に応じてファブを表示および非表示にできます。これは、SCROLL_STATE_DRAGGING SCROLL_STATE_SETTLING SCROLL_STATE_IDLEの状態の順序です。

例えば:

viewPager.addOnPageChangeListener(this);
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if(state==ViewPager.SCROLL_STATE_IDLE)
            fab.show();
        else if(state==ViewPager.SCROLL_STATE_DRAGGING)
            fab.hide();

    }
1
Omid Aminiva

それが私のために働いたものです:

private boolean isFloatingActionButtonHidden = false;
private int[] colorIntArray = {R.color.walking,R.color.running,R.color.biking,R.color.paddling,R.color.golfing};
private int[] iconIntArray = {R.drawable.ic_walk_white,R.drawable.ic_run_white,R.drawable.ic_bike_white,R.drawable.ic_add_white,R.drawable.ic_arrow_back_white};

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:
                // This is triggered just before the view pager reaches the final state
                // if you want to trigger the animation after the page reaches its final position
                // just move this to "case ViewPager.SCROLL_STATE_IDLE:"
                showFloatingActionButton(viewPager.getCurrentItem());
                break;
            case ViewPager.SCROLL_STATE_IDLE:
                // This is only triggered if user pulls to the left of the start or right of the end
                if (isFloatingActionButtonHidden) {
                    showFloatingActionButton(viewPager.getCurrentItem());
                }
                break;
            default:
                // in all other cases just hide the fab if it is not visable
                if (!isFloatingActionButtonHidden) {
                    hideFloatingActionButton();
                }
        }
   }
});

private void showFloatingActionButton(int position) {
    fab.setImageDrawable(getResources().getDrawable(iconIntArray[position], null));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        floatingActionButton.setBackgroundTintList(getResources().getColorStateList(iconIntArray[position], getTheme()));
    } else {
        floatingActionButton.setBackgroundTintList(getResources().getColorStateList(iconIntArray[position]));
    }

    floatingActionButton.show();
}

private void hideFloatingActionButton() {
    isFloatingActionButtonHidden = true;
    floatingActionButton.hide();
}
0
Wilko