您的位置:首页 > 其它

Material Design系列,自定义Behavior支持所有View

2016-08-14 18:39 459 查看

Material Design系列,自定义Behavior支持所有View

版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003

友情连接:

Material Design博客专栏

系列博客:

1. Material Design系列,Behavior之BottomSheetBehavior与BottomSheetDialog

2. Material Design系列,Behavior之SwipeDismissBehavior

3. Material Design系列,自定义Behavior之上滑显示返回顶部按钮

4. Material Design系列,自定义Behavior实现Android知乎首页

5. Material Design系列,自定义Behavior支持所有 View

建议看今天博客的客官先看下之前的几篇博客,因为今天的博客是前几篇的一个升级,不看前面的,看这篇你会觉得没啥意思。

博客和Demo源码下载:传送门

一、实现效果图

这个右下角的FAB,动画当然可以多种多样,可以放在界面的任何地方,我们这里只举个例子。但是v7包中提供的
Behavior
目前只能是
FloatingActionButton
来用,所以今天我们实现的这个Behavior是支持所有的View的,可以用在
ImageView
Button
Layout
,只要是继承
View
的类都可以用。



二、自定义Behavior和动画的封装

我们知道
Behavior
CoordinatorLayout
的一个子类,
Ctrl + T
查看它的实现类目前有如下几个:

1. AppBarLayout.Behavior;
2. AppBarLayout.ScrollingViewBehavior;
3. FloatingActionButton.Behavior;
4. Snackbar.Behavior;
5. BottomSheetBehaviro;
6. SwipeDismissBehavior;
7. HeaderBehavior;
8. ViewOffsetBehavior;
9. HeaderScrollingViewBehavior;


其中第1、7是抽象类,8是package保护的类,9是8的一个子类,我们回头再说。

AppBarLayout.ScrollingViewBehavior
我们经常用,也就是我们在
layout xml
中经常用的:
app:layout_behavior="@string/appbar_scrolling_view_behavior"


Snackbar.Behavior
被用于
Snackbar
,这个不用多说。

FloatingActionButton.Behavior
BottomSheetBehaviro
SwipeDismissBehavior
在文章开头的几个友情链接的博客中已经讲的很清楚了,大家可以回过头去再看看。

今天讲的是自定义Behavior支持所有View作为FAB,那么也就是
FloatingActionButton.Behavior
了,但是它只支持
FloatingActionButton
,所以今天我们要自己继承
Behavior
来写
DefineBehavior
。所以第一步就是打开
FloatingActionButton.Behavior
的源码看。

实现BasicBehavior

首先必须要知道的是
CoordinatorLayout.Behavior
这个基类是支持泛型的,看到
FloatingActionButton.Behavior
后发先它是限制了引用它的
View
必须是
FloatingActionButton
罢了,那我们这里也来学它继承一下就OK了。

我们新建一个类
BasicBehavior
,把
FloatingActionButton.Behavior
的代码拷贝过来,把里面的泛型改为如下:

public class BasicBehavior<T extends View> extends CoordinatorLayout.Behavior<T>;


也就是说只要引用
实现BasicBehavior
的类是个
View
就可以,所以接着把
BasicBehavior
里面拷贝的代码中把引用泛型为
FloatingActionButton
的地方改为
View
,嗯觉得打工告成的时候发现有几个类的包导不进来:



仔细一看,这几个类在
android.support.design.widget
包下,一想肯定这几个类是package保护的类,所以我们在我们的项目下新建一个
android.support.design.widget
包,把
实现BasicBehavior
移到新建的包下,发现问题迎刃而解。

项目源码和
BasicBehavior
的完整源代码下载链接请在文章开头或者末尾找。

动画的实现和简化

(没看之前博客的客观一定要回过头看看,一定会有不一样的收获。)

我们在之前的同系列博客中,实现View的缩放动画的时候,尤其是在View被隐藏时须用如下代码记录View移出动画是否执行完,因为在界面滑动的时候View移除会被
Behavior
一直调用,所以不能重复执行,需要用一个值来记录:

// 记录View移出动画是否执行完。
private boolean isOutExecute = false;

private ViewPropertyAnimatorListener outAnimatorListener = new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isOutExecute = true;
}

@Override
public void onAnimationEnd(View view) {
view.setVisibility(View.GONE);
isOutExecute = false;
}

@Override
public void onAnimationCancel(View view) {
isOutExecute = false;
}
};


为了不在每一个调用的地方都写这么长一段,我们把这端代码封装成一个类,简化如下:

public static class ListenerAnimatorEndBuild {
// 记录View移出动画是否执行完。
private boolean isOutExecute = false;

private ViewPropertyAnimatorListener outAnimatorListener;

public ListenerAnimatorEndBuild() {
outAnimatorListener = new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isOutExecute = true;
}
@Override
public void onAnimationEnd(View view) {
view.setVisibility(View.GONE);
isOutExecute = false;
}

@Override
public void onAnimationCancel(View view) {
isOutExecute = false;
}
};
}

// View移出动画是否执行完。
public boolean isFinish() {
return !isOutExecute;
}

// 返回ViewPropertyAnimatorListener。
public ViewPropertyAnimatorListener build() {
return outAnimatorListener;
}
}


这样一来我们在用的时候就只是两行代码了:

ListenerAnimatorEndBuild listenerAnimatorEndBuild = new ListenerAnimatorEndBuild();

// 判断是否执行完动画:
listenerAnimatorEndBuild.isFinish();


继承BasicBehavior实现DefineBavior

前面定义好了
BasicBehavior
,这里只需要继承
BasicBehavior
实现我们的动画逻辑:

public class DefineBehavior extends BasicBehavior<View> {

private ListenerAnimatorEndBuild listenerAnimatorEndBuild;

public DefineBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
listenerAnimatorEndBuild = new ListenerAnimatorEndBuild();
}

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}

@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
//        if (dyConsumed > 0 && dyUnconsumed == 0) {
//            System.out.println("上滑中。。。");
//        }
//        if (dyConsumed == 0 && dyUnconsumed > 0) {
//            System.out.println("到边界了还在上滑。。。");
//        }
//        if (dyConsumed < 0 && dyUnconsumed == 0) {
//            System.out.println("下滑中。。。");
//        }
//        if (dyConsumed == 0 && dyUnconsumed < 0) {
//            System.out.println("到边界了,还在下滑。。。");
//        }

// 这里可以写你的其他逻辑动画,这里只是举例子写了个缩放动画。
if ((dyConsumed > 0 || dyUnconsumed > 0) && listenerAnimatorEndBuild.isFinish() && child.getVisibility() == View.VISIBLE) {//往下滑
scaleHide(child, listenerAnimatorEndBuild.build());
} else if ((dyConsumed < 0 || dyUnconsumed < 0) && child.getVisibility() != View.VISIBLE) {
scaleShow(child, null);
}
}
}


你可能会很惊讶,哈哈,不要惊讶,封装的好久是这么简单就能实现所有的View支持。

三、如何使用

使用和google提供的
Behavior
一样,引用完整包名就可以:

app:layout_behavior="com.yanzhenjie.definebehavior.behavior.DefineBehavior"


为了和google提供的
Behavior
使用一样简单,我们可以String.xml中定义一下这个string:

<string name="define_behavior">com.yanzhenjie.definebehavior.behavior.DefineBehavior</string>


用的时候:

app:layout_behavior="@string/define_behavior"


现在我们把原来项目中的
FloatingActionButton
换成
ImageView


<ImageView
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:layout_behavior="@string/define_behavior"
app:layout_scrollFlags="scroll|enterAlways|snap" />


好吧,OK了,具体效果大家下载源码吧:传送门

版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: