Android Material Design簡單使用


吐槽

作為一個 Android developer,沒有什么比拿着 UI 設計的一堆 iOS 風格的設計 來做需求更惡心的了,基本所有空間都要照着 iOS 來畫一遍,Material Design 辣么酷炫 為什么 UI在設計的階段不設計成 Material Design風格呢?

今天試了幾個比較Support包中比較典型的Material Design控件,后期會在學習下Material Design的設計思想和理念,希望能拉着 UI 做一次Material Design 分享,改變我們 APP 的 iOS 風格啊。

最終效果如下

Android Design Support 庫依賴

在 build.gradle 中加入support 包

1
compile 'com.android.support:appcompat-v7:23.1.1'

Design Support Library 中包含了 Support v4 和 AppCompat v7

Floating Action Button

我們希望FloatingActionButton懸浮在頁面的右下方,所以我們父節點應使用Flowlayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="io.github.xuyushi.materialdesigndemo.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />

<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:src="@android:drawable/ic_dialog_email" />

</FrameLayout>

和普通 button 一樣可以設置其點擊事件

1
2
3
4
5
6
7
8
9
private void initFb() {
mFb = (FloatingActionButton) findViewById(R.id.fb);
mFb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "fb predsed ", Toast.LENGTH_SHORT).show();
}
});
}

Android:elevation屬性為 view 在空閑狀態下的陰影深度, 需要在 api 21以上才能使用,使用 support 包可以使用app:elevation來表示空閑狀態高度,app:pressedTanslationZ為按下狀態的高度

按鈕的顏色一般為主題的強調色,也可以使用 ”app:backgroundTint“修改

Snackbar

和 Toast 很像,snackbar 可以展示一段簡單的信息,不同點是它的展示更像是整體 UI 的一部分,不是想 toast 一樣是浮在 UI 上的,並且可以有簡單的交互

在點擊 floatingActionButton時顯示Snackbar

但是可以看到,Snackbar 遮擋住了我們的 view,這時候需要一個CoordinatorLayout來協調 view 布局

CoordinatorLayout

將父布局中的framelaout換成CoordinatorLayout,其他不變,再來看看效果

==todo CoordinatorLayout學習==

Toolbar

Toolbar 比傳統的 ActionBar 更靈活,功能也更多,我們可以看到現在市面上很多的 APP 已經用 Toolbar 替代了 actionbar,在 Desgin Support 的組件中,很多設計都可以和 Toolbar 協同工作,而不是和 actionbar,所以還是建議使用新的 toolbar 替換以前的 actionbar

替換步驟

1、在 minifest 中,將 activity 的 apptheme 的 style 中的 actionbar屬性去掉

1
2
3
4
5
6
7
8
9
10
11
12
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
  1. 在 fb 之前放入 Toolbar 組件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<android.support.design.widget.CoordinatorLayout 
.........

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />

<android.support.design.widget.FloatingActionButton
android:id="@+id/fb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:src="@android:drawable/ic_dialog_email"
app:elevation="12dp"
app:pressedTranslationZ="30dp"
/>


</android.support.design.widget.CoordinatorLayout>
  1. 通知系統使用 toolbar
1
2
3
4
private void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
}

CoordinatorLayout中的 view 必須是能一同協作的 view,就像 Snackbar 一樣,但是 toolbar 並不是這樣能協同作戰的 view,所以我們需要用一個可以協同作戰的 view 來包裹上Toolbar,這就是 AppBarLayout

現在我們的布局文件結構是這樣的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<android.support.design.widget.CoordinatorLayout
...>

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
.../>
</android.support.design.widget.AppBarLayout>

<android.support.design.widget.FloatingActionButton
...>
</android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>

注意

根據官方的谷歌文檔,AppBarLayout目前必須是第一個嵌套在CoordinatorLayout里面的子view

在 toolbar 中加入屬性,app:layout_collapseMode=”pin”,使得 Toolbar 中的按鈕能固定在頂部

在布局中加入內容

在布局中嘗試加入一些按鈕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
....
</android.support.design.widget.AppBarLayout>


<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:orientation="vertical">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />

</LinearLayout>
...

我們定義三個按鍵,卻被 toolbar 遮住了一個,原因是LinearLayout並沒有被設計成在CoordinatorLayout協同工作的模式,為了使他們能在CoordinatorLayout協同工作,我們需要在LinearLayout加上一條屬性,來告訴它的滾動屬性()

1
2
3
4
5
<LinearLayout
...
app:layout_behavior="@string/appbar_scrolling_view_behavior"
...
>

搞定

TabLayout

根據官網的知道,TabLayout通常應該是放在頂部,(iOS 的 tab 好像基本在底部),
他應該在陰影部分上面,所以應該放在AppBarlayout

1
2
3
4
5
6
7
<android.support.design.widget.AppBarLayout ...>
<android.support.v7.widget.Toolbar ... />
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.design.widget.AppBarLayout>

java 中設置這些 tab 屬性

1
2
3
4
5
6
private void initTableLayout() {
mTabLayout = (TabLayout) findViewById(R.id.tabLayout);
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 1"));
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 2"));
mTabLayout.addTab(mTabLayout.newTab().setText("Tab 3"));
}

背景會設置為主題色,導航線是強調色。但是字還是黑色的,因為我們沒有為 tablayout 定義主題,

1
2
3
<android.support.design.widget.TabLayout
...
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

通常 tablayout 會和ViewPager一起使用 ,這時候使用
public void setupWithViewPager (ViewPager viewPager)

一張圖看的比較清晰

內容滾動時,AppBarLayout隱藏

當滑檔內容時,為了騰出跟多的空間展示內容可以將AppBarLayout隱藏

1.用 scrollView 包裹 LinearLayout,記得加上 app:layout_behavior屬性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
...
</LinearLayout>
</ScrollView>
  1. Toolbar 加上屬性
1
2
3
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways" />
  1. scrollView 也不能喝 CoordinatorLayout 協同工作,同上面一樣,要用別的 view 包裹或者直接使用 NestedSrollView替換scrollView

如果希望 tablayout 也消失,只需要和 tablayout 加上相同的屬性就行了

1
2
3
<android.support.design.widget.TabLayout
...
app:layout_scrollFlags="scroll|enterAlways" />

滑動內容 和 AppBarLayout是如何進行聯系的?

我們需要定義AppBarLayout與滾動視圖之間的聯系。在RecyclerView或者任意支持嵌套滾動的view比如NestedScrollView上添加app:layout_behavior。support library包含了一個特殊的字符串資源@string/appbar_scrolling_view_behavior,它和AppBarLayout.ScrollingViewBehavior相匹配,用來通知AppBarLayout 這個特殊的view何時發生了滾動事件,這個behavior需要設置在觸發事件(滾動)的view之上

當CoordinatorLayout發現scrollView中定義了這個屬性,它會搜索自己所包含的其他view,看看是否有view與這個behavior相關聯。AppBarLayout.ScrollingViewBehavior描述了RecyclerView與AppBarLayout之間的依賴關系。RecyclerView的任意滾動事件都將觸發AppBarLayout或者AppBarLayout里面view的改變。

AppBarLayout里面定義的view只要設置了app:layout_scrollFlags屬性,就可以在RecyclerView滾動事件發生的時候被觸發:

app:layout_scrollFlags屬性里面必須至少啟用scroll這個flag,這樣這個view才會滾動出屏幕,否則它將一直固定在頂部。可以使用的其他flag有:

  • enterAlways: 一旦向上滾動這個view就可見。
  • enterAlwaysCollapsed: 顧名思義,這個flag定義的是何時進入(已經消失之后何時再次顯示)。假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那么view將在到達這個最小高度的時候開始顯示,並且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
  • exitUntilCollapsed: 同樣顧名思義,這個flag時定義何時退出,當你定義了一個minHeight,這個view將在滾動到達這個最小高度的時候消失。

記住,要把帶有scroll flag的view放在前面,這樣收回的view才能讓正常退出,而固定的view繼續留在頂部。

可折疊的 Toolbar

  • 用 CollapsingToolbarLayout 包裹 Toolbar,但仍然在 AppBarLayout 中
  • 刪除 Toolbar 中的 layout_scrollFlags
  • 為 CollapsingToolbarLayout 聲明 layout_scrollFlags,並且將 layout_scrollFlags 設置成 scroll|exitUntilCollapsed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="256dp">

<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"/>

</android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

注意 CollapsingToolbarLayout 的高度是android:layout_height="match_parent"

CollapsingToolbarLayout在展開和收縮時,標題的文字會自動過度的,可以通過 app:expandedTitleMargin 等來改變文字位置

為 appBar 添加背景圖片

由於 CollapsingToolbarLayout 是繼承 Framelayout 的,所以我們可以直接添加一個 ImageView 作為背景圖片

1
2
3
4
5
6
7
8
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/header" />

<android.support.v7.widget.Toolbar
...

此時雖然背景已經出來了,但是藍色的導航條依舊存在,需要在 toolbar 去掉這條屬性

1
android:background="?attr/colorPrimary"

給 Imageview 加上視差模式會更帥

1
2
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.5"

也可以在最后恢復成主題色

1
2
3
<android.support.design.widget.CollapsingToolbarLayout
...
app:contentScrim="?attr/colorPrimary">

AppBarLayout布局下,增DrawerLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<android.support.design.widget.AppBarLayout>
...

<android.support.v4.widget.DrawerLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:openDrawer="start">

<include layout="@layout/content_main" />

<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main2"
app:menu="@menu/activity_main2_drawer" />

</android.support.v4.widget.DrawerLayout>

DrawerLayout中分兩部分組成,一部分是content 就是我們需要的主布局內容,另一部分是我們的抽屜的布局,NavigationView中有頂部頭,和標簽

1
2
app:headerLayout="@layout/nav_header_main2"
app:menu="@menu/activity_main2_drawer"

創建菜單。

菜單元素是放在group標簽之下,同時聲明每次只能有一個item被選中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group android:checkableBehavior="single">
<item
android:id="@+id/nav_camera"
android:icon="@drawable/ic_menu_camera"
android:title="Import" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="Gallery" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:title="Slideshow" />
<item
android:id="@+id/nav_manage"
android:icon="@drawable/ic_menu_manage"
android:title="Tools" />
</group>

<item android:title="Communicate">
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="Share" />
<item
android:id="@+id/nav_send"
android:icon="@drawable/ic_menu_send"
android:title="Send" />
</menu>
</item>

</menu>

為了防止頁面被遮蓋,同樣要使得 DrawerLayout協調。加入app:layout_behavior="@string/appbar_scrolling_view_behavior"屬性

java初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void initDraw() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();

if (id == R.id.nav_camera) {
// Handle the camera action
} else if (id == R.id.nav_gallery) {

} else if (id == R.id.nav_slideshow) {

} else if (id == R.id.nav_manage) {

} else if (id == R.id.nav_share) {

} else if (id == R.id.nav_send) {

}

DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
});

SwipeRefreshLayout

NestedScrollView外在包裹一層SwipeRefreshLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
>
.....
</android.support.v4.widget.NestedScrollView>

</android.support.v4.widget.SwipeRefreshLayout>

初始化監聽器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void initRefresh() {
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshContent();
}
});
}

private void refreshContent() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(false);
}
}, 2000);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM