How to animate FloatingActionButton like in Google+ app for Android?

前端 未结 3 1262
轮回少年
轮回少年 2020-12-23 10:49

I set FloatingActionButton to bottom of screen and I want to animate the button.

  • Hidden when scrolling down
  • Shown when scrolling up

Lik

相关标签:
3条回答
  • 2020-12-23 11:07

    You can achieve it using the default FloatingActionButton changing its default Behavior using the app:layout_behavior attribute:

    You can use a layout like:

     <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>
    

    With the app:layout_behavior you can define your own Behavior. With the onStartNestedScroll() and onNestedScroll() methods you can interact with scroll events.

    You can use a Behavior like this. You can find the original code here:

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-23 11:11

    Googles Design Support Library will do that.

    Try to implement this code to your layout file:

    <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"/>
    

    It's important that you add compile 'com.android.support:design:22.2.0' to your build.gradle. If you're using eclipse there is another way to add this library to your project.

    Important resources:
    Design Support Library (II): Floating Action Button
    Android Design Support Library
    Google Releasing A FABulous New Design Support Library [Updated]

    0 讨论(0)
  • 2020-12-23 11:32

    As of this post, there are no methods that will automatically handle hiding and showing the FloatingActionButton in the Design Support Libraries. I know this because this was my first assignment at work.

    The methods you are thinking of are used to animate the FloatingActionButton up and down when a Snackbar is created, and yes, that will work if you are using a CoordinatorLayout.

    Here's my code. It's based off of this repo. It has listeners for RecyclerView and AbsListView that handle animating the button automatically. You can either do

    button.show();
    

    or

    button.hide();
    

    to hide the button manually, or you can call:

    button.attachToListView(listView);
    

    and

    button.attachToRecyclerView(recyclerView);
    

    and it will hide on scroll down and show on scroll up with no further code.

    Hope this helps!

    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);
    }
    }
    
    0 讨论(0)
提交回复
热议问题