実装しようとしていますFloatingActionButtonfromGoogle Design Support Libraryを3つのタブのうちの2つに、そして Material Design Guidelines-FloatingActionButton に従って:
複数の横画面(タブなど)にフローティングアクションボタンがある場合、各画面に入ると、それぞれに含まれるアクションが異なる場合、ボタンは表示および非表示になります。アクションが同じ場合、ボタンは画面に表示されたままになります(必要に応じて新しい位置に変換されます)。
アプリのFABボタンにこの種のトランジションまたはアニメーションを作成するにはどうすればよいですか?
この機能は現在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) {
}
});
持っているタブの数に合わせて十分な色とアイコンを用意してください。
以下は、希望する結果を得るための簡単な方法です
以下のようなメインアクティビティに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) {
}
};
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) {
}
});
最後に、非常に簡単で、添付のgifとまったく同じアニメーションを表示するソリューションを見つけました。@ NaumanのOder @Omidのソリューションでは、アニメーションの非表示が完了する前にアニメーションが表示されます。ただし、必ず最新のサポートライブラリを使用してください。バージョン23.2.1でテストしました。
使用事例:
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.OnPageChangeListener
のonCreate(...)
メソッドは起動時に呼び出されないため、デフォルトのタブでfabを表示するためにonPageSelected(...)
メソッドで関数を手動で1回呼び出しますshowRightFab(...)
関数が呼び出されたことがないため表示されます)。
showRightFab(viewPager.getCurrentItem());
その作業は私のアプリケーションでは完璧です!
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
とタブを備えたonCreate
のActivity
メソッドに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に感謝します。 :)
ビューページャーにリスナーを追加し、ビューページャーのスクロールを開始したときにその状態に応じてファブを表示および非表示にできます。これは、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();
}
それが私のために働いたものです:
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();
}