最终效果:
主要用到三个组件:CoordinatorLayout,AppBarLayout,CollapsingToolbarLayout
协调者布局,Google将其解释为一个超级FrameLayout:
CoordinatorLayout is a super-powered FrameLayout.
CoordinatorLayout是支持包”com.Android.support:design”里很重要的一个控件,继承于FrameLayout。
它有两种使用场景:
1.作为 一个应用顶层的装饰布局,也就是一个Activity Layout 的最外一层布局。
2.作为一个或多个有特定响应动作的容器,协调子控件的相互作用。
CoordinatorLayout存在的意义就是通过自定义Children的Behaviors(行为)来实现控件之间的交互动画效果,我们在使用CoordinatorLayout时,也可以自定义Behavior来实现一些复杂的交互效果,Google 也给我们定义了一些常用的Behavior,如后面要用的到的 appbar_scrolling_view_behavior ,用于协调 AppBarLayout 与 ScrollView 滑动的Behavior。
这里首先提出一个概念, CoordinatorLayout 其实是将其下的所有子View都抽象成:
互相依赖(depends)的关系.
CoordinatorLayout的使用核心是Behavior,Behavior就是执行你定制的动作。在讲Behavior之前必须先理解两个概念:Child和Dependency。
Child是指要执行动作的CoordinatorLayout的子View。而Dependency是指Child依赖的View。
因此某个view可以基于另一个view来定位, 但这只是冰山一角, 这样抽象的好处更强大的地方在于:每一个view的所有属性, 坐标, 样式, 状态等一切都可以依赖于另一个view, 因此使得parentView和所有childView之间都可以互相联动起来.
举个栗子:
可以看出这里的关键在于 layout_anchor 和 layout_anchorGravity 两个参数, 意思也很简单:
就是这个View以指定的 anchor 作为参照物来定位, anchorGravity 设置为 bottom|right 则表示将FloatingActionButton放置于参照物(FrameLayout)的右下角.由此实现了view之间的联动。
AppBarLayout是一种支持响应滚动手势的app bar布局,继承自LinearLayout,布局方向为垂直方向。
AppBarLayout是在LinearLayou上加了一些材料设计的概念,它可以让你定制当某个可滚动View的滚动手势发生变化时,其内部的子View实现何种动作。
我们可以通过设置AppBarLayout子控件的layout_scrollFlags参数,来控制AppBarLayout中控件的行为。
layout_scrollFlags参数
给ViewPager设置行为,实现与AppBarLayout联动。
app:layout_behavior=“@string/appbar_scrolling_view_behavior”
举个栗子:
在AppBarLayout的子控件toolbar中设置滑动属性layout_scrollFlags=“scroll”,toolbar会跟随滑动事件一起发生移动。
CollapsingToolbarLayout是用来对Toolbar进行再次包装的ViewGroup,主要是用于实现折叠的App Bar效果,是专门用来实现子布局内不同元素响应滚动细节的布局。
它需要放在AppBarLayout布局里面,并且作为AppBarLayout的直接子View。CollapsingToolbarLayout主要包括几个功能:
(1) 折叠Title(Collapsing title):当布局内容全部显示出来时,title是最大的,但是随着View逐步移出屏幕顶部,title变得越来越小。你可以通过调用setTitle函数来设置title。
(2)内容纱布(Content scrim):根据滚动的位置是否到达一个阀值,来决定是否对View“盖上纱布”。可以通过setContentScrim(Drawable)来设置纱布的图片。
(3)状态栏纱布(Status bar scrim):根据滚动位置是否到达一个阀值决定是否对状态栏“盖上纱布”,你可以通过setStatusBarScrim(Drawable)来设置纱布图片,但是只能在LOLLIPOP设备上面有作用。
(4)视差滚动子View(Parallax scrolling children):子View可以选择在当前的布局当时是否以“视差”的方式来跟随滚动。将布局参数app:layout_collapseMode设为parallax。
(5)将子View位置固定(Pinned position children):子View可以选择是否在全局空间上固定位置,这对于Toolbar来说非常有用,因为当布局在移动时,可以将Toolbar固定位置而不受移动的影响。 将app:layout_collapseMode设为pin。
CollapsingToolbarLayout可以
通过app:contentScrim设置折叠时工具栏布局的颜色。
通过app:statusBarScrim设置折叠时状态栏的颜色。
默认contentScrim是colorPrimary的色值,statusBarScrim是colorPrimaryDark的色值。
CollapsingToolbarLayout的子布局有3种折叠模式(Toolbar中设置的app:layout_collapseMode)
当CollapsingToolbarLayout的子布局设置了parallax模式时,我们还可以通过app:layout_collapseParallaxMultiplier设置视差滚动因子,值为:0~1。
举个栗子:
在此栗子中,AppBarLayout的直属子view中layout_scrollflags设置为scroll | exitUtilCollapsed,表示collapsingtoolbarlayout有联动滑动效果且滑动minHeight的距离,再看其子view,Imageview设置了layout_collapsemode为parallax,则实现视差折叠效果,而toolbar因设置了layout_collapsemode为pin,会固定在顶部。
还有一些常用的属性及其含义
|app:title|设置标题|
|app:collapsedTitleGravity="center"|设置标题位置|
|app:contentScrim |设置折叠时toolbar的颜色,默认是colorPrimary的色值|
|app:statusBarScrim | 设置折叠时状态栏的颜色 ,默认是colorPrimaryDark的色值|
|app:layout_collapseParallaxMultiplier|设置视差|
|app:layout_collapseMode="parallax"| 视差模式,在折叠的时候会有个视差折叠的效果|
|app:layout_collapseMode="pin"|固定模式,在折叠的时候最后固定在顶端 |
当然也可以在java中设置。
到这里这三个重要组件的主要功能就总结完成了,接下来我们来看看这次要做的仿知乎个人中心页面。
这是我们最终要实现的效果,其中几个重要的部分:
沉浸式statusbar,可滑动折叠imageview,toolbar。
Viewpager,tablayout,fragment和recyclerview的结合。
recyclerview和appbarlayout的联动。
tablayout上滑到toolbar与toolbar合并。
1.沉浸式statusbar第一篇已经提过,这里也依然使用,略。
2.可滑动折叠的imageview和toolbar就要用到我们讲过的三个组件了。
思路:
思路有了,布局也就写出来了。
。。。(不重要先省略)
。。。(不重要先省略)
配合java中toolbar等的设置,这一步就完成了。
由于viewpager中要嵌套fragment,故我们使用FragmentPagerAdapter,整体还是比较容易的。
自定义FragmentPagerAdapter:
public class ZhiHuFragmentAdapter extends FragmentPagerAdapter {
private FragmentManager fragmentManager;
private List fragmentList;
private String[] titles;
public ZhiHuFragmentAdapter(FragmentManager fm, List list, String[] titles) {
super(fm);
this.fragmentManager = fm;
this.fragmentList = list;
this.titles = titles;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
标题我们用tablayout来实现,那么tablayout和viewpager怎么实现联动呢:
mTabLayout.setupWithViewPager(mViewpager);
并且在FragmentPagerAdapter中要重写getpagetitle方法,titles是你的标题数据:
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
并在activity通过初始化fragmentadapter将需要的参数传进去(这里我为了简便,将同一个fragment放入fragmentlist中),并设置viewpager的adapter为fragmentadapter:
private String[] titles = {"动态", "回答", "文章", "想法"};
fragmentList = new ArrayList<>();
for (int i = 0; i < titles.length; i++) {
ListFragment fragment = new ListFragment();
fragmentList.add(fragment);
}
ZhiHuFragmentAdapter adapter = new ZhiHuFragmentAdapter(
getSupportFragmentManager(), fragmentList, titles);
mViewpager.setAdapter(adapter);
fragment中嵌套recyclerview,在自定义fragment的oncreateView和onActivityCreated方法中初始化recyclerview和其adapter(比较容易):
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, null);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
return view;
}
@SuppressLint("ResourceAsColor")
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TextAdapter adapter = new TextAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
recyclerView.setNestedScrollingEnabled(false);
}
自定义recyclerview的adapter(比较容易):
public class TextAdapter extends RecyclerView.Adapter {
@Override
public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(getContext()).inflate
(R.layout.item_recyclerview, parent, false);
return new TextViewHolder(view);
}
@Override
public void onBindViewHolder(TextViewHolder holder, int position) {
holder.mTextView.setText(position + "");
}
@Override
public int getItemCount() {
return 6;
}
};
public class TextViewHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
public TextViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.text_recycler_view);
}
}
此Listfragment在for循环中产生实例加入fragmentlist中即可。
完成。
当toolbar,imagerview的滑动和折叠效果完成后,在imageview处滑动会产生效果,但是由于我们下部分布局也是滑动的view(recyclerview),所以为了用户体验,我们希望在下部分发生滑动手势时,toolbar和imageview部分也能产生滑动效果,那怎么实现呢?就用appbarlayout的layout_behavior属性来实现两个view的联动。
我们这里就用有着嵌套滑动机制的NestedScrollView,(嵌套联动:https://www.jianshu.com/p/1806ed9737f6)并设置
app:layout_behavior=“@string/appbar_scrolling_view_behavior”
联动完成,并将我们的textview布局和viewpager嵌套在NestedScrollView中,注意,因为NestedScrollView只能有一个子布局,所以textview和viewpager需要用relativelayout或linearlayout等布局嵌套一下。
布局如下:(里面一些非重点的属性已经省略,加入scrollview是为了滑动更快)
这步当时思路:
1.用CoordinatorLayout,AppBarLayout,CollapsingToolbarLayout三个组件来完成。首先,我们要完成的效果是tablayout上滑到toolbar时与toolbar融合,下滑经过toolbar后再分开。这三个组件能够实现这样的效果吗?其次,我们要完成的效果并不是toolbar下面就是tablayout,而是toolbar下方有一些textview内容,其次下方是tablayout,那么这时候就有一个问题,就是appbarlayout和collapsingtoolbarlayout究竟能不能在两个不相邻的子控件上都产生效果?经过我的各种尝试,没有成功(也可能是能力不够,更深层次的自定义和各种属性不熟悉),所以我放弃了这种思路。
2.第二种思路,当tablayout上滑到toolbar时,toolbar原本下方有的另一个tablayout控件显示出来,当下滑时,再让其消失。这种方式还是可行的,可通过NestedScrollView的滑动监听来监听tablayout的坐标位置和toolbar底部的坐标位置的关系,判断(新)tablayout的显示和消失。
NestedScrollView的监听事件onScrollChangeListener()
Tablayout获取xy坐标,tablayout.getX(),getY()
当tablayout的y坐标小于toolbar底部y坐标,则让新tablayout显示,否则消失。
代码如下:
mScrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View view, int i, int i1, int i2, int i3) {
int[] location = new int[2];
mTabLayout.getLocationInWindow(location);
mTabLayout.getLocationOnScreen(location);
if (location[1] < Utils.dp2px(MainActivity.this, 88)) {
mTabLayoutCopy.setVisibility(View.VISIBLE);
mTabLayout.setVisibility(View.INVISIBLE);
} else {
mTabLayoutCopy.setVisibility(View.GONE);
mTabLayout.setVisibility(View.VISIBLE);
}
}
测试时候发现下滑时顶部tablayout发生抖动,下滑失败,并且进入activity时recyclerview的布局充满屏幕,布局时从中间开始显示。
代码如下:
@Override
protected void onResume() {
super.onResume();
mRelativeLayout.setFocusable(true);
mRelativeLayout.setFocusableInTouchMode(true);
mRelativeLayout.requestFocus();
}
完整代码地址:http://download.csdn.net/download/denglixuan1996/10265021