本文同步發佈到個人簡書專欄Material Design之 AppbarLayout 開發實踐總結html
前一篇文章是Material Design 系列的第一篇文章,講了Toolbar 的使用,《Material Design 之 Toolbar 開發實踐總結》,還沒看過的同窗能夠去看一下,這篇是Material Design 系列的第二篇文章,此次咱們講AppbarLayout。一說到AppbarLayout,那麼咱們必然會說到另外兩個與AppbarLayout常常一塊兒使用的View,那就是 CoordinatorLayout和CollapsingToolbarLayout。這三個View在一塊兒使用的功能很是強大,能夠實現不少炫酷的UI和動畫效果。本篇文章將分別介紹三個view的一些功能和屬性,最將三個View結合在一塊兒使用實現炫酷的UI效果。java
第一次接觸CoordinatorLayout 你可能有這些疑問,CoordinatorLayout 究竟是個什麼玩意兒呢?它到底能幫咱們作什麼?咱們要了解它,確定是先看官方文檔了。文檔的第一句話就很是醒目: CoordinatorLayout is a super-powered FrameLayout ,很是明瞭,CoordinatorLayout 繼承於ViewGroup,它就是一個超級強大Framelayout。CoordinatorLayout的做用就是協調子View。它有兩種使用場景:android
1,做爲 一個應用頂層的裝飾佈局,也就是一個Activity Layout 的最外一層佈局。
2,As a container for a specific interaction with one or more child views,做爲一個或多個有特定響應動做的容器。git
CoordinatorLayout 能夠協調子View,而這些子View 的具體響應動做是經過 behavior 來指定的。若是你有特定的需求,你就須要本身定義一個特定的 Behavior,Google 也給咱們定義了一些經常使用的Behavior,如後面要用的到的 appbar_scrolling_view_behavior ,用於協調 AppbarLayout 與 ScrollView 滑動的Behavior,以下:github
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>複製代碼
經過 layout_behavior 屬性來指定 Behavior。本篇文章不會過多的去介紹Behavior,後面會單獨拿出來介紹,咱們只要知道CoordinatorLayout 的做用就好了,能夠經過Behavior 協調子View 。在網上也看到了一篇不錯的專門介紹CoordinatorLayout 的文章,CoordinatorLayout的使用如此簡單,須要的能夠去了解一下。app
AppbarLayout繼承自LinearLayout,它就是一個垂直方向的LinearLayout,在LinearLayout的基礎上添加了一些材料設計的概念和特性,即滑動手勢。它可讓你定製在某個可滑動的View(如:ScrollView ,ListView ,RecyclerView 等)滑動手勢發生改變時,內部的子View 該作什麼動做。子View應該提供滑動時他們指望的響應的動做Behavior,經過setScrollFlags(int)),或者xml 中使用屬性:ide
app:layout_scrollFlags複製代碼
注意:AppbarLayout 嚴重依賴於CoordinatorLayout,必須用於CoordinatorLayout 的直接子View,若是你將AppbarLayout 放在其餘的ViewGroup 裏面,那麼它的這些功能是無效的。oop
上面說了 AppbarLayout 能夠定製當某個可滑動的View滑動手勢改變時內部子View的動做,經過app:layout_scrollFlags來指定,那麼如今咱們就看一下layout_scrollFlags有哪幾種動做。layout_scrollFlags有5種動做,分別是 scroll
,enterAlways
,enterAlwaysCollapsed
,exitUntilCollapsed
,snap
。咱們來分別看一下這五種動做的含義和效果。佈局
1, scroll ,子View 添加layout_scrollFlags屬性 的值scroll 時,這個View將會隨着可滾動View(如:ScrollView,如下都會用ScrollView 來代替可滾動的View )一塊兒滾動,就好像子View 是屬於ScrollView的一部分同樣。動畫
佈局代碼以下:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="AppbarLayout"
app:titleTextColor="@color/white"
app:layout_scrollFlags="scroll"
>
</android.support.v7.widget.Toolbar>複製代碼
效果以下:
2, enterAlways ,子View 添加layout_scrollFlags屬性 的值有enterAlways 時, 當ScrollView 向下滑動時,子View 將直接向下滑動,而無論ScrollView 是否在滑動。注意:要與scroll 搭配使用,否者是不能滑動的。
代碼以下:<android.support.v7.widget.Toolbar android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:title="AppbarLayout" app:titleTextColor="@color/white" app:layout_scrollFlags="scroll|enterAlways" />複製代碼
效果以下:
3, enterAlwaysCollapsed , enterAlwaysCollapsed 是對enterAlways 的補充,當ScrollView 向下滑動的時候,滑動View(也就是設置了enterAlwaysCollapsed 的View)下滑至摺疊的高度,當ScrollView 到達滑動範圍的結束值的時候,滑動View剩下的部分開始滑動。這個摺疊的高度是經過View的minimum height (最小高度)指定的。
補充說明:要配合scroll|enterAlways 一塊兒使用
代碼以下:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="200dp"
android:minHeight="?attr/actionBarSize"
app:title="AppbarLayout"
android:gravity="bottom"
android:layout_marginBottom="25dp"
app:titleTextColor="@color/white"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
/>複製代碼
效果圖以下:
4,exitUntilCollapsed, 當ScrollView 滑出屏幕時(也就時向上滑動時),滑動View先響應滑動事件,滑動至摺疊高度,也就是經過minimum height 設置的最小高度後,就固定不動了,再把滑動事件交給 scrollview 繼續滑動。
代碼以下:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="200dp"
android:minHeight="?attr/actionBarSize"
app:title="AppbarLayout"
android:gravity="bottom"
app:titleTextColor="@color/white"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
/>複製代碼
效果圖以下:
5, snap,意思是:在滾動結束後,若是view只是部分可見,它將滑動到最近的邊界。好比,若是view的底部只有25%可見,它將滾動離開屏幕,而若是底部有75%可見,它將滾動到徹底顯示。
解釋:可能這段話有點難懂,解釋一下,就是說,好比在屏幕的頂部有個View ,高度200dp,我向上滑動40%後中止,也就 40% 滑出了屏幕,剩下的60%留在屏幕,那麼這個屬性就會自動將屏幕外的40% 滑回屏幕,結果的整個View都留在屏幕上,相反,若是我向上將60%的部分滑出屏幕,而後中止滑動,那麼這個屬性會將剩下的40% 也自動滑出屏幕,結果是整個View都在屏幕以外。這就是上面所說的滑動到最近的邊界。
代碼以下:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="200dp"
android:minHeight="?attr/actionBarSize"
app:title="AppbarLayout"
android:gravity="bottom"
app:titleTextColor="@color/white"
app:layout_scrollFlags="scroll|snap"
/>複製代碼
效果圖以下:
介紹一下AppbarLayout幾個經常使用且重要的方法
addOnOffsetChangedListener 當AppbarLayout 的偏移發生改變的時候回調,也就是子View滑動。
getTotalScrollRange 返回AppbarLayout 全部子View的滑動範圍
removeOnOffsetChangedListener 移除監聽器
setExpanded (boolean expanded, boolean animate)設置AppbarLayout 是展開狀態仍是摺疊狀態,animate 參數控制切換到新的狀態時是否須要動畫
setExpanded (boolean expanded) 設置AppbarLayout 是展開狀態仍是摺疊狀態,默認有動畫
CollapsingToolbarLayout 是對Toolbar的包裝而且實現了摺疊app bar效果,使用時,要做爲 AppbarLayout 的直接子View。CollapsingToolbarLayout有如下特性:
(1) Collapsing title(摺疊標題) 當佈局所有可見的時候,title 是最大的,當佈局開始滑出屏幕,title 將變得愈來愈小,你能夠經過setTitle(CharSequence) 來設置要顯示的標題。
注意:Toolbar 和CollapsingToolbarLayout 同時設置了title時,不會顯示Toolbartitle而是顯示CollapsingToolbarLayout 的title,若是要顯示Toolbar 的title,你可一在代碼中添加以下代碼:
collapsingToolbarLayout.setTitle("");複製代碼
(2)Content scrim(內容紗布) 當CollapsingToolbarLayout滑動到一個肯定的閥值時將顯示或者隱藏內容紗布,能夠經過setContentScrim(Drawable)來設置紗布的圖片。
提醒:紗布能夠是圖片也能夠是顏色色值,若是要顯示顏色,在xml 佈局文件中用contentScrim屬性添加,代碼以下:
app:contentScrim="@color/colorPrimary"複製代碼
(3)Status bar scrim(狀態欄紗布) 當CollapsingToolbarLayout滑動到一個肯定的閥值時,狀態欄顯示或隱藏紗布,你能夠經過setStatusBarScrim(Drawable)來設置紗布圖片。
提醒:同內容紗布同樣,狀態欄紗布能夠是圖片也能夠是一個顏色值,若是要顯示顏色值,在xml 中用statusBarScrim 屬性指定。
(4)Parallax scrolling children(有視差地滾動子View) 讓CollapsingToolbarLayout 的子View 能夠有視差的滾動,須要在xml中用 添加以下代碼:
app:layout_collapseMode="parallax"複製代碼
(5)Pinned position children(固定子View的位置)子View能夠固定在全局空間內,這對於實現了摺疊而且容許經過滾動佈局來固定Toolbar 這種狀況很是有用。在xml 中將collapseMode設爲pin,代碼以下:
app:layout_collapseMode="pin"複製代碼
以上就是CollapsingToolbarLayout的一些特性,有了CollapsingToolbarLayout,配合AppbarLayout咱們就能夠很輕鬆的作出這種摺疊header的效果了,請看效果圖:
咱們再來看一下設置了Content scrim(內容紗布) 的效果:
以上就是對CollapsingToolbarLayout 、AppbarLayout、CoordinatorLayout 3個View的使用介紹,總的來講,CoordinatorLayout 是協調子View的,經過Behavior指定子View動做。AppbarLayout就是一個豎直方向的LinearLayout,只不過它添加了一些材料的概念和特性,能夠定製子View的滑動。CollapsingToolbarLayout 是對Toolbar 的包裝,它有5個特性,Collapsing title、Content scrim、Status bar scrim、Parallax scrolling children、Pinned position children。這個三個View配合使用,能夠作出一些很炫酷的UI效果。須要注意的是: AppbarLayout 要做爲CoordinatorLayout 的直接子View使用,而CollapsingToolbarLayout 要做爲AppbarLayout 的直接子View 使用,不然,上面將的特性是沒有效果的。
前文講了CollapsingToolbarLayout 、AppbarLayout、CoordinatorLayout 3個View的使用方法,最後就經過一個實例來結束本文,使用AppbarLayout 實現簡書首頁效果。簡書首頁效果以下:
實現效果以下:
佈局文件以下:
<?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:orientation="vertical"
android:background="@color/white"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/jianshu_appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:elevation="0dp"
>
<com.bigkoo.convenientbanner.ConvenientBanner
android:id="@+id/banner"
android:layout_width="match_parent"
android:layout_height="200dp"
app:canLoop="true"
app:layout_scrollFlags="scroll"
/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll"
android:scrollbars="none"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="@+id/item_label1"
android:layout_width="130dp"
android:layout_height="50dp"
android:textColor="@color/white"
android:textSize="15sp"
android:text="小說精選"
android:gravity="center"
android:background="@drawable/label_shape"
/>
<TextView
android:id="@+id/item_label2"
android:layout_width="130dp"
android:layout_height="50dp"
android:textColor="@color/white"
android:textSize="15sp"
android:layout_marginLeft="5dp"
android:text="攝影遊記"
android:gravity="center"
android:background="@drawable/label_shape2"
/>
<TextView
android:id="@+id/item_label3"
android:layout_width="130dp"
android:layout_height="50dp"
android:textColor="@color/white"
android:textSize="15sp"
android:text="漫畫手繪"
android:layout_marginLeft="5dp"
android:gravity="center"
android:background="@drawable/label_shape3"
/>
<TextView
android:id="@+id/item_label4"
android:layout_width="130dp"
android:layout_height="50dp"
android:textColor="@color/white"
android:textSize="15sp"
android:text="簽約做者"
android:layout_marginLeft="5dp"
android:gravity="center"
android:background="@drawable/label_shape4"
/>
</LinearLayout>
</HorizontalScrollView>
<EditText
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:hint="搜索簡書的內容和朋友"
android:gravity="center"
android:background="@drawable/edti_text_shape"
android:layout_marginBottom="5dp"
/>
<View
android:id="@+id/line_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@android:color/darker_gray"
android:layout_marginBottom="10dp"
android:visibility="gone"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/vertical_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>複製代碼
Activity 代碼以下:
**
*
* 仿簡書 首頁效果
* Created by zhouwei on 16/12/8.
*/
public class JanshuActivity extends AppCompatActivity {
private ConvenientBanner mConvenientBanner;
private RecyclerView mRecyclerView;
private AppBarLayout mAppBarLayout;
private View mLine;
private String[] images = {"http://img2.imgtn.bdimg.com/it/u=3093785514,1341050958&fm=21&gp=0.jpg",
"http://img2.3lian.com/2014/f2/37/d/40.jpg",
"http://d.3987.com/sqmy_131219/001.jpg",
"http://img2.3lian.com/2014/f2/37/d/39.jpg",
"http://www.8kmm.com/UploadFiles/2012/8/201208140920132659.jpg",
"http://f.hiphotos.baidu.com/image/h%3D200/sign=1478eb74d5a20cf45990f9df460b4b0c/d058ccbf6c81800a5422e5fdb43533fa838b4779.jpg",
"http://f.hiphotos.baidu.com/image/pic/item/09fa513d269759ee50f1971ab6fb43166c22dfba.jpg"
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.janshu_activity_layout);
initView();
}
private void initView() {
mAppBarLayout = (AppBarLayout) findViewById(R.id.jianshu_appbar_layout);
mLine = findViewById(R.id.line_divider);
mConvenientBanner = (ConvenientBanner) findViewById(R.id.banner);
mRecyclerView = (RecyclerView) findViewById(R.id.vertical_recyclerView);
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(manager);
MyAdapter myAdapter = new MyAdapter();
mRecyclerView.setAdapter(myAdapter);
myAdapter.setData(mockData());
myAdapter.notifyDataSetChanged();
mConvenientBanner.setPages(new CBViewHolderCreator<NetworkImageHolderView>() {
@Override
public NetworkImageHolderView createHolder() {
return new NetworkImageHolderView();
}
}, Arrays.asList(images));
mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if(Math.abs(verticalOffset) >= mAppBarLayout.getTotalScrollRange()){
mLine.setVisibility(View.VISIBLE);
}else{
mLine.setVisibility(View.GONE);
}
}
});
}
@Override
protected void onResume() {
super.onResume();
mConvenientBanner.startTurning(2000);// 2s 換一張
}
@Override
protected void onPause() {
super.onPause();
mConvenientBanner.stopTurning();
}
/** * 模擬首頁數據 * @return */
private List<JsEntry> mockData(){
List<JsEntry> data = new ArrayList<>();
JsEntry jsEntry = new JsEntry();
jsEntry.comment = 50;
jsEntry.award = 3;
jsEntry.like = 460;
jsEntry.seek = 12504;
jsEntry.time = "15小時前";
jsEntry.title = "這些情商的技巧,你是否是都掌握了?";
jsEntry.authorName = "JayChou";
jsEntry.label = "心理";
jsEntry.cover ="http://upload-images.jianshu.io/upload_images/2785318-5306a632b46a8c27.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1020/q/80";
JsEntry jsEntry2 = new JsEntry();
jsEntry2.comment = 150;
jsEntry2.award = 33;
jsEntry2.like = 1460;
jsEntry2.seek = 170444;
jsEntry2.time = "10小時前";
jsEntry2.title = "除了陰謀,《錦繡未央》裏還有哪些溫情?";
jsEntry2.authorName = "菇涼似夢";
jsEntry2.label = "文化.藝術";
jsEntry2.cover = "http://upload-images.jianshu.io/upload_images/2881988-b217e714eb05f88e.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1020/q/80";
for (int i=0;i<100;i++){
if(i % 2 == 0){
data.add(jsEntry);
}else{
data.add(jsEntry2);
}
}
return data;
}
public static class NetworkImageHolderView implements CBPageAdapter.Holder<String>{
private ImageView imageView;
@Override
public View createView(Context context) {
imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
return imageView;
}
@Override
public void UpdateUI(Context context, int position, String data) {
ImageLoader.getInstance().displayImage(data,imageView);
}
}
public static class MyAdapter extends RecyclerView.Adapter{
private List<JsEntry> mData;
public void setData(List<JsEntry> data) {
mData = data;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.jianshu_label_item,null));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
MyViewHolder viewHolder = (MyViewHolder) holder;
JsEntry jsEntry = mData.get(position);
viewHolder.title.setText(jsEntry.title);
viewHolder.name.setText(jsEntry.authorName);
viewHolder.label.setText(jsEntry.label);
viewHolder.time.setText(jsEntry.time);
ImageLoader.getInstance().displayImage(jsEntry.cover,viewHolder.cover);
viewHolder.comment.setText(String.format(viewHolder.comment.getContext().getResources().getString(R.string.js_comment),jsEntry.seek,jsEntry.comment,jsEntry.like,jsEntry.award));
}
@Override
public int getItemCount() {
return mData == null ? 0:mData.size();
}
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private TextView time;
private TextView comment;
private TextView label;
private TextView name;
private ImageView cover;
public MyViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.item_content);
time = (TextView) itemView.findViewById(R.id.publish_time);
comment = (TextView) itemView.findViewById(R.id.js_comment);
label = (TextView) itemView.findViewById(R.id.js_label);
name = (TextView) itemView.findViewById(R.id.author_name);
cover = (ImageView) itemView.findViewById(R.id.cover);
}
}
}複製代碼
Demo 源碼請看GithubMaterialDesignSamples
好了,以上就是關於AppbarLayout 、CollapsingToolbarLayout、CollapsingToolbarLayout的使用介紹,若有什麼問題,歡迎評論區指正,Good night。