5.0之后的Android系统,google推出了自己的移动端设计规范,Material Design,同时sdk包中也加入了support.design包,加入了很多符合md风格的组件。这里主要讲解一下CoordinatorLayout这个布局和它的使用。
什么是CoordinatorLayout
正如它的名字,这个布局是用来协调2个控件之间的联动。我们使用一个图片来说明这个布局的最终效果。
可以发现地下的视图滑动上去之后上面图片所在的区域会被折叠,然后会有一个Toolbar悬停在顶部。
下面我来实现以下这个效果
引入design包里面的组件:
compile 'com.android.support:design:25.3.1'
新建一个Activity和布局文件
布局文件如下:
<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.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="exitUntilCollapsed|scroll|enterAlways"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/head_img"
app:layout_collapseMode="parallax"
android:scaleType="centerCrop"
/>
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
这个布局的分析如下:
最外层使用CoordinatorLayout,顶部的折叠部分使用AppBarLayout,在AppBarLayout中我们加入了一个图片和Toolbar,图片和Toolbar需要包含在一个CollapsingToolbarLayout里面。
下方是一个用来滑动的RecyclerView。
分析一下一些默认属性的作用:
CollapsingToolbarLayout
app:contentScrim 折叠后Toolbar的颜色
app:layout_scrollFlags 滑动折叠的五种效果
- scroll Child View 伴随着滚动事件而滚出或滚进屏幕。注意两点:第一点,如果使用了其他值,必定要使用这个值才能起作用;第二点:如果在这个child View前面的任何其他Child View没有设置这个值,那么这个Child View的设置将失去作用。
- enterAlways 滚动优先级。比较scroll和scroll|enterAlways, 设置该属性后,向下滚动的时候,前者优先滚动可滚动的组件,后者优先滚动child view。
- enterAlwaysCollapsed enterAlways的附加值。这里涉及到Child View的高度和最小高度,向下滚动时,Child View先向下滚动最小高度值,然后Scrolling View开始滚动,到达边界时,Child View再向下滚动,直至显示完全。
- exitUntilCollapsed child view不完全退出屏幕,child view向上滚动停住以后再往上滚动滚动组件。例如折叠后Toolbar不退出屏幕,悬停在上方,然后RecyclerView的视图向上滑动。
- snap child view要么显示要么全部退出屏幕。
child view中的
app:layout_collapseMode 折叠的模式,有三种。其中none表示没有效果。其余2个分别是
- pin 固定模式,折叠后固定在顶端。
- parallax 视差模式,在折叠的时候会有个视差折叠的效果。
####### 可滚动的控件
app:layout_behavior 行为,指定在scroll view上面的。这里我们设置为
app:layout_behavior="@string/appbar_scrolling_view_behavior"
这个指的是AppBarLayout中的内部类ScrollingViewBehavior,这个用来指定RecyclerView和AppBarLayout之间的联动。查看源码
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
// We depend on any AppBarLayouts
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
offsetChildAsNeeded(parent, child, dependency);
return false;
}
这段代码的含义分别是指定滑动行为根据AppBarLayout决定。在onDependentViewChanged方法中我们指定了移动的方式。具体代码在这不赘述。
这个时候我们的基本效果就实现了。可见这个MD控件的强大。当然它的具体实现也是很复杂的。
常见问题
同时我们可以发现Toolbar的行为是根据CollapsingToolbarLayout来控制的。
设置Toolbar的title,会发现效果没有达到预期。title不会随着滑动缩放。
那么问题来了,我们如何设置Toolbar的标题呢?
Toolbar的标题我们可以如下设置:
mCollapsingToolbarLayout.setTitle("标题");
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
现在问题又来了,正常我们不设置的时候,会发现有一个md规范控件自带的效果,标题会在滚动过程中上下移动并且缩放大小的效果。有时候我们并不想把标题显示在下发,这个时候应该怎么办呢?
可以在style文件中设置
<style name="collapsing_toolbar_text_size">
<item name="android:textSize">0sp</item>
</style>
在Activity中设置
mCollapsingToolbarLayout
.setExpandedTitleTextAppearance(R.style.collapsing_toolbar_text_size);
这句代码的含义是设置滑动后的标题字体大小,我们设置为0sp,就不会有任何显示了。
附上最后Activity里面的代码,也可以参考我在github上的demo代码
public class CoordinatorTestActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List<String> data = new ArrayList<>();
private CollapsingToolbarLayout collapsingToolbarLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_coordinator);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
initData();
MyAdapter adapter = new MyAdapter(this,data);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout_1);
collapsingToolbarLayout.setTitle("标题");
collapsingToolbarLayout.setExpandedTitleTextAppearance(R.style.collapsing_toolbar_text_size);
}
private void initData() {
for (int i = 0;i < 20;i++) {
data.add("str"+i);
}
}
class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder> {
private Context mContext;
private List<String> mData;
public MyAdapter(Context context, List<String> data) {
mContext = context;
mData = data;
}
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyHolder(LayoutInflater.from(mContext).inflate(android.R.layout.simple_list_item_1, parent, false));
}
@Override
public void onBindViewHolder(MyHolder holder, int position) {
String text = mData.get(position);
holder.tv.setText(text);
}
@Override
public int getItemCount() {
return mData.size();
}
class MyHolder extends RecyclerView.ViewHolder{
TextView tv;
public MyHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(android.R.id.text1);
}
}
}
}