如何在Google+应用中为Android设置FloatingActionButtonanimation?

我将FloatingActionButton设置为屏幕底部,我想为button设置animation

  • 向下滚动时隐藏
  • 滚动时显示

就像谷歌在他们的Google+应用中实现它一样。

我认为CoordinatorLayout和AppBarLayout是需要的,但如何实现它与FloatingActionButton一起使用?

Solutions Collecting From Web of "如何在Google+应用中为Android设置FloatingActionButtonanimation?"

您可以使用默认的FloatingActionButton使用app:layout_behavior更改其默认Behavior来实现它app:layout_behavior属性:

您可以使用如下的布局:

  <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" 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/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:layout_scrollFlags="scroll|enterAlways" /> </android.support.design.widget.AppBarLayout> // Your layout, for example a RecyclerView <RecyclerView ..... app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <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:src="@drawable/ic_done" app:layout_behavior="com.support.android.designlibdemo.ScrollAwareFABBehavior" /> </android.support.design.widget.CoordinatorLayout> 

随着app:layout_behavior你可以定义你自己的Behavior 。 使用onStartNestedScroll()onNestedScroll()方法,您可以与滚动事件交互。

你可以使用这样的行为。 你可以在这里find原始的代码:

 public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior { private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator(); private boolean mIsAnimatingOut = false; 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 && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) { // User scrolled down and the FAB is currently visible -> hide the FAB animateOut(child); } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) { // User scrolled up and the FAB is currently not visible -> show the FAB animateIn(child); } } // Same animation that FloatingActionButton.Behavior uses to hide the FAB when the AppBarLayout exits private void animateOut(final FloatingActionButton button) { if (Build.VERSION.SDK_INT >= 14) { ViewCompat.animate(button).scaleX(0.0F).scaleY(0.0F).alpha(0.0F).setInterpolator(INTERPOLATOR).withLayer() .setListener(new ViewPropertyAnimatorListener() { public void onAnimationStart(View view) { ScrollAwareFABBehavior.this.mIsAnimatingOut = true; } public void onAnimationCancel(View view) { ScrollAwareFABBehavior.this.mIsAnimatingOut = false; } public void onAnimationEnd(View view) { ScrollAwareFABBehavior.this.mIsAnimatingOut = false; view.setVisibility(View.GONE); } }).start(); } else { Animation anim = AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_out); anim.setInterpolator(INTERPOLATOR); anim.setDuration(200L); anim.setAnimationListener(new Animation.AnimationListener() { public void onAnimationStart(Animation animation) { ScrollAwareFABBehavior.this.mIsAnimatingOut = true; } public void onAnimationEnd(Animation animation) { ScrollAwareFABBehavior.this.mIsAnimatingOut = false; button.setVisibility(View.GONE); } @Override public void onAnimationRepeat(final Animation animation) { } }); button.startAnimation(anim); } } // Same animation that FloatingActionButton.Behavior uses to show the FAB when the AppBarLayout enters private void animateIn(FloatingActionButton button) { button.setVisibility(View.VISIBLE); if (Build.VERSION.SDK_INT >= 14) { ViewCompat.animate(button).scaleX(1.0F).scaleY(1.0F).alpha(1.0F) .setInterpolator(INTERPOLATOR).withLayer().setListener(null) .start(); } else { Animation anim = AnimationUtils.loadAnimation(button.getContext(), R.anim.fab_in); anim.setDuration(200L); anim.setInterpolator(INTERPOLATOR); button.startAnimation(anim); } } } 

截至这篇文章,没有方法会自动处理隐藏和显示devise支持库中的FloatingActionButton 。 我知道这是因为这是我的第一份工作。

您正在考虑的方法用于在创buildSnackbar时向上和向下设置FloatingActionButtonanimation效果,如果使用的是CoordinatorLayout ,则此方法将起作用。

这是我的代码。 这是基于这个回购。 它具有RecyclerViewAbsListView监听器,可以自动处理button的animation。 你可以做

 button.show(); 

要么

 button.hide(); 

手动隐藏button,或者您可以调用:

 button.attachToListView(listView); 

 button.attachToRecyclerView(recyclerView); 

它会隐藏在向下滚动和显示上滚动没有进一步的代码。

希望这可以帮助!

AnimatedFloatingActionButton:

 public class AnimatedFloatingActionButton extends FloatingActionButton { private static final int TRANSLATE_DURATION_MILLIS = 200; private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); private boolean mVisible; public AnimatedFloatingActionButton(Context context, AttributeSet attrs) { super(context, attrs); Log.i("Abscroll", "mVisible" + mVisible); } public void show() { show(true); } public void hide() { hide(true); } public void show(boolean animate) { toggle(true, animate, false); } public void hide(boolean animate) { toggle(false, animate, false); } private void toggle(final boolean visible, final boolean animate, boolean force) { if (mVisible != visible || force) { mVisible = visible; int height = getHeight(); if (height == 0 && !force) { ViewTreeObserver vto = getViewTreeObserver(); if (vto.isAlive()) { vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { ViewTreeObserver currentVto = getViewTreeObserver(); if (currentVto.isAlive()) { currentVto.removeOnPreDrawListener(this); } toggle(visible, animate, true); return true; } }); return; } } int translationY = visible ? 0 : height + getMarginBottom(); Log.i("Abscroll", "transY" + translationY); if (animate) { this.animate().setInterpolator(mInterpolator) .setDuration(TRANSLATE_DURATION_MILLIS) .translationY(translationY); } else { setTranslationY(translationY); } } } private int getMarginBottom() { int marginBottom = 0; final ViewGroup.LayoutParams layoutParams = getLayoutParams(); if (layoutParams instanceof ViewGroup.MarginLayoutParams) { marginBottom = ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin; } return marginBottom; } public void attachToListView(@NonNull AbsListView listView) { listView.setOnScrollListener(new AbsListViewScrollDetector() { @Override void onScrollUp() { hide(); } @Override void onScrollDown() { show(); } @Override void setScrollThreshold() { setScrollThreshold(getResources().getDimensionPixelOffset(R.dimen.fab_scroll_threshold)); } }); } public void attachToRecyclerView(@NonNull RecyclerView recyclerView) { recyclerView.addOnScrollListener(new RecyclerViewScrollDetector() { @Override void onScrollUp() { hide(); } @Override void onScrollDown() { show(); } @Override void setScrollThreshold() { setScrollThreshold(getResources().getDimensionPixelOffset(R.dimen.fab_scroll_threshold)); } }); } } 

AbsListViewScrollDetector:

 abstract class AbsListViewScrollDetector implements AbsListView.OnScrollListener { private int mLastScrollY; private int mPreviousFirstVisibleItem; private AbsListView mListView; private int mScrollThreshold; abstract void onScrollUp(); abstract void onScrollDown(); abstract void setScrollThreshold(); @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(totalItemCount == 0) return; if (isSameRow(firstVisibleItem)) { int newScrollY = getTopItemScrollY(); boolean isSignificantDelta = Math.abs(mLastScrollY - newScrollY) > mScrollThreshold; Log.i("Abscroll", "mLastScrollY " + mLastScrollY); Log.i("Abscroll", "newScrollY " + newScrollY); if (isSignificantDelta) { Log.i("Abscroll", "sig delta"); if (mLastScrollY > newScrollY) { onScrollUp(); Log.i("Abscroll", "sig delta up"); } else { onScrollDown(); Log.i("Abscroll", "sig delta down"); } } mLastScrollY = newScrollY; } else { if (firstVisibleItem > mPreviousFirstVisibleItem) { onScrollUp(); Log.i("Abscroll", "prev up"); } else { onScrollDown(); Log.i("Abscroll", "prev down"); } mLastScrollY = getTopItemScrollY(); mPreviousFirstVisibleItem = firstVisibleItem; } } public void setScrollThreshold(int scrollThreshold) { mScrollThreshold = scrollThreshold; Log.i("Abscroll", "LView thresh " + scrollThreshold); } public void setListView(@NonNull AbsListView listView) { mListView = listView; } private boolean isSameRow(int firstVisibleItem) { return firstVisibleItem == mPreviousFirstVisibleItem; } private int getTopItemScrollY() { if (mListView == null || mListView.getChildAt(0) == null) return 0; View topChild = mListView.getChildAt(0); return topChild.getTop(); } } 

RecyclerViewScrollDetector:

 abstract class RecyclerViewScrollDetector extends RecyclerView.OnScrollListener { private int mScrollThreshold; abstract void onScrollUp(); abstract void onScrollDown(); abstract void setScrollThreshold(); @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { boolean isSignificantDelta = Math.abs(dy) > mScrollThreshold; if (isSignificantDelta) { if (dy > 0) { onScrollUp(); Log.i("Abscroll", "Rview up"); } else { onScrollDown(); Log.i("Abscroll", "RView down"); } } } public void setScrollThreshold(int scrollThreshold) { mScrollThreshold = scrollThreshold; Log.i("Abscroll", "RView thresh " + scrollThreshold); } } 

谷歌devise支持库将做到这一点。

尝试将此代码实现到您的布局文件中:

 <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_add_black" android:layout_gravity="bottom|end" app:elevation="6dp" app:pressedTranslationZ="12dp"/> 

compile 'com.android.support:design:22.2.0'到你的build.gradle是很重要的。 如果你使用eclipse,还有另外一种方法可以将这个库添加到你的项目中。

重要资源:
devise支持库(II):浮动操作button
Androiddevise支持库
Google发布一个新的devise支持库[更新]