概述
Google官方對(duì)它的概述如下:
AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
material designs app bar concept, namely scrolling gestures.
Children should provide their desired scrolling behavior through
{@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
{@code app:layout_scrollFlags}.
This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
not work.
AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.
The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you
should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}.
A string resource containing the full class name is available.
大概的意思也就是說(shuō):
AppBarLayout是一個(gè)垂直的{@link LinearLayout},它實(shí)現(xiàn)了material designs app bar概念的許多特性,換句話說(shuō)就是實(shí)現(xiàn)了material designs 滾動(dòng)技術(shù)。
AppBarLayout的子View應(yīng)該通過(guò){@link LayoutParams#setScrollFlags(int)}或者相關(guān)的布局xml屬性:{@code app:layout_scrollFlags}提供他們想要的滾動(dòng)行為。
此視圖在很大程度上取決于在{@link CoordinatorLayout}中用作直接子級(jí)。如果你在不同的{@link ViewGroup}中使用AppBarLayout,它的大部分功能將會(huì)不行。
AppBarLayout也需要一個(gè)滾動(dòng)的兄弟View,以便知道什么時(shí)候滾動(dòng)。其與兄弟View的綁定是通過(guò){@link ScrollingViewBehavior}行為類來(lái)完成的,這意味著你應(yīng)該為滾動(dòng)的兄弟View的行為設(shè)置為{@linkScrollingViewBehavior}的實(shí)例。
由上面Google對(duì)于AppBarLayout的概述可知,AppBarLayout核心就是:
- 實(shí)現(xiàn)了material designs 滾動(dòng)技術(shù)
- 最好作為CoordinatorLayou的直接子View使用
- 要擁有一個(gè)可滾動(dòng)的兄弟View并且通過(guò)為可滾動(dòng)的兄弟View設(shè)置ScrollingViewBehavior實(shí)例來(lái)實(shí)現(xiàn)綁定。
material designs 滾動(dòng)技術(shù)
滾動(dòng)技術(shù)影響內(nèi)容相對(duì)于應(yīng)用欄滾動(dòng)的方式。
以下這些模式描述了內(nèi)容滾動(dòng)時(shí)的高度(即垂直于手機(jī)屏幕方向上的偏移量),如何確定靈活空間的大小,以及何時(shí)固定特定元素。
App bar 可滾動(dòng)的區(qū)域
Status bar、Toolbar、Tab bar/search bar 和 Flexible space
App bar 可滾動(dòng)的區(qū)域
當(dāng)設(shè)計(jì)滾動(dòng)行為時(shí),App bar包含構(gòu)成滾動(dòng)結(jié)構(gòu)的四個(gè)主要區(qū)域(稱為塊):
- Status bar
- Tool bar
- Tab bar/search bar
- Flexible space: 用來(lái)容納圖像或者擴(kuò)展app bar的期望寬高比


Behavior
1. Standard app bar
規(guī)格:
The standard app bar在移動(dòng)設(shè)備上的高度為56 dp,在較大屏幕尺寸上為64 dp。
The app bar 有兩種滾動(dòng)選項(xiàng):
- The app bar可以滾動(dòng)離開(kāi)屏幕用來(lái)顯示內(nèi)容,并在用戶反向滾動(dòng)時(shí)返回。
- The app bar可以保持固定在頂部,內(nèi)容在它下面滾動(dòng)。

The standard app bar
Status bar height: 24dp
Toolbar height: 56dp / 64dp

2. App bar with tabs
Tabs 可以具有以下行為之一:
- 在 the toolbar滾出過(guò)程中The tab bar保持固定在頂部。
- The app bar始終位于頂部,內(nèi)容在下方滾動(dòng)。
- the toolbar和 tab bar都會(huì)滾出用來(lái)顯示內(nèi)容。 tab bar在反向滾動(dòng)時(shí)返回,the toolbar在完全反向滾動(dòng)時(shí)返回。

Status bar, toolbar, and tab bar
Status bar height: 24dp
Toolbar height: 56dp / 64dp
Tab bar height: 48dp

3. Flexible space
因?yàn)閠he app bar是靈活的,它可以擴(kuò)展以適應(yīng)更大的排版或圖片。 要擴(kuò)展the app bar,請(qǐng)?zhí)砑觙lexible space塊。
Flexible space可以被顯示為以下兩種方式之一:
The flexible space逐漸縮小,直到只剩下the toolbar。 The title在導(dǎo)航欄中縮小到20sp。 當(dāng)滾動(dòng)到頁(yè)面的頂部時(shí),the flexible space和the title再次成長(zhǎng)。
整個(gè)The app bar滾出后。 當(dāng)用戶反向滾動(dòng)時(shí),the toolbar返回固定到頂部。 當(dāng)向后滾動(dòng)時(shí),the flexible space和the title再次成長(zhǎng)。

Status bar, toolbar, and flexible space
Status bar height: 24dp
Toolbar height: 56dp / 64dp

4. Flexible space with image
使用flexible space在the app bar中容納所期望寬高比的圖片。
在此示例中,寬高比為4:3。 當(dāng)滾動(dòng)時(shí),內(nèi)容上推圖像,這縮減了flexible space。 在轉(zhuǎn)換結(jié)束時(shí),圖像被著色成the primary color,與滾動(dòng)無(wú)關(guān)。

Status bar, toolbar, and flexible space
Status bar height: 24dp
Toolbar height: 56dp / 64dp

5. Flexible space with overlapping content
內(nèi)容可以與the app bar重疊。
The app bar有兩種滾動(dòng)選項(xiàng):
- The app bar最初位于內(nèi)容的后面。 向上滾動(dòng)時(shí),the app bar應(yīng)比內(nèi)容滾動(dòng)更快,直到內(nèi)容不再與the app bar重疊。 一旦錨定到位,the app bar就會(huì)提升自己以使內(nèi)容可以在the app bar下方滾動(dòng)。
- The app bar可以滾動(dòng)離開(kāi)屏幕用來(lái)顯示內(nèi)容,并在用戶反向滾動(dòng)時(shí)返回。
在此互動(dòng)中,the app bar不能包含tabs。

Flexible space
Status bar: 24dp
Toolbar: 56dp/64dp


通過(guò)實(shí)例實(shí)現(xiàn)material designs 滾動(dòng)技術(shù)中的5種Behavior
1. 實(shí)現(xiàn)第一種Behavior(Standard app bar)
布局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:layout_scrollFlags="scroll|enterAlways">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentLeft="true"
android:layout_margin="8dp"
android:src="@drawable/topbar_left"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="First Behavior"/>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_margin="8dp"
android:src="@drawable/topbar_info"/>
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview_show_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
上面對(duì)于AppBarLayout的概述可知,AppBarLayout最好作為CoordinatorLayout直接子View使用并且需要一個(gè)滾動(dòng)的兄弟View,所以我構(gòu)建了上面的布局結(jié)構(gòu);可以看到上面的RelativeLayout中設(shè)置了app:layout_scrollFlags屬性,該屬性有5個(gè)不同的值可以設(shè)置,具體的含義如下:
1. scroll(SCROLL_FLAG_SCROLL)
//Google的解釋
The view will be scroll in direct relation to scroll events. This flag needs to be
set for any of the other flags to take effect. If any sibling views
before this one do not have this flag, then this value has no effect.
//我的理解(為了便于描述,就用上面的布局來(lái)說(shuō)明)
1> 如果RelativeLayout想要滾動(dòng)效果,必須設(shè)置app:layout_scrollFlags="scroll";當(dāng)手指向上
滑動(dòng)時(shí),RelativeLayout和RecyclerView會(huì)整體向上移動(dòng),直到RelativeLayout完全移出屏幕,
RecyclerView的內(nèi)容才會(huì)向上滑動(dòng);接著手指向下滑動(dòng),RecyclerView的內(nèi)容會(huì)向下滑動(dòng),
當(dāng)RecyclerView的第一項(xiàng)內(nèi)容完全顯示時(shí)RelativeLayout和RecyclerView會(huì)整體向下移動(dòng),
直到RelativeLayout完全顯示。
2> 如果RelativeLayout沒(méi)有設(shè)置app:layout_scrollFlags="scroll",那么設(shè)置其他的flag是不會(huì)
起作用的。
3> 布局文件中,假如RelativeLayout前面有兄弟View并且前面的兄弟View沒(méi)有設(shè)置app:layout_scrollFlags="scroll",
那么RelativeLayout設(shè)置app:layout_scrollFlags="scroll"是不起作用的。
2. enterAlways(SCROLL_FLAG_ENTER_ALWAYS)
//Google的解釋
When entering (scrolling on screen) the view will scroll on any downwards
scroll event, regardless of whether the scrolling view is also scrolling. This
is commonly referred to as the 'quick return' pattern.
//我的理解(為了便于描述,就用上面的布局來(lái)說(shuō)明)
1> enterAlways是用來(lái)與scroll聯(lián)合使用,否者不起作用;
2> enterAlways主要是用來(lái)改變scroll向下滑動(dòng)的效果,當(dāng)手指向下滑動(dòng)時(shí),效果與單獨(dú)使用scroll相同;
接著手指向下滑動(dòng),RelativeLayout和RecyclerView會(huì)整體向下滑動(dòng),當(dāng)RelativeLayout完全顯示時(shí),
RecyclerView的內(nèi)容接著向下滑動(dòng)。
3. enterAlwaysCollapsed(SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)
//Google的解釋
An additional flag for 'enterAlways' which modifies the returning view to only
initially scroll back to it's collapsed height. Once the scrolling view has
reached the end of it's scroll range, the remainder of this view will be
scrolled into view. The collapsed height is defined by the view's minimum height.
//我的理解(為了便于描述,就用上面的布局來(lái)說(shuō)明)
1> enterAlwaysCollapsed是作為enterAlways的附加flag的,因此enterAlwaysCollapsed必須與
enterAlways和scroll聯(lián)合使用,否者不起作用;
2> enterAlwaysCollapsed主要是用來(lái)改變scroll|enterAlways向下滑動(dòng)的效果,當(dāng)手指向下滑動(dòng)時(shí),
效果與使用scroll|enterAlways相同;接著手指向下滑動(dòng),RelativeLayout和RecyclerView會(huì)整體會(huì)
向下滑動(dòng),當(dāng)RelativeLayout顯示到最小高度時(shí),RecyclerView的內(nèi)容接著向下滑動(dòng),
當(dāng)RecyclerView的第一項(xiàng)內(nèi)容完全顯示時(shí)RelativeLayout和RecyclerView會(huì)整體向下移動(dòng),
直到RelativeLayout完全顯示。
4 exitUntilCollapsed(SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)
//Google的解釋
When exiting (scrolling off screen) the view will be scrolled until it is
'collapsed'. The collapsed height is defined by the view's minimum height.
//我的理解(為了便于描述,就用上面的布局來(lái)說(shuō)明)
1> exitUntilCollapsed是用來(lái)與scroll聯(lián)合使用,否者不起作用;
2> exitUntilCollapsed主要是用來(lái)改變scroll向上滑動(dòng)的效果,當(dāng)手指向上滑動(dòng)時(shí),
RelativeLayout和RecyclerView會(huì)整體向上移動(dòng),直到RelativeLayout縮小到最小高度時(shí),
RecyclerView的內(nèi)容接著向上滑動(dòng);接著手指向下滑動(dòng),效果與單獨(dú)使用scroll相同。
5. snap(SCROLL_FLAG_SNAP)
//Google的解釋
Upon a scroll ending, if the view is only partially visible then it will be snapped
and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
is visible then it will be scrolled fully into view.
//我的理解(為了便于描述,就用上面的布局來(lái)說(shuō)明)
1> snap是用來(lái)與scroll聯(lián)合使用,否者不起作用;
2> 當(dāng)滾動(dòng)結(jié)束時(shí),如果RelativeLayout只是部分可見(jiàn),那么RelativeLayout將滾動(dòng)到它最近的邊緣。
例如,如果RelativeLayout只有底部25%顯示,RelativeLayout將完全滾動(dòng)屏幕。 相反,
如果RelativeLayout的底部75%是可見(jiàn)的,那么它將被完全滾動(dòng)到視圖。
6. 注意
1> scroll、enterAlways、和exitUntilCollapsed三個(gè)聯(lián)合使用的時(shí)候;
當(dāng)手指向上滑動(dòng)時(shí),RelativeLayout和RecyclerView會(huì)整體向上移動(dòng),直到RelativeLayout縮小
到最小高度時(shí),RecyclerView的內(nèi)容接著向上滑動(dòng);接著手指向下滑動(dòng),RelativeLayout和
RecyclerView會(huì)整體向下移動(dòng),直到RelativeLayout完全顯示時(shí),RecyclerView的內(nèi)容接著
向下滑動(dòng)。
2>scroll、enterAlways、enterAlwaysCollapsed和exitUntilCollapsed四個(gè)聯(lián)合使用的時(shí)候,
效果很奇怪,不是我期望的,有興趣的同學(xué)可以自己研究下。
java代碼我就不再展示了,也就是mock一些數(shù)據(jù)來(lái)填充RecyclerView。
運(yùn)行結(jié)果如下所示:

1> 如果RelativeLayout設(shè)置app:layout_scrollFlags="scroll|enterAlways"就實(shí)現(xiàn)了第一種Behavior中的第一種種滾動(dòng)方式(也就是上面的例子);
2> 如果RelativeLayout沒(méi)有設(shè)置layout_scrollFlags屬性,就實(shí)現(xiàn)了第一種Behavior中的第二種滾動(dòng)方式,有興趣的同學(xué)可以嘗試一下。
2. 實(shí)現(xiàn)第二種Behavior(App bar with tabs)
布局代碼如下:
主Fragment的布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:layout_scrollFlags="scroll|enterAlways">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentLeft="true"
android:layout_margin="8dp"
android:src="@drawable/topbar_left"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="First Behavior"/>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_margin="8dp"
android:src="@drawable/topbar_info"/>
</RelativeLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabTextColor="@android:color/black"
app:tabSelectedTextColor="@android:color/holo_blue_bright"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
ViewPager中Fragment的布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recyclerview_show_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
上面布局中的TabLayout大家或許有些陌生,但是通過(guò)名字就知道是用著tab欄的,下面簡(jiǎn)單介紹一下TabLayout的幾個(gè)常用屬性:
tabGravity —Tab的重心,有填充和居中兩個(gè)值,為別為fill和center,默認(rèn)為fill。
tabMode —Tab的模式,有固定和滾動(dòng)兩個(gè)模式,分別為 fixed 和 scrollable,默認(rèn)為fixed。
tabTextColor —設(shè)置默認(rèn)狀態(tài)下Tab上字體的顏色。
tabSelectedTextColor —設(shè)置選中狀態(tài)下Tab上字體的顏色。
其他的布局結(jié)構(gòu)我就不多說(shuō)了,相信大家可以看得懂。
java代碼如下所示:
主Fragment的實(shí)現(xiàn)代碼
public class SecondBehaviorFragment extends BaseFragment {
private View root;
private TabLayout tabLayout;
private ViewPager viewPager;
@Override
protected int getLayoutResId() {
return R.layout.fargment_second_behavior;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
root = super.onCreateView(inflater, container, savedInstanceState);
initView();
return root;
}
private void initView() {
tabLayout = (TabLayout) root.findViewById(R.id.tablayout);
tabLayout.addTab(tabLayout.newTab().setText("TabOne"), true);
tabLayout.addTab(tabLayout.newTab().setText("TabTwo"));
tabLayout.addTab(tabLayout.newTab().setText("TabThree"));
viewPager = (ViewPager) root.findViewById(R.id.viewpager);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
viewPager.setAdapter(viewPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private String[] pageTitles = new String[] {"TabOne", "TabTwo", "TabThree"};
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
SecondBehaviorVpFragment fragment = new SecondBehaviorVpFragment();
return fragment;
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
return pageTitles[position];
}
}
}
ViewPager中Fragment的實(shí)現(xiàn)代碼:
public class SecondBehaviorVpFragment extends BaseFragment {
private View root;
private RecyclerView recyclerView;
@Override
protected int getLayoutResId() {
return R.layout.fragment_second_behavior_vp;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
root = super.onCreateView(inflater, container, savedInstanceState);
initView();
return root;
}
private void initView() {
recyclerView = (RecyclerView) root.findViewById(R.id.recyclerview_show_image);
MyAdapter myAdapter = new MyAdapter();
final GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 2);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.addItemDecoration(new RecycleViewDivider(getActivity(),
RecycleViewDivider.LayoutManagerType.GRID, RecycleViewDivider.OrientationType.HORIZONTAL));
recyclerView.setAdapter(myAdapter);
}
public class MyAdapter extends RecyclerView.Adapter {
private int[] imageResIds = new int[] {R.drawable.beauty1, R.drawable.beauty2,
R.drawable.beauty3, R.drawable.beauty4, R.drawable.beauty5,
R.drawable.beauty6, R.drawable.beauty7, R.drawable.beauty8};
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ImageView imageView = new ImageView(getContext());
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
RecyclerView.LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(layoutParams);
MyViewHolder myViewHolder = new MyViewHolder(imageView);
return myViewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyViewHolder)holder).updateView(imageResIds[position]);
}
@Override
public int getItemCount() {
return imageResIds.length;
}
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
public MyViewHolder(View itemView) {
super(itemView);
imageView = (ImageView) itemView;
}
public void updateView(int resId) {
imageView.setImageResource(resId);
}
}
}
代碼很簡(jiǎn)單,我就不做解釋了,下面給出運(yùn)行結(jié)果:

1>如果RelativeLayout設(shè)置app:layout_scrollFlags="scroll|enterAlways"和TabLayout不設(shè)置layout_scrollFlags屬性就實(shí)現(xiàn)了第二種Behavior中的第一種種滾動(dòng)方式(也就是上面的例子);
2> 如果RelativeLayout和TabLayout都沒(méi)有設(shè)置layout_scrollFlags屬性,就實(shí)現(xiàn)了第二種Behavior中的第二種滾動(dòng)方式,有興趣的同學(xué)可以嘗試一下;
3> 如果RelativeLayout和TabLayout都設(shè)置
app:layout_scrollFlags="scroll|enterAlways"就實(shí)現(xiàn)了第二種Behavior中的第三種種滾動(dòng)方式,有興趣的同學(xué)可以嘗試一下。
后面的3個(gè)效果我會(huì)在CollapsingToolbarLayout用法分析中繼續(xù)研究。