作为Android的控件, CoordinatorLayout已经加入最新的HelloWorld项目中, 也是Material风格的重要组件, 协调(Coordinate)其他组件, 实现联动. 那么让我们来看看这个动画效果怎么用吧?
本文源码的GitHub下载地址
欢迎Follow我的GitHub: https://github.com/SpikeKing
准备
首先新建HelloWorld项目.
在项目的build.gradle文件中, 引入头像控件库和CardView库, 在本例中会使用.1
2
3compile 'de.hdodenhof:circleimageview:1.3.0'
compile 'com.android.support:cardview-v7:23.1.0'
compile 'com.jakewharton:butterknife:7.0.1'
页面
在activity_main.xml
中, 保留CoordinatorLayout和AppBarLayout, 重新编写页面.
android:fitsSystemWindows="true"
这句也需要去掉, 本例需要保留最上面的状态栏(status bar), 这个属性会导致重叠, 默认false.
activity_main.xml代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<android.support.design.widget.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:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
修改主题颜色, 粉色符合风格1
2<color name="colorPrimary">#FFCCCC</color>
<color name="colorPrimaryDark">#FF00FF</color>
在AppBarLayout中, 添加CollapsingToolbarLayout控件, CollapsingToolbar会滚动消失, 被ToolBar替换, 实现滚动动画.1
2
3
4
5
6<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="450dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
</android.support.design.widget.CollapsingToolbarLayout>
app:layout_scrollFlags
的属性, scroll滑动, exitUntilCollapsed退出到最小, snap自动滑动动画, 定义CollapsingToolbarLayout的滑动属性.
CollapsingToolbarLayout划分两部分, 一部分大图, 一部分文字.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<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="@drawable/large"
app:layout_collapseMode="parallax" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_gravity="bottom|center_horizontal"
android:background="@color/colorPrimary"
app:layout_collapseMode="parallax">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="bottom|center"
android:text="@string/person_name"
android:textColor="@android:color/white"
android:textSize="30sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:text="@string/person_title"
android:textColor="@android:color/white"/>
</LinearLayout>
</FrameLayout>
app:layout_collapseMode="parallax"
折叠时的视差效果, 自动滑动.
效果
添加NestedScrollView, 文字的滚动视图.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
app:behavior_overlapTop="30dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="8dp"
app:contentPadding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="8dp"
android:text="@string/person_intro"/>
</android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>
app:behavior_overlapTop="30dp"
, The amount that the scrolling view should overlap the bottom of any AppBarLayout. 使ScrollView压在AppBarLayout上面一段长度.
添加ToolBar, 滚动结束的效果图.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<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_anchor="@id/main_fl_title"
app:theme="@style/ThemeOverlay.AppCompat.Dark">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<Space
android:layout_width="@dimen/image_final_width"
android:layout_height="@dimen/image_final_width" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/person_name"
android:textColor="@android:color/white"
android:textSize="20sp"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
ToolBar是工具栏, 滑动完成时, 在最上部显示.
Space
预留头像的位置.
圆形头像CircleImageView显示(使用第三方库)1
2
3
4
5
6
7
8<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center"
android:src="@drawable/small"
app:border_color="@android:color/white"
app:border_width="2dp"
app:layout_behavior=".AvatarImageBehavior" />
头像控件的滑动行为是自定义的类, 具体行为参考注释. 判断起始和终止位置, child和dependency视图联动, 这里是图像视图和Toolbar联动.
1 | /** |
添加需要使用的id, activity_main.xml
如下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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.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:fitsSystemWindows="false"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/main_abl_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="450dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<ImageView
android:id="@+id/main_iv_placeholder"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="@drawable/large"
app:layout_collapseMode="parallax" />
<FrameLayout
android:id="@+id/main_fl_title"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_gravity="bottom|center_horizontal"
android:background="@color/colorPrimary"
app:layout_collapseMode="parallax">
<LinearLayout
android:id="@+id/main_ll_title_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:layout_marginTop="@dimen/title_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="bottom|center"
android:text="@string/person_name"
android:textColor="@android:color/white"
android:textSize="30sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:text="@string/person_title"
android:textColor="@android:color/white" />
</LinearLayout>
</FrameLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
app:behavior_overlapTop="30dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="8dp"
app:contentPadding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="8dp"
android:text="@string/person_intro" />
</android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>
<android.support.v7.widget.Toolbar
android:id="@+id/main_tb_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_anchor="@id/main_fl_title"
app:theme="@style/ThemeOverlay.AppCompat.Dark">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<Space
android:layout_width="@dimen/image_final_width"
android:layout_height="@dimen/image_final_width" />
<TextView
android:id="@+id/main_tv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:gravity="center_vertical"
android:text="@string/person_name"
android:textColor="@android:color/white"
android:textSize="20sp"
android:visibility="invisible"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="@dimen/image_width"
android:layout_height="@dimen/image_width"
android:layout_gravity="center"
android:src="@drawable/small"
app:border_color="@android:color/white"
app:border_width="2dp"
app:layout_behavior=".AvatarImageBehavior" />
</android.support.design.widget.CoordinatorLayout>
效果
逻辑
在onCreate里面设置滑动逻辑, 设置两个动画: 监听AppBar的滑动, 处理Toolbar和Title的显示; 自动滑动效果.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mTbToolbar.setTitle("");
// AppBar的监听
mAblAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int maxScroll = appBarLayout.getTotalScrollRange();
float percentage = (float) Math.abs(verticalOffset) / (float) maxScroll;
handleAlphaOnTitle(percentage);
handleToolbarTitleVisibility(percentage);
}
});
initParallaxValues(); // 自动滑动效果
}
根据滑动百分比, 设置Title和Toolbar的显示与消失, 使用Alpha动画.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// 处理ToolBar的显示
private void handleToolbarTitleVisibility(float percentage) {
if (percentage >= PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR) {
if (!mIsTheTitleVisible) {
startAlphaAnimation(mTvToolbarTitle, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
mIsTheTitleVisible = true;
}
} else {
if (mIsTheTitleVisible) {
startAlphaAnimation(mTvToolbarTitle, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
mIsTheTitleVisible = false;
}
}
}
// 控制Title的显示
private void handleAlphaOnTitle(float percentage) {
if (percentage >= PERCENTAGE_TO_HIDE_TITLE_DETAILS) {
if (mIsTheTitleContainerVisible) {
startAlphaAnimation(mLlTitleContainer, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
mIsTheTitleContainerVisible = false;
}
} else {
if (!mIsTheTitleContainerVisible) {
startAlphaAnimation(mLlTitleContainer, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
mIsTheTitleContainerVisible = true;
}
}
}
// 设置渐变的动画
public static void startAlphaAnimation(View v, long duration, int visibility) {
AlphaAnimation alphaAnimation = (visibility == View.VISIBLE)
? new AlphaAnimation(0f, 1f)
: new AlphaAnimation(1f, 0f);
alphaAnimation.setDuration(duration);
alphaAnimation.setFillAfter(true);
v.startAnimation(alphaAnimation);
}
自动滑动动画, 当到一定比例时展开或关闭.1
2
3
4
5
6
7
8
9
10
11
12
13
14// 设置自动滑动的动画效果
private void initParallaxValues() {
CollapsingToolbarLayout.LayoutParams petDetailsLp =
(CollapsingToolbarLayout.LayoutParams) mIvPlaceholder.getLayoutParams();
CollapsingToolbarLayout.LayoutParams petBackgroundLp =
(CollapsingToolbarLayout.LayoutParams) mFlTitleContainer.getLayoutParams();
petDetailsLp.setParallaxMultiplier(0.9f);
petBackgroundLp.setParallaxMultiplier(0.3f);
mIvPlaceholder.setLayoutParams(petDetailsLp);
mFlTitleContainer.setLayoutParams(petBackgroundLp);
}
最终逻辑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
101
102
103
104
105
106
107public class MainActivity extends AppCompatActivity {
// 控制ToolBar的变量
private static final float PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR = 0.9f;
private static final float PERCENTAGE_TO_HIDE_TITLE_DETAILS = 0.3f;
private static final int ALPHA_ANIMATIONS_DURATION = 200;
private boolean mIsTheTitleVisible = false;
private boolean mIsTheTitleContainerVisible = true;
(R.id.main_iv_placeholder)
ImageView mIvPlaceholder; // 大图片
(R.id.main_ll_title_container)
LinearLayout mLlTitleContainer; // Title的LinearLayout
(R.id.main_fl_title)
FrameLayout mFlTitleContainer; // Title的FrameLayout
(R.id.main_abl_app_bar)
AppBarLayout mAblAppBar; // 整个可以滑动的AppBar
(R.id.main_tv_toolbar_title)
TextView mTvToolbarTitle; // 标题栏Title
(R.id.main_tb_toolbar)
Toolbar mTbToolbar; // 工具栏
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mTbToolbar.setTitle("");
// AppBar的监听
mAblAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int maxScroll = appBarLayout.getTotalScrollRange();
float percentage = (float) Math.abs(verticalOffset) / (float) maxScroll;
handleAlphaOnTitle(percentage);
handleToolbarTitleVisibility(percentage);
}
});
initParallaxValues(); // 自动滑动效果
}
// 设置自动滑动的动画效果
private void initParallaxValues() {
CollapsingToolbarLayout.LayoutParams petDetailsLp =
(CollapsingToolbarLayout.LayoutParams) mIvPlaceholder.getLayoutParams();
CollapsingToolbarLayout.LayoutParams petBackgroundLp =
(CollapsingToolbarLayout.LayoutParams) mFlTitleContainer.getLayoutParams();
petDetailsLp.setParallaxMultiplier(0.9f);
petBackgroundLp.setParallaxMultiplier(0.3f);
mIvPlaceholder.setLayoutParams(petDetailsLp);
mFlTitleContainer.setLayoutParams(petBackgroundLp);
}
// 处理ToolBar的显示
private void handleToolbarTitleVisibility(float percentage) {
if (percentage >= PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR) {
if (!mIsTheTitleVisible) {
startAlphaAnimation(mTvToolbarTitle, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
mIsTheTitleVisible = true;
}
} else {
if (mIsTheTitleVisible) {
startAlphaAnimation(mTvToolbarTitle, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
mIsTheTitleVisible = false;
}
}
}
// 控制Title的显示
private void handleAlphaOnTitle(float percentage) {
if (percentage >= PERCENTAGE_TO_HIDE_TITLE_DETAILS) {
if (mIsTheTitleContainerVisible) {
startAlphaAnimation(mLlTitleContainer, ALPHA_ANIMATIONS_DURATION, View.INVISIBLE);
mIsTheTitleContainerVisible = false;
}
} else {
if (!mIsTheTitleContainerVisible) {
startAlphaAnimation(mLlTitleContainer, ALPHA_ANIMATIONS_DURATION, View.VISIBLE);
mIsTheTitleContainerVisible = true;
}
}
}
// 设置渐变的动画
public static void startAlphaAnimation(View v, long duration, int visibility) {
AlphaAnimation alphaAnimation = (visibility == View.VISIBLE)
? new AlphaAnimation(0f, 1f)
: new AlphaAnimation(1f, 0f);
alphaAnimation.setDuration(duration);
alphaAnimation.setFillAfter(true);
v.startAnimation(alphaAnimation);
}
}
这样联动效果就已经显示出来了, 在AppBar中Toolbar和Title之间的关系, 符合Material的风格, 给用户更多的体验. 既然已经集成到HelloWorld中, 说明Android是极力推荐使用CoordinatorLayout这种风格的.
OK, that’s all! Enjot it!
原始地址:
http://www.wangchenlong.org/2016/03/22/1603/228-coordinator-layout-first/
欢迎Follow我的GitHub, 关注我的简书, 微博, CSDN, 掘金.
我已委托“维权骑士”为我的文章进行维权行动. 未经授权, 禁止转载, 授权或合作请留言.