Android Material Design 相容庫的使用詳解
眾所周知Material Design(材質設計)是Google在2014年I/O大會上釋出的一種新的設計規範。一經推出就好評如潮,個人是非常喜歡這種風格的,由於他只支援5.0及其以上的裝置,開發者也只是去嚐嚐鮮,並沒用在真實的專案中去,使得其在國內的市場並不是太好。隨後不久Google就退出了其相容庫Android Design Support Library,相容至2.1!這絕對是業界良心,極大的便利了我們這些開發者。這一相容庫可以讓我們可以在自己的專案真實的去體驗一把。百度谷歌一下,其實現在已經有個很多介紹這個支援庫的文章,但是很多都並不太詳細,大部分讓人看了都似懂非懂,我之前也學習過這個庫的用法,很久沒看基本又忘了,最近我又重新開始學習了這個庫,所以本文也主要是對這次學習的一個總結,當然也參考了其他很多的文章,算是站在巨人的肩膀上吧,當然這篇文章也為後來的學習者有更多的參考資料。
先來幾張效果圖吧:
圖1 圖2感興趣的也可以先看看,最後能做一個什麼樣的App:MDDemo.apk
說到這個相容庫,其實很多都是可以在Github去找到對應的效果的,其實我們也是可以去用Github上的那些開源專案,那麼多的庫都要一個一個去引用,還是非常的麻煩,我猜可能Google也是看到這些東西經常被開發者用在了專案中,索性自己將其封裝成了一個庫,以方便開發者的使用。這個庫我們應該怎樣去使用呢?這是本文講解的重點。首先我們看看這個庫怎麼去引用到我們的專案中:
compile ‘com.android.support:design:23.1.1’
我們只需將其新增到專案的依賴中去,然後同步一下就OK了。
添加了依賴之後我們再來看看這個庫裡面都有哪些控制元件吧,如下圖,紅色邊框矩形框出來的就是這個庫裡面包含的控制元件,我們可以看到有AppBarLayout、CollapsingToolbarLayout、CoordinatorLayout,FloatingActionButton、NavigationView、Snackbar、TabLayout、TextInputLayout
1.Snackbar
SnackBar通過在螢幕底部展示簡潔的資訊,為一個操作提供了一個輕量級的反饋,並且在Snackbar中還可以包含一個操作,在同一時間內,僅且只能顯示一個 Snackbar,它的顯示依賴於UI,不像Toast那樣可以脫離應用顯示。它的用法和Toast很相似,唯一不同的就是它的第一個引數不是傳入Context而是傳入它所依附的父檢視,但是他比Toast更強大。
我們來看看它的基本使用
<pre>
Snackbar.make(mDrawerLayout, “SnackbarClicked”, Snackbar.LENGTH_SHORT).show();</pre>
是不是和Toast很相似。我們來看看它的效果圖:
SnackWithoutAction是不是看著比Toast舒服多了
好了,我們再來看看一個包含Action的Snackbar怎麼使用
<pre>
Snackbar.make(mDrawerLayout, “SnackbarClicked”, Snackbar.LENGTH_SHORT).setAction(“Action”, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, “I’m a Toast”, Toast.LENGTH_SHORT).show();
}
}).setActionTextColor(Color.RED).show();
</pre>
這裡我給Snackbar設定了一個Action,設定其文字顏色為紅色,並帶有了一個點選事件,在單擊這個Action後就彈出一個Toast,效果圖如下
SnackbarWithAction
2.TextInputLayout
使用過EditText的同學肯定知道,有一個叫hint的屬性,它可以提示使用者此處應該輸入什麼內容,然而當用戶輸入真實內容之後,hint的提示內容就消失了,使用者的體驗效果是十分不好的,TextInputLayout的出現解決了這個問題。
我們來看看這個控制元件的是怎麼使用的
<pre>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android“
xmlns:tools=”http://schemas.android.com/tools“
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:padding=”10dp”
tools:context=”burgess.com.materialdesignstudy.MDTextViewActivity”>
<!--TextInputLayout的顏色來自style中的colorAccent的顏色-->
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="使用者名稱/手機號"
android:inputType="textEmailAddress" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密碼"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
</pre>
其實這個控制元件的使用非常簡單,我們只需在EditText外面再巢狀一個TextInputLayout就行了。我們來看看效果
TextInputLayout還是很不錯的,當用戶在輸入的時候hint的內容就會跑到輸入內容的上邊去,其中TextInputLayout中字型的顏色是style檔案中的colorAccent(關於colorAccent是什麼,文末有一張圖看了就清楚了)的顏色。
3.FloatingActionButton
FloatingActionButton從名字可以看出它是一個浮動的按鈕,它是一個帶有陰影的圓形按鈕,可以通過fabSize來改變其大小,主要負責介面的基本操作,這個按鈕總體來說還是比較簡單的。
我們來看看它的一些屬性:
- 預設FloatingActionButton 的背景色是應用主題的 colorAccent(其實MD中的控制元件主題預設基本都是應用的這個主題),可以通過app:backgroundTint 屬性或者setBackgroundTintList (ColorStateList tint)方法去改變背景顏色。
- 上面提到 Floating action button 的大小尺寸,可以用過**app:fabSize **屬性設定(normal or mini)
- ** android:src** 屬性改變 drawable
- app:rippleColor設定點選 button 時候的顏色(水波紋效果)
- app:borderWidth設定 button 的邊框寬度
- app:elevation設定普通狀態陰影的深度(預設是 6dp)
- app:pressedTranslationZ設定點選狀態的陰影深度(預設是 12dp)
怎麼去使用:
<pre><android.support.design.widget.FloatingActionButton
android:id=”@+id/fab_search”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”bottom|end”
android:layout_margin=”16dp”
android:src=”@android:drawable/ic_dialog_email”
app:borderWidth=”2dp”
app:fabSize=”normal”
app:rippleColor=”#ff0000” />
</pre>
其效果圖如下:
FloatingActionButton
4.CoordinatorLayout
我們來看看官方對他的描述:
- CoordinatorLayout is a super-poweredFrameLayout.
- CoordinatorLayout is intended for two primary use cases:
1.As a top-level application decor or chrome layout
2.As a container for a specific interaction with one or more child views
從這裡我們可以知道它是一個增強版的FrameLayout,他可以協調其子View的互動,控制手勢機滾動技巧。這個控制元件十分的強大,用處也十分的廣泛。就比如剛才說的FloatingActionButton如果用CoordinatorLayout 作為FloatingActionButton的父佈局,它將很好的協調Snackbar的顯示與FloatingActionButton。
沒有使用CoordinatorLayout 作為父佈局的FloatingActionButton顯示Snackbar
使用了CoordinatorLayout 作為父佈局的FloatingActionButton顯示Snackbar
這兩個動態圖來源於網上,效果是顯而易見的,原諒我用的手機測試,所以沒法錄製動態圖(如果有人知道怎麼去錄製的請告知)。
還有它的另外一個比較重要的用處是控制協調內容區和AppBarLayout的滾動,這個後面會講到。這個控制元件還是比較複雜的,用多了就能慢慢體會到他的強大之處。我對這個控制元件的還不是那麼的清楚,所以在這裡提供一篇關於CoordinatorLayout 用法的譯文: 掌握 Coordinator Layout
5.NavigationView
抽屜式的導航控制元件,有了他我們可以使得導航更簡單。我們可以直接通過選單資原始檔生成所需的元素。下圖左邊的即是NavigationView。
NavigationView下面我們來看看它是怎麼實現的吧。
<pre>
<?xml version=”1.0” encoding=”utf-8”?>
<!--內容區-->
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/appbar"
layout="@layout/toolbar" />
<FrameLayout
android:id="@+id/frame_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/appbar"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
<!--左側導航選單-->
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/navigation_header"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>
</pre>
可以看到這裡我們是以DrawerLayout作為其父佈局,對於DrawLayout他可以實現一種抽屜式的側滑效果,這裡不多做講解,這裡只簡單說一點:DrawLayout中的第一個佈局是內容佈局,第二個是選單佈局。現在我們直接定位到NavigationView,我們看到這裡有** app:headerLayout=”@layout/navigation_header”、app:menu=”@menu/drawer”**這兩行程式碼,其中headerLayout是設定其頭部的佈局,這個佈局我們可以隨便寫,就和寫普通的佈局檔案一樣的。對於menu就是選單項的配置了,其配置檔案如下:
<pre>
<?xml version=”1.0” encoding=”utf-8”?>
<menu xmlns:android=”http://schemas.android.com/apk/res/android“
xmlns:tools=”http://schemas.android.com/tools“
tools:context=”.MainActivity”>
<group android:checkableBehavior=”single”>
<item
android:id=”@+id/navigation_item_home”
android:icon=”@drawable/iconfont_home”
android:title=”首頁” />
<item
android:id=”@+id/navigation_item_blog”
android:icon=”@drawable/iconfont_blog”
android:title=”我的部落格” />
<item
android:id="@+id/navigation_item_about"
android:icon="@drawable/iconfont_about"
android:title="關於" />
</group>
</menu>
</pre>
就這麼簡單,我們要實現上述的效果就只要這些就足夠了。但是如果我們想要對Item新增一個點選的事件怎麼做呢?請看下面:
<pre>
private void setNavigationViewItemClickListener() {
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_item_home:
mToolbar.setTitle(“首頁”);
switchFragment(“MainFragment”);
break;
case R.id.navigation_item_blog:
mToolbar.setTitle(“我的部落格”);
switchFragment(“BlogFragment”);
break;
case R.id.navigation_item_about:
mToolbar.setTitle(“關於”);
switchFragment(“AboutFragment”);
break;
default:
break;
}
item.setChecked(true);
mDrawerLayout.closeDrawer(Gravity.LEFT);
return false;
}
});
}
</pre>
上面的程式碼很清楚了,就是為NavigationView添加了一個OnNavigationItemSelectedListener的監聽事件,然後我們就可以做我們想做的事了。
6. AppBarLayout、CollapsingToolbarLayout、TabLayout
這三個控制元件我用一個例子一下講解了,在實際的使用中這三個控制元件還是經常會組合在一起的。
-
AppBarLayout:其繼承於LinearLayout,使用AppBarLayout可以讓包含在其中的子控制元件能響應被標記了ScrollingViewBehavior的的滾動事件(要與CoordinatorLayout 一起使用),利用它我們可以很容易的去實現視差滾動效果,這樣所你可能還是不太懂,千言萬語不如一張圖來的直接爽快(這樣圖還是來源於網上,其藍色的部分就是AppBarLayout,內容區就是被標記了ScrollingViewBehavior的,可以看到效果是不是挺不錯的)。
CollapsingToolbarLayout :讓Toolbar可伸縮,在伸縮的時候決定ToolBar是移除螢幕和固定在最上面。由於Toolbar 只能固定到螢幕頂端並且不能做出好的動畫效果,所以才有了這個Layout的出現。
-
TabLayout:這個其實和我們之前使用的第三方庫ViewPagerIndicator是很類似的,一般都和ViewPager一起使用,效果如下圖:
動態圖來源於網上
基本知道他們是幹什麼用的了,下面就讓我們動手,看看到底怎麼用吧。
先來幾張下面我們要實現的效果。由於我是真機測試,沒法錄製動態圖,這裡就用幾張靜態圖代替了。
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/iv_book_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerInside"
android:transitionName="transition_book_img"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#292929"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="2dp"
android:textColor="#FF6347"
android:textSize="14sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:textColor="#575757"
android:textSize="12sp" />
</LinearLayout>
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="scrollable" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
</pre>
我們來分析一下這個佈局檔案:
1.最外層的佈局用的是CoordinatorLayout,因為這裡面有很多的動畫,CoordinatorLayout可以很好的去協調裡面的動畫。在android.support.design.widget.AppBarLayout下面的那個LinearLayout被標記了appbar_scrolling_view_behavior,這樣一來AppBarLayout就能響應LinearLayout中的滾動事件。
2.再來看看CollapsingToolbarLayout,其中contentScrim是設定其內容區的顏色,layout_scrollFlags取了scroll和exitUntilCollapsed兩個值。
layout_scrollFlags的Flag包括:
- scroll: 所有想滾動出螢幕的view都需要設定這個flag,沒有設定這個flag的view將被固定在螢幕頂部。
**enterAlways**: 這個flag讓任意向下的滾動都會導致該view變為可見,當向上滑的時候Toolbar就隱藏,下滑的時候顯示。
**enterAlwaysCollapsed**: 顧名思義,這個flag定義的是何時進入(已經消失之後何時再次顯示)。假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那麼view將在到達這個最小高度的時候開始顯示,並且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
**exitUntilCollapsed**: 同樣顧名思義,這個flag時定義何時退出,當你定義了一個minHeight,這個view將在滾動到達這個最小高度的時候消失。
只看概覽可能還是不會太清楚,注意一定要多實踐,感興趣的讀者可以一個一個去試一試效果。
3.定位到ImageView,有這兩個屬性是我們平常用沒看到的,說明我寫在註釋上了
<pre>
app:layout_collapseMode=”parallax”//這個是配置當ImageView消失或者顯示時候有一種視差滾動效果
app:layout_collapseParallaxMultiplier=”0.7”//視差因子,越大視差特效越明顯,最大為1 </pre>
4.Toolbar中有一個app:layout_collapseMode=”pin” 這個確保Toolbar在AppBarLayout摺疊的時候仍然被固定在螢幕的頂部
佈局檔案就到這,我們來看看Activity中的邏輯
<pre>
/**
Created by _SOLID
Date:2016/3/30
-
Time:20:16
*/
public class BookDetailActivity extends BaseActivity {private String mUrl;
private Toolbar mToolbar;
private CollapsingToolbarLayout mCollapsingToolbarLayout;
private ImageView mIvBook;private BookBean mBookBean;
private TextView mTvTitle;
private TextView mTvMsg;
private TextView mTvRating;
private ViewPager mViewPager;
private TabLayout mTabLayout;@Override
protected void initView() {
//設定Toolbar
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
getSupportActionBar().setHomeButtonEnabled(true);//決定左上角的圖示是否可以點選
getSupportActionBar().setDisplayHomeAsUpEnabled(true);//決定左上角圖示的右側是否有向左的小箭頭
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout); mIvBook = (ImageView) findViewById(R.id.iv_book_image); mTvTitle = (TextView) findViewById(R.id.tv_title); mTvMsg = (TextView) findViewById(R.id.tv_msg); mTvRating = (TextView) findViewById(R.id.tv_rating); mViewPager = (ViewPager) findViewById(R.id.viewpager); mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs); mTabLayout.addTab(mTabLayout.newTab().setText("作者資訊")); mTabLayout.addTab(mTabLayout.newTab().setText("章節")); mTabLayout.addTab(mTabLayout.newTab().setText("書籍簡介"));
}
@Override
protected int setLayoutResourseID() {
return R.layout.activity_book_detail;
}@Override
protected void init() {
mUrl = getIntent().getStringExtra(“url”);
}@Override
protected void initData() {
SolidHttpUtils.getInstance().loadString(mUrl, new SolidHttpUtils.HttpCallBack() {
@Override
public void onLoading() {} @Override public void onSuccess(String result) { Gson gson = new Gson(); mBookBean = gson.fromJson(result, BookBean.class); mCollapsingToolbarLayout.setTitle(mBookBean.getTitle()); mTvTitle.setText(mBookBean.getTitle()); mTvMsg.setText(mBookBean.getAuthor() + "/" + mBookBean.getPublisher() + "/" + mBookBean.getPubdate()); mTvRating.setText(mBookBean.getRating().getAverage() + "分"); SolidHttpUtils.getInstance().loadImage(mBookBean.getImages().getLarge(), mIvBook); BookInfoPageAdapter adapter = new BookInfoPageAdapter(BookDetailActivity.this, mBookBean, getSupportFragmentManager()); mViewPager.setAdapter(adapter); mTabLayout.setupWithViewPager(mViewPager); } @Override public void onError(Exception e) { } });
}
}
</pre>
其實並不難,這裡只需注意一點,當Toolbar被CollapsingToolbarLayout包裹的時候,設定標題是設定CollapsingToolbarLayout的,而不是Toolbar。
好了終於寫完了,差不多寫了接近4個小時,好累的說,現在整個人都是暈暈的。
附(一些控制元件使用使用應注意的地方):
1.在使用CardView的時候,一定要注意,當CardView的寬和高填充父容器的時候,CardView的margin最好要比cardElevation大,不然就看不到立體的效果。
2.我們知道ListView有一個onItemClick的事件,但是RecyclerView卻沒有,那麼我們應該怎樣去設定呢?其實很簡單,關於RecyclerView設定item的點選事件,只需在建立ViewHolder的時候,給填充的View設定單擊事件即可。
3.在使用android.support.design.widget.AppBarLayout的時候內容區最好使用android.support.v4.widget.NestedScrollView,之前我的內容區用的是ScrollView,在往上拉的時候AppBarLayout一直沒有動畫效果,折騰了幾個小時都沒找到原因。最後逼不得用Android Studio建立了一個他自帶的關於AppBarLayout的模板專案,看到他用的是NestedScrollView作為內容區,我果斷就把我的內容區換成了這個,立馬就有動畫效果了。
NestedScrollView官方的描述:
NestedScrollView is just likeScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.
如果感覺還不錯的就給個喜歡支援一下吧,有問題請留言,謝謝
最後附一張MD的主題色解析圖:
MD主題色解析.jpg