動畫必須有(二):懸浮選單瞭解一下!

weixin_33860722發表於2018-07-27

動畫必須有(一): 屬性動畫淺談
githhub傳送門


目錄

  • 前言
  • 效果圖
  • FloatingActionButton基礎
  • FloatingActionButton例項
  • 最後

前言

懸浮按鈕是我非常喜歡的, 可以把最關鍵的功能放入到懸浮按鈕中. 比如日記app裡的新建日記, 閱讀類app裡的喜歡. 稍微處理一下可以將懸浮按鈕擴充套件成懸浮選單, 來看下實現吧! github直接看原始碼


效果圖

廢話不多說, 先看圖, 感興趣再往下看!

5319256-00fba85952a1caf7.gif
懸浮選單

FloatingActionButton基礎

記得導包.

compile 'com.android.support:design:26.+'

可以看看谷歌官方介紹. 你會被瞬間圈粉. 然後是官方文件, 這個文件說了如何呼叫.

  • 搭配Snackbar
    官方推薦配合Snackbar來使用, 這都不多說了.
5319256-8d983fbb28dc7cdd.gif
配合Snackbar
  • 顯示和隱藏
    然後還有就是懸浮按鈕的隱藏和顯示函式.
Button btHide = (Button) findViewById(R.id.bt_hide);
btHide.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        fab.hide();
    }
});

Button btShow = (Button) findViewById(R.id.bt_show);
btShow.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        fab.show();
    }
});
5319256-d48d8678a4ae454a.gif
隱藏和顯示
  • 顏色
    可以設定點選顏色app:rippleColor, 以及背景顏色app:backgroundTint. 我將背景色改成藍色, 點選水波紋擴散變為紫色, 效果圖如下:
5319256-77d02b5e190fed3b.jpg
設定顏色
5319256-db1b5a61a91b125b.gif
注意看顏色
  • 位置
    當然了, 位置可以隨便改, 甚至可以吸附在某個控制元件之上.
android:layout_gravity="bottom|left"
5319256-6819064e78e1c16c.jpg
設定位置

吸附效果如下, 即使滾動也會保持相對的位置:

app:layout_anchor="@id/toolbar"
app:layout_anchorGravity="center|bottom"
5319256-2f7e88b0863de4c5.jpg
吸附並設定位置

FloatingActionButton例項

來看看效果圖是如何實現的吧.

  • 佈局檔案
    佈局檔案是個要點, 裡面塞進了兩個選單, 你選一個喜歡的用就好. 一個是扇型的, 一個是線型的.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">

    <FrameLayout
        android:id="@+id/fl_fan_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone">

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginBottom="@dimen/twenty_dp"
            android:layout_marginEnd="@dimen/twenty_dp"
            android:src="@mipmap/ic_launcher"
            app:backgroundTint="@color/colorAccent"
            app:elevation="@dimen/zero_dp"
            app:fabSize="mini" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_left_top"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginBottom="@dimen/twenty_dp"
            android:layout_marginEnd="@dimen/twenty_dp"
            android:src="@mipmap/ic_launcher"
            app:backgroundTint="@color/colorAccent"
            app:elevation="@dimen/zero_dp"
            app:fabSize="mini" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_top"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginBottom="@dimen/twenty_dp"
            android:layout_marginEnd="@dimen/twenty_dp"
            android:src="@mipmap/ic_launcher"
            app:backgroundTint="@color/colorAccent"
            app:elevation="@dimen/zero_dp"
            app:fabSize="mini" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_origin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_marginBottom="@dimen/twenty_dp"
            android:layout_marginEnd="@dimen/twenty_dp"
            android:src="@drawable/ic_add"
            app:backgroundTint="@color/colorPrimary"
            app:fabSize="normal" />
    </FrameLayout>

    <RelativeLayout
        android:id="@+id/rl_line_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:id="@+id/rl_menu_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone">

            <LinearLayout
                android:id="@+id/ll_fun1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="@dimen/hundred_dp"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_toLeftOf="@+id/fab_mini1"
                    android:layout_weight="1"
                    android:gravity="right"
                    android:paddingRight="@dimen/eight_dp"
                    android:text="1"
                    android:textColor="@android:color/white"
                    android:textSize="@dimen/sixteen_sp" />

                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/fab_mini1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="@dimen/twenty_six_dp"
                    android:src="@mipmap/ic_launcher"
                    app:backgroundTint="@color/colorPrimary"
                    app:fabSize="mini" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/ll_fun2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_above="@+id/ll_fun1"
                android:layout_marginBottom="@dimen/twenty_dp"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_toLeftOf="@+id/fab_mini2"
                    android:layout_weight="1"
                    android:gravity="right"
                    android:paddingRight="@dimen/eight_dp"
                    android:text="2"
                    android:textColor="@android:color/white"
                    android:textSize="@dimen/sixteen_sp" />

                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/fab_mini2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="@dimen/twenty_six_dp"
                    android:src="@mipmap/ic_launcher"
                    app:backgroundTint="@color/colorPrimary"
                    app:fabSize="mini" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/ll_fun3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_above="@+id/ll_fun2"
                android:layout_marginBottom="@dimen/twenty_dp"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_toLeftOf="@+id/fab_mini2"
                    android:layout_weight="1"
                    android:gravity="right"
                    android:paddingRight="@dimen/eight_dp"
                    android:text="3"
                    android:textColor="@android:color/white"
                    android:textSize="@dimen/sixteen_sp" />

                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/fab_mini3"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="@dimen/twenty_six_dp"
                    android:src="@mipmap/ic_launcher"
                    app:backgroundTint="@color/colorPrimary"
                    app:fabSize="mini" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/ll_fun4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_above="@+id/ll_fun3"
                android:layout_marginBottom="@dimen/twenty_dp"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_toLeftOf="@+id/fab_mini2"
                    android:layout_weight="1"
                    android:gravity="right"
                    android:paddingRight="@dimen/eight_dp"
                    android:text="4"
                    android:textColor="@android:color/white"
                    android:textSize="@dimen/sixteen_sp" />

                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/fab_mini4"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="@dimen/twenty_six_dp"
                    android:src="@mipmap/ic_launcher"
                    app:backgroundTint="@color/colorPrimary"
                    app:fabSize="mini" />
            </LinearLayout>
        </RelativeLayout>

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="@dimen/twenty_dp"
            android:layout_marginEnd="@dimen/twenty_dp"
            android:backgroundTint="@color/colorAccent"
            android:src="@drawable/ic_add"
            app:fabSize="normal"
            app:rippleColor="@color/colorPrimaryDark" />
    </RelativeLayout>

    <Button
        android:id="@+id/bt_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/switch_menu" />
</RelativeLayout>
  • normal與mini
    懸浮按鈕有兩種尺寸, normal和mini. 在xml中加入app:fabSize="mini"就變成mini尺寸的了. 所以在設定動畫和位置的時候不是將按鈕全部放置在同一位置, 需要修正位置. 修正距離就是(normal-mini)/2, 應該很好理解.
// 計算偏移值
int w = View.MeasureSpec.makeMeasureSpec(0,
        View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
        View.MeasureSpec.UNSPECIFIED);
mFabOrigin.measure(w, h);
mFabLeft.measure(w, h);
mOffset = (mFabOrigin.getMeasuredHeight() - mFabLeft.getMeasuredHeight()) / 2;

// 修正位置
FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) mFabLeft.getLayoutParams();
lParams.setMargins(0, 0, UIUtil.dp2px(20) + mOffset, UIUtil.dp2px(20) + mOffset);
mFabLeft.setLayoutParams(lParams);
FrameLayout.LayoutParams ltParams = (FrameLayout.LayoutParams) mFabLeftTop.getLayoutParams();
ltParams.setMargins(0, 0, UIUtil.dp2px(20) + mOffset, UIUtil.dp2px(20) + mOffset);
mFabLeftTop.setLayoutParams(ltParams);
FrameLayout.LayoutParams tParams = (FrameLayout.LayoutParams) mFabTop.getLayoutParams();
tParams.setMargins(0, 0, UIUtil.dp2px(20) + mOffset, UIUtil.dp2px(20) + mOffset);
mFabTop.setLayoutParams(tParams);
  • 關於動畫
    只要位置算對了, 動畫不是特別難, 當然想要像google或者apple的動畫那樣舒適還是很難的. 沒看第一篇的可以回頭看看.
/**
 * 顯示扇型選單
 */
private void showFanMenu() {
    // 識別符號設定是
    mFanMenuOpen = true;

    // 按鈕1向左移動
    int x = (int) mFabOrigin.getX();
    int y = (int) mFabOrigin.getY();
    ValueAnimator va1 = ValueAnimator.ofInt(x, x - ConstantUtil.FAN_OFFSET);
    va1.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int l = (int) animation.getAnimatedValue();
            int t = (int) mFabLeft.getY();
            int r = mFabLeft.getWidth() + l;
            int b = mFabLeft.getHeight() + t;
            mFabLeft.layout(l, t, r, b);
        }
    });

    // 按鈕2向左上移動
    ValueAnimator va2x = ValueAnimator.ofInt(x, x - (int) (ConstantUtil.FAN_OFFSET / Math.pow(2, 1.0 / 2)));
    ValueAnimator va2y = ValueAnimator.ofInt(y, y - (int) (ConstantUtil.FAN_OFFSET / Math.pow(2, 1.0 / 2)));
    va2x.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int l = (int) animation.getAnimatedValue();
            int t = (int) mFabLeftTop.getY();
            int r = mFabLeftTop.getWidth() + l;
            int b = mFabLeftTop.getHeight() + t;
            mFabLeftTop.layout(l, t, r, b);
        }
    });
    va2y.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int l = (int) mFabLeftTop.getX();
            int t = (int) animation.getAnimatedValue();
            int r = mFabLeftTop.getWidth() + l;
            int b = mFabLeftTop.getHeight() + t;
            mFabLeftTop.layout(l, t, r, b);
        }
    });

    // 按鈕3向上移動
    ValueAnimator va3 = ValueAnimator.ofInt(y, y - ConstantUtil.FAN_OFFSET);
    va3.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int l = (int) mFabTop.getX();
            int t = (int) animation.getAnimatedValue();
            int r = mFabTop.getWidth() + l;
            int b = mFabTop.getHeight() + t;
            mFabTop.layout(l, t, r, b);
        }
    });

    // 開始動畫
    va1.start();
    va2x.start();
    va2y.start();
    va3.start();
}
  • 切換圖示
    然後就是在不同狀態切換懸浮按鈕的圖示, 使用setImageResource方法即可.
mFabAdd.setImageResource(mLineMenuOpen ? R.drawable.ic_add : R.drawable.ic_close);

最後

我本人還是很喜歡google的material design的, 這個懸浮按鈕也非常實用. 喜歡記得點贊或者關注我哦, 有意見或者建議評論區見~

動畫必須有(一): 屬性動畫淺談
githhub傳送門


相關文章