Android UI进阶之旅11--Material Desig

2017-05-21  本文已影响172人  小楠总

FloatingActionButton

一个基本的使用例子如下:

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_gravity="right|bottom"
    android:onClick="rotate"
    android:src="@drawable/ic_add_white_24dp"
    app:backgroundTint="?attr/colorPrimary"
    app:elevation="10dp"
    app:fabSize="normal"
    app:rippleColor="#f00"
    />

其中:

  1. src属性是设置图标
  2. backgroundTint是设置背景色(图标是透明背景的)
  3. elevation是设置阴影大小
  4. fabsize是设置图标的大小,一般为normal(不用设置)
  5. rippleColor是设置水波纹的颜色

点击事件如下(旋转):

private boolean reverse = false;

public void rotate(View v) {
    float toDegree = reverse ? -180f : 180f;
    ObjectAnimator animator = ObjectAnimator
            .ofFloat(v, "rotation", 0.0f, toDegree)
            .setDuration(400);
    animator.start();
    reverse = !reverse;
}

兼容性问题

与CardViewy一样:

阴影的细微差别,这里直接在不同的dimen中设置即可:

  1. 5.x系统:边距阴影比较小,需要手动添加边距16dp,android:layout_margin="16dp"
  2. 4.x系统:边距阴影比较大,手动修改边距0dp(原因:兼容包里面设置阴影效果自动设置了margin来处理16dp)

FloatingActionButton动画

列表滑动的时候FloatingActionButton隐藏与显示

通过自定义OnScrollListener实现

核心的逻辑如下:

public class FabScrollListener extends OnScrollListener {
    private static final int THRESHOLD = 20;
    private int distance = 0;
    private HideScrollListener hideListener;
    private boolean visible = true;//是否可见

    public FabScrollListener(HideScrollListener hideScrollListener) {
        this.hideListener = hideScrollListener;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        /**
         * dy:Y轴方向的增量
         * 有正和负
         * 当正在执行动画的时候,就不要再执行了
         */
        if (distance > THRESHOLD && visible) {
            //隐藏动画
            visible = false;
            hideListener.onHide();
            distance = 0;
        } else if (distance < -THRESHOLD && !visible) {
            //显示动画
            visible = true;
            hideListener.onShow();
            distance = 0;
        }
        if (visible && dy > 0 || (!visible && dy < 0)) {
            distance += dy;
        }
    }

}

自定义一个OnScrollListener,重写onScrolled方法。判断当前的滚动方向、滚动距离、当前的FloatingActionButton是否显示来进行相应的逻辑处理。

其中HideScrollListener是一个自定义的监听接口:

public interface HideScrollListener {
    void onHide();
    void onShow();
}

由Activity实现这个接口:

public class FabAnimActivity extends AppCompatActivity implements HideScrollListener {

    private RecyclerView recyclerview;
    private ImageButton fab;
    private Toolbar toolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //省略一些代码

        //添加滑动监听
        recyclerview.addOnScrollListener(new FabScrollListener(this));
    }

    @Override
    public void onHide() {
        // 隐藏动画--属性动画
        toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));
        RelativeLayout.LayoutParams layoutParams = (LayoutParams) fab.getLayoutParams();

        fab.animate().translationY(fab.getHeight() + layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
    }

    @Override
    public void onShow() {
        // 显示动画--属性动画
        toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));

        fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
    }
}

通过自定义FloatingActionButton的Behavior实现

通过协调并调度里面的子控件或者布局来实现触摸(一般是指滑动)产生一些相关的动画效果。可以通过设置view的Behavior来实现触摸的动画调度。

继承FloatingActionButton的Behavior:

public class FabBehavior extends FloatingActionButton.Behavior {
    private boolean visible = true;//是否可见

    //实例化CoordinatorLayout.LayoutParams时反射生成Behavior实例,这就是为什么自定义behavior需要重写如下的构造函数
    public FabBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
        // 当观察的View(RecyclerView)发生滑动的开始的时候回调的
        //nestedScrollAxes:滑动关联轴, 我们现在只关心垂直的滑动。
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        // 当观察的view滑动的时候回调的
        //根据情况执行动画
        if (dyConsumed > 0 && visible) {
            //show
            visible = false;
            onHide(child);
        } else if (dyConsumed < 0) {
            //hide
            visible = true;
            onShow(child);
        }

    }

    public void onHide(FloatingActionButton fab) {
        // 隐藏动画--属性动画
//      toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));

        CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
//      fab.animate().translationY(fab.getHeight()+layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
        //FAB 缩小
        ViewCompat.animate(fab).scaleX(0f).scaleY(0f).start();
    }

    public void onShow(FloatingActionButton fab) {
        // 显示动画--属性动画
//      toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));

        CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
//      fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
        //FAB放大
        ViewCompat.animate(fab).scaleX(1f).scaleY(1f).start();
    }

}

其中:

构造方法必须重写,重写onStartNestedScroll返回判断哪个方向的滑动,重写onNestedScroll进行相应的逻辑处理(FloatingActionButton的属性动画显示与隐藏)。

最后在布局文件中使用CoordinatorLayout布局,并且给FloatingActionButton添加自定义的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:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:paddingTop="?attr/actionBarSize"
        />

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:title="Fab动画"
        app:titleTextColor="#fff"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="58dp"
        android:layout_height="58dp"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@drawable/ic_favorite_outline_white_24dp"
        app:layout_behavior="com.nan.advancedui.fab.anim.behavior.FabBehavior"
        />

</android.support.design.widget.CoordinatorLayout>

如果觉得我的文字对你有所帮助的话,欢迎关注我的公众号:

公众号:Android开发进阶

我的群欢迎大家进来探讨各种技术与非技术的话题,有兴趣的朋友们加我私人微信huannan88,我拉你进群交(♂)流(♀)

上一篇下一篇

猜你喜欢

热点阅读