前言
最近接到一个需求,这需求让我表示很尴尬。(下面是一些废话)
要求的效果是这样的,顶部有部分悬浮,接着是一些布局,在下面是几个可切换的Tab
页面,然后滚动的时候~~吧啦吧啦吧啦吧啦~~ 还是直接看图吧
主要就是顶部和
Tab
的悬浮,还有就是被顶掉的那个效果。
听到要实现这样的效果,我抽屉那把砍产品专用菜刀已经蠢蠢欲动了。
思路
先说说实现的思路吧,上面的效果大致可以分成两个部分:
- 1、
Tab
向上滚动到顶部时悬浮
Tab
滚动后悬浮在顶部嘛~~ 这效果使用CoordinatorLayout
+AppBarLayout
就能轻松实现。(什么?你还不懂这两个控件怎么使用?额,应该可以勉强看懂后面的内容) - 2、顶部悬浮以及“被顶走”的效果
只要在CoordinatorLayout
外面套一层FrameLayout
,然后把这个顶部的布局改在上面。接着监听AppBarLayout
的滚动,利用topMargin
实现被“顶上去”的效果
拆分完毕,接下来就是实现了
实现
-
Tab的悬浮效果
利用CoordinatorLayout
、AppBarLayout
、TabLayout
、ViewPager
来实现Tab
的悬浮效果
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="260dp"
android:scaleType="centerCrop"
android:src="@drawable/bg_header"/>
</LinearLayout>
<android.support.design.widget.TabLayout
android:id="@+id/table_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#FBDD9C"
app:tabGravity="fill"
app:tabIndicatorColor="#5f00"
app:tabIndicatorHeight="4dp"
app:tabMode="fixed"
app:tabSelectedTextColor="#FFFFFF"
app:tabTextColor="#FFFFFF"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
-
LinearLayout
中设置app:layout_scrollFlags="scroll|exitUntilCollapsed"
而TabLayout
不设置app:layout_scrollFlags
属性 -
ViewPager
中使用app:layout_behavior="@string/appbar_scrolling_view_behavior"
layout_scrollFlags:AppBarLayout供Children View使用的属性,一共有五种值:scroll,enterAlways,enterAlwaysCollapsed,snap,exitUntilCollapsed。具体的使用可以参考Android 详细分析AppBarLayout的五种ScrollFlags
(CoordinatorLayout、AppBarLayout的详细用法我就不多说了)
然后,只要在Java代码中为ViewPager
添加几个列表Fragment
就能看到以下的效果(注意:列表不可以是ListView
,需要用RecyclerView
)
到目前为止,效果已经实现了大半。最后值需要实现“被顶走”的效果就好了。
-
顶部“被顶走”的效果
这时候,布局稍微改变下。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<View
android:layout_width="match_parent"
android:layout_height="50dp"/>
<ImageView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="260dp"
android:scaleType="centerCrop"
android:src="@drawable/bg_header"/>
</LinearLayout>
<android.support.design.widget.TabLayout
android:id="@+id/table_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#FBDD9C"
app:tabGravity="fill"
app:tabIndicatorColor="#5f00"
app:tabIndicatorHeight="4dp"
app:tabMode="fixed"
app:tabSelectedTextColor="#FFFFFF"
app:tabTextColor="#FFFFFF"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
<TextView
android:id="@+id/sticky_view"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textColor="#FFFFFF"
android:text="除了悬浮 我还会动"
android:gravity="center"
android:textSize="16sp"
android:background="#39b9e9"/>
</FrameLayout>
在原有的基础上,套了个FrameLayout
,顶部的悬浮部分可以通过FrameLayout
来实现。这样也导致下面的布局被盖住了一部分,因此在LinearLayout
中加了与悬浮部分相同高度的空View
。
布局是完成了,那个“被顶走”的效果怎么实现呢?这时候只要在MainActivity
中对AppBarLayout
的滚动进行监听即可。
@BindView(R.id.app_bar)
AppBarLayout mAppBar;
@BindView(R.id.sticky_view)
View mStickyView;
@BindView(R.id.header)
View mHeader;
private void setAppBarListener() {
mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
//头部高度(除去被顶部覆盖的部分)
int minScrollHeight = mHeader.getMeasuredHeight();
int margin = minScrollHeight + verticalOffset;
margin = margin > 0 ? 0 : margin;
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mStickyView.getLayoutParams();
layoutParams.topMargin = margin;
mStickyView.setLayoutParams(layoutParams);
}
});
}
这里通过AppBarLayout
滚动的进行监听(向上滚动时,verticalOffset
值的变化为:0、-1 、-2 ... -n-1、-n)来计算margin
值。通过改变topMargin,实现“被顶走”的效果。
再看一眼效果:
敲到这里,我才默默的收起了那把砍产品专用菜刀。
Tips
- 问题:使用CoordinatorLayout时,滚动不流畅问题
解决方案:可以写个Behavior添加到AppBarLayout中。具体的解决方案 - 问题:如果你在想要刷新功能,在CoordinatorLayout外面套了一个SwipeRefreshLayout,一不小心就触发了刷新~~(自己体会)
解决方案:这个问题可以通过对AppBarLayout的监听,设置swipeLayout的Enabled来处理
mAblAppBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
if (verticalOffset == 0) {
swipeLayout.setEnabled(true);
} else {
if (!swipeLayout.isRefreshing()) {
swipeLayout.setEnabled(false);
}
}
});
源码地址
以上有错误之处,感谢指出