使用CoordinatorLayout打造各种炫酷的效果
自定义Behavior —— 仿知乎,FloatActionButton隐藏与展示
前段时间写了一篇博客使用CoordinatorLayout打造各种炫酷的效果,主要介绍了APPBarLayout和CollapsingToolbarLayout的基本用法,AppBarLayout主要用来实现在滚动的时候ToolBar的 隐藏于展示,CollapsingToolbarLayout主要用来实现parallax和pin等效果。如果你对CoordinatorLayout还不了解的话,请先阅读这篇文章。
写作思路
- CoordinatorLayout Behavior 简介
- 怎样自定义 Behavior
- 仿知乎效果 自定义 Behavior 的实现
- 自定义 Behavior 两种方法的 对比
- FloatActionButton 自定义 Behavior 效果的实现
- 题外话
今天就来讲解怎样通过自定义behavior来实现各种炫酷的效果 ,效果图如下
下面让我们一起来看一下怎样实现仿知乎的效果
老规矩,先看代码
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.design.widget.AppBarLayout
android:id="@+id/index_app_bar"
theme="@style/AppTheme.AppBarOverlay"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways">
<ImageView
android:id="@+id/search"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:src="@drawable/search"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/search"
android:text="搜索话题、问题或人"
android:textSize="16sp"/>
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
<!--使用RadioGroup来实现tab的切换-->
<RadioGroup
android:id="@+id/rg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/bg_tab"
android:orientation="horizontal"
app:layout_behavior="@string/behavior_footer"
>
<RadioButton
android:id="@+id/rb_home"
style="@style/bottom_tab"
android:drawableTop="@drawable/sel_home"
android:text="Home"/>
<RadioButton
android:id="@+id/rb_course"
style="@style/bottom_tab"
android:drawableTop="@drawable/sel_course"
android:text="course"/>
<RadioButton
android:id="@+id/rb_direct_seeding"
style="@style/bottom_tab"
android:drawableTop="@drawable/sel_direct_seeding"
android:text="direct"/>
<RadioButton
android:id="@+id/rb_me"
style="@style/bottom_tab"
android:drawableTop="@drawable/sel_me"
android:text="me"/>
</RadioGroup>
</android.support.design.widget.CoordinatorLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
<style name="bottom_tab">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">60dp</item>
<item name="android:layout_weight">1</item>
<item name="android:text">0dp</item>
<item name="android:gravity">center</item>
<item name="android:textColor">@drawable/sel_bottom_tab_text</item>
<item name="android:padding">10dp</item>
<item name="android:button">@null</item>
</style>
<style name="bottom_tab">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">60dp</item>
<item name="android:layout_weight">1</item>
<item name="android:text">0dp</item>
<item name="android:gravity">center</item>
<item name="android:textColor">@drawable/sel_bottom_tab_text</item>
<item name="android:padding">10dp</item>
<item name="android:button">@null</item>
</style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
思路分析
根据动态如可以看到,主要有两个效果
- 上面的AppBarLayout 向上滑动的时候会隐藏,向下滑动的时候会展示,说白了就是给APPLayout的子View Relativelayout 设置 app:layout_scrollFlags=”scroll|enterAlways”,核心代码如下
<android.support.design.widget.AppBarLayout
android:id="@+id/index_app_bar"
theme="@style/AppTheme.AppBarOverlay"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways">
----
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 下面的 RadioGroup ,我们可以看到,向上 滑动的时候会隐藏,向下滑动的时候会显示,其实我们只是给其设置了 behavior 而已 app:layout_behavior=”@string/behavior_footer”,那这个behavior_footer是什么东西,别急 ,下面就是介绍了
<string name="behavior_footer">com.xujun.contralayout.behavior.FooterBehavior</string>
Behavior简介
Behavior是CoordinatorLayout里面的一个内部类,通过它我们可以与 CoordinatorLayout的一个或者多个子View进行交互,包括 drag,swipes, flings等手势动作。
今天 我们主要着重介绍里面的几个方法
方法 | 解释 |
---|
boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) | 确定child View 是否有一个特定的兄弟View作为布局的依赖(即dependency) |
boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) | 当child View 的 dependent view 发生变化的时候,这个方法会调用 |
boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) | 当CoordinatorLayout 的直接或者非直接子View开始准备嵌套滑动的时候会调用 |
void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) | 当嵌套滑动的 时候,target尝试滑动或者正在滑动的 时候会调用 |
关于更多方法,请参考官网文档说明
怎样自定义Behavior
前面已经说到,今天主要介绍四个方法,这里我们把它分为两组。
第一组
boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency)
boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency)
第二组
boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes)
onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)
首先我们先看第一组是怎样实现的?
/**
* 知乎效果底部behavior 依赖于 AppBarLayout
*
* @author xujun on 2016/11/30.
* @email gdutxiaoxu@163.com
*/
public class FooterBehaviorDependAppBar extends CoordinatorLayout.Behavior<View> {
public static final String TAG = "xujun";
public FooterBehaviorDependAppBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.abs(dependency.getTop());
child.setTranslationY(translationY);
Log.i(TAG, "onDependentViewChanged: " + translationY);
return true;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
思路分析
这里我们要分清两个概念,child 和 dependency ,child 是我们要改变的坐标的view,而 dependency 是child 的 附属 ,即child 会随着 dependency 坐标的改变而改变。
比如上面的例子:当我们把 app:layout_behavior=”com.xujun.contralayout.behavior.FooterBehaviorDependAppBar” 设置给 RadioGroup 的时候,这时候 child 就是 RadioGroup ,而 dependency 就是 APPBarLayout ,因为我们在 layoutDependsOn 方法里面 ,返回 dependency instanceof AppBarLayout ,即当 dependency 是 AppBarLayout 或者 AppBarLayout的子类的时候返回TRUE。
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
而之所以 RadioGroup 在向上滑动的时候会隐藏,向下滑动的时候会显示,是因为我们在 onDependentViewChanged 方法的时候 动态地根据 dependency 的 top 值改变 RadioGroup 的 translationY 值,核心 代码如下
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.abs(dependency.getTop());
child.setTranslationY(translationY);
Log.i(TAG, "onDependentViewChanged: " + translationY);
return true;
}
到此第一种思路分析为止
第二种思路
主要是根据 onStartNestedScroll() 和 onNestedPreScroll()方法 来实现的,
- 当我们开始滑动的时候,我们判断是否是垂直滑动,如果是返回TRUE,否则返回 FALSE,返回TRUE,会接着调用onNestedPreScroll()等一系列方法。
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,
View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
- 在 onNestedPreScroll() 方法里面,我们根据我们的逻辑来决定是否显示 target , 在这里我们是向上上滑动的时候,如果我们滑动的距离超过 target 的高度 并且 当前是可见的状态下,我们执行动画,隐藏 target,当我们向下滑动的时候,并且 View 是不可见的情况下,我们执行动画 ,显示target
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child,
View target, int dx, int dy, int[] consumed) {
if (dy > 0 && sinceDirectionChange < 0 || dy < 0 && sinceDirectionChange > 0) {
child.animate().cancel();
sinceDirectionChange = 0;
}
sinceDirectionChange += dy;
int visibility = child.getVisibility();
if (sinceDirectionChange > child.getHeight() && visibility == View.VISIBLE) {
hide(child);
} else {
if (sinceDirectionChange < 0 && (visibility == View.GONE || visibility == View
.INVISIBLE)) {
show(child);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
全部代码如下
/**
* 知乎效果底部 behavior
*
* @author xujun on 2016/11/30.
* @email gdutxiaoxu@163.com
*/
public class FooterBehavior extends CoordinatorLayout.Behavior<View> {
private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
private int sinceDirectionChange;
public FooterBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,
View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child,
View target, int dx, int dy, int[] consumed) {
if (dy > 0 && sinceDirectionChange < 0 || dy < 0 && sinceDirectionChange > 0) {
child.animate().cancel();
sinceDirectionChange = 0;
}
sinceDirectionChange += dy;
int visibility = child.getVisibility();
if (sinceDirectionChange > child.getHeight() && visibility == View.VISIBLE) {
hide(child);
} else {
if (sinceDirectionChange < 0 && (visibility == View.GONE || visibility == View
.INVISIBLE)) {
show(child);
}
}
}
private void hide(final View view) {
ViewPropertyAnimator animator = view.animate().translationY(view.getHeight()).
setInterpolator(INTERPOLATOR).setDuration(200);
animator.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
view.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animator) {
show(view);
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animator.start();
}
private void show(final View view) {
ViewPropertyAnimator animator = view.animate().translationY(0).
setInterpolator(INTERPOLATOR).
setDuration(200);
animator.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
view.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationCancel(Animator animator) {
hide(view);
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animator.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
两种实现方法的对比和总结
-
我们知道第一种方法我们主要是重写layoutDependsOn 和 onDependentViewChanged 这两个方法,这个方法在 layoutDependsOn 判断 dependency 是否是 APpBarLayout 的实现类,所以 会导致 child 依赖于 AppBarLayout,灵活性不是太强
-
而第二种方法,我们主要是重写 onStartNestedScroll 和 onNestedPreScroll 这两个方法,判断是否是垂直滑动,是的话就进行处理,灵活性大大增强,推荐使用这一种方法
-
需要注意的是不管是第一种方法,还是第二种方法,我们都需要重写带两个构造方法的函数,因为底层机制会采用反射的形式获得该对象
public FooterBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
效果图如下
缩放隐藏的
向上向下隐藏的
布局代码
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/activity_floating_action_button"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.xujun.contralayout.UI.FloatingActionButtonActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/index_app_bar"
theme="@style/AppTheme.AppBarOverlay"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways">
<ImageView
android:id="@+id/search"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:src="@drawable/search"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/search"
android:text="搜索话题、问题或人"
android:textSize="16sp"/>
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right|end"
android:layout_marginBottom="40dp"
android:layout_marginRight="25dp"
android:background="@android:color/holo_green_light"
android:src="@drawable/add"
app:layout_behavior="@string/behavior_my_fab_scale"/>
</android.support.design.widget.CoordinatorLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
如果想使用不同的效果,只需要给 FloatingActionButton 制定不同的 bevaior 即可
app:layout_behavior="com.xujun.contralayout.behavior.MyFabBehavior"
自定义behavior 代码
public class MyFabBehavior extends CoordinatorLayout.Behavior<View> {
private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
private float viewY;
private boolean isAnimate;
public MyFabBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
if(child.getVisibility() == View.VISIBLE&&viewY==0){
viewY=coordinatorLayout.getHeight()-child.getY();
}
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
if (dy >=0&&!isAnimate&&child.getVisibility()==View.VISIBLE) {
hide(child);
} else if (dy <0&&!isAnimate&&child.getVisibility()==View.GONE) {
show(child);
}
}
private void hide(final View view) {
ViewPropertyAnimator animator = view.animate().translationY(viewY).setInterpolator(INTERPOLATOR).setDuration(200);
animator.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
isAnimate=true;
}
@Override
public void onAnimationEnd(Animator animator) {
view.setVisibility(View.GONE);
isAnimate=false;
}
@Override
public void onAnimationCancel(Animator animator) {
show(view);
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animator.start();
}
private void show(final View view) {
ViewPropertyAnimator animator = view.animate().translationY(0).setInterpolator(INTERPOLATOR).setDuration(200);
animator.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
view.setVisibility(View.VISIBLE);
isAnimate=true;
}
@Override
public void onAnimationEnd(Animator animator) {
isAnimate=false;
}
@Override
public void onAnimationCancel(Animator animator) {
hide(view);
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animator.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
/**
* <p>下拉时显示FAB,上拉隐藏,留出更多位置给用户。</p>
* Created on 2016/12/1.
*
* @author xujun
*/
public class ScaleDownShowBehavior extends FloatingActionButton.Behavior {
/**
* 退出动画是否正在执行。
*/
private boolean isAnimatingOut = false;
private OnStateChangedListener mOnStateChangedListener;
public ScaleDownShowBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimatingOut && child.getVisibility() == View.VISIBLE) {
AnimatorUtil.scaleHide(child, viewPropertyAnimatorListener);
if (mOnStateChangedListener != null) {
mOnStateChangedListener.onChanged(false);
}
} else if ((dyConsumed < 0 || dyUnconsumed < 0) && child.getVisibility() != View.VISIBLE) {
AnimatorUtil.scaleShow(child, null);
if (mOnStateChangedListener != null) {
mOnStateChangedListener.onChanged(true);
}
}
}
public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) {
this.mOnStateChangedListener = mOnStateChangedListener;
}
public interface OnStateChangedListener {
void onChanged(boolean isShow);
}
public static <V extends View> ScaleDownShowBehavior from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
}
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();
if (!(behavior instanceof ScaleDownShowBehavior)) {
throw new IllegalArgumentException("The view is not associated with ScaleDownShowBehavior");
}
return (ScaleDownShowBehavior) behavior;
}
private ViewPropertyAnimatorListener viewPropertyAnimatorListener = new ViewPropertyAnimatorListener() {
@Override
public void onAnimationStart(View view) {
isAnimatingOut = true;
}
@Override
public void onAnimationEnd(View view) {
isAnimatingOut = false;
view.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(View arg0) {
isAnimatingOut = false;
}
};
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
思路这里就不详细展开了,因为前面在讲解 仿知乎效果的时候已经讲过了,大概就是根据不同的滑动行为执行不同的动画 而已
题外话
- 通过这篇博客,熟悉 CoordinatorLayout 的 各种用法,同时也初步理解了自定义Behavior的思路
- 同时复习了动画的相关知识
- 如果你觉得效果还不错,欢迎到我的github上面star,github地址
文章首发地址CSDN:http://blog.csdn.net/gdutxiaoxu/article/details/53453958
源码下载地址:https://github.com/gdutxiaoxu/CoordinatorLayoutExample.git