Material Button

圖片來源:https://material.io

1. TextButton(明顯度最低)

3. Contained button(明顯度最高)

2. Outlined Button (明顯度中等)

4. Toggle button

圖片來源:https://material.io

1.Text button

A. Text label

C. Icon (optional)

2.Outlined button

A. Text label

B. Container

C. Icon (optional)

3.Contained button

A. Text label

B. Container

C. Icon (optional)

4.Toggle button

A. Container

C. Icon

圖片來源:https://material.io

TextButton

  • 常用在卡片裡

圖片來源:https://material.io

TextButton

  • 常用在對話框

圖片來源:https://material.io

TextButton

  • 在Snackbar裡

圖片來源:https://material.io

OutlineButton

圖片來源:https://material.io

Contained Button

圖片來源:https://material.io

Contained Button

圖片來源:https://material.io

Toggle Button

圖片來源:https://material.io

Color

<item name="materialButtonStyle">@style/AppButton</item>
<style name="AppButton" parent="Widget.MaterialComponents.Button">
    <item name="backgroundTint">?attr/colorSecondary</item>
    <item name="android:textColor">@color/button_text_color</item>
    <item name="iconTint">@color/button_text_color</item>
    <item name="rippleColor">@color/button_ripple_color</item>
</style>

Shape

<!-- 客制形狀 -->
<item name="shapeAppearanceSmallComponent">@style/AppShapeAppearance.SmallComponent</item>
<item name="shapeAppearanceMediumComponent">@style/AppShapeAppearance.MediumComponent</item>
<item name="shapeAppearanceLargeComponent">@style/AppShapeAppearance.LargeComponent</item>
<style name="AppShapeAppearance.SmallComponent" parent="ShapeAppearance.MaterialComponents.SmallComponent">
    <item name="cornerFamily">cut</item>
    <item name="cornerSize">8dp</item>
</style>

<style name="AppShapeAppearance.MediumComponent" parent="ShapeAppearance.MaterialComponents.MediumComponent">
    <item name="cornerFamily">rounded</item>
    <item name="cornerSize">8dp</item>
</style>

<style name="AppShapeAppearance.LargeComponent" parent="ShapeAppearance.MaterialComponents.LargeComponent">
    <item name="cornerFamily">rounded</item>
    <item name="cornerSize">8dp</item>
</style>

Material Motion

Material Motion

  • Container transform
  • Shared axis
  • Fade Through
  • Fade

Container transform

Container transform

  • Fragment
  • Activity
  • View

Shared axis

Fade Through

Fade

Selection controls

Selection controls

Menu

UI 常見錯誤

在我的手機測試都正常,App一上線就跑版

在我的手機測試都正常,App一上線就跑版

  • 手機種類變多了
  • 資料變多了

當資料只有局部出現或無資料時

當資料只有局部出現或無資料時

  • 無資料
  • 部分無資料
  • 極限資料

發生錯誤,沒有告訴使用者該怎麼辦

使用者自行調整手機字型大小

UI佈局優化

客制可重用的UI元件

客制可重用的UI元件

  • 重用UI元件:Include
  • 使用Merge減少View的階層
  • 延遲載入UI資源:Viewstub
  • 自定義View:CustomView
  • 用ViewGroup管理多個子View

Include

Merge

ViewStub

CustomView

Custom View Component

<evan.chen.tutorial.ui.customviewsample.CustomView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="@color/colorAccent" />
<evan.chen.tutorial.ui.customviewsample.CustomView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="@color/colorAccent"
    android:padding="20dp" />

CustomView

<FrameLayout
    android:layout_width="200dp"
    android:layout_height="200dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent">
    <evan.chen.tutorial.ui.customviewsample.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:padding="20dp" />
</FrameLayout>

測量大小

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)
    val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)

    if (widthSpecMode == MeasureSpec.AT_MOST) {
        //wrap_content
    }
    if (widthSpecMode == MeasureSpec.EXACTLY) {
        //指定大小
    }
    if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
        //未定大小
    }
}

Draw

override fun draw(canvas: Canvas?) {
  canvas?.drawLine(startX, startY, stopX, stopY)
}

ViewGroup

ViewGroup

ViewGroup

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        
        //依子View計算GroupView的寬、高
    
    	setMeasuredDimension(finalWidth, finalHeight)
}

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
	
	//編排子View
}

Motion Layout

貝茲曲線

來源:維基百科

Loading Button

Loading Button

矩形 -> 圓形

載入中動畫

Reveal 完成

圓形 -> 矩形

Canvas.drawArc

canvas.drawArc(oval, 30f, 300f, true, p)

canvas.drawArc(oval, 300f, 300f, true, p)

BottomAppBar

Bar

  • ActionBar 最基本的Bar
     
  • ToolBar 客制Bar裡面的內容
     
  • AppBarLayout 隨著其他View響應變化
     
  • CollapsingToolbarLayout 可折疊式的Bar
     
  • BottomAppBar 底部的Bar

RecyclerView 結構

實作重點

  • 建 Model
  • 讓App 的Icon 第四個剛好出現一點點
  • 在LinearLayout裡新增內容

Item Decoration

Material Design Component

加入Material Design

dependencies {
	implementation 'com.google.android.material:material:1.1.0-beta01'
}
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">

加入Dependencies

修改Style

AndroidX

<androidx.drawerlayout.widget.DrawerLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

https://developer.android.com/jetpack/androidx/migrate/class-mappings

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

下載範例練習

CoordinatorLayout

用來協調子View之間動作的一個Layout

DatePicker

Material Dialog

對話框

Alert Dialog

Simple Dialog

Single Choice Dialog

Multi Choice Dialog

  1. Container
  2. Title (optional)
  3. Supporting text
  4. Buttons
  5. Scrim

圖片來源:https://material.io

Alert Dialog

MaterialAlertDialogBuilder(this)
	.setMessage("確認刪除")
	.setPositiveButton("刪除") { _: DialogInterface?, _: Int ->
	    Toast.makeText(this, "刪除", Toast.LENGTH_SHORT).show()
	}
	.setNegativeButton("取消") { _, _ ->
	    Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show()
	
	}
	.show()

Simple Dialog

val items = arrayOf("選項1", "選項2", "選項3")
MaterialAlertDialogBuilder(this)
    .setTitle("請選擇")
    .setItems(items){ _, i ->
        Toast.makeText(this, "選項: ${items[i]}", Toast.LENGTH_SHORT).show()
    }
    .setCancelable(false)
    .show()

Single Choice Dialog

val items = arrayOf("選項1", "選項2", "選項3")
val checkedItem = 0
MaterialAlertDialogBuilder(this)
    .setTitle("請選擇")
    .setSingleChoiceItems(items, checkedItem) { dialog, which ->
        Toast.makeText(this, "選項 ${items[which]}", Toast.LENGTH_SHORT).show()
    }
    .setPositiveButton("確定") { dialog, which ->
        Toast.makeText(this, "選項:${items[which]}", Toast.LENGTH_SHORT).show()
    }
    .setNegativeButton("取消") { dialog, which ->
        Toast.makeText(this, "選項 cancel", Toast.LENGTH_SHORT).show()
    }
    .show()

Multi Choice Dialog

val items = arrayOf("選項1", "選項2", "選項3")
val checkedItems = booleanArrayOf(true, false, false, false)
MaterialAlertDialogBuilder(this)
    .setTitle("請選擇")
    .setMultiChoiceItems(items, checkedItems) { dialog, which, checked ->
        Toast.makeText(this, "Chose ${items[which]}", Toast.LENGTH_SHORT).show()
    }
    .setPositiveButton("確定") { dialog, which ->
        Toast.makeText(this, "確定 ${items[which]}", Toast.LENGTH_SHORT).show()
    }
    .setNegativeButton("取消") { dialog, which ->
        Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show()
    }
    .show()

Bottom Sheet

由底部由下往上展開的,可以滑動來展開、收合的View

Bottom Sheet

SwipeRefreshLayout

下拉更新資料

Bottom Navigation

Code

<com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:layout_insetEdge="bottom"
        app:menu="@menu/bottom_nav_menu" />
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home" />
    <item
        android:id="@+id/navigation_settings"
        android:icon="@drawable/ic_settings_black_24dp"
        android:title="@string/title_setting" />
    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_notifications" />
</menu>

頁面滑至底部自動載入更多

多筆列表的滑動刪除

與拖拽排序

ItemTouchHelper

 ItemTouchHelper mIth = new ItemTouchHelper(
     new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
         ItemTouchHelper.LEFT) {
         public abstract boolean onMove(RecyclerView recyclerView,
             ViewHolder viewHolder, ViewHolder target) {
             final int fromPos = viewHolder.getAdapterPosition();
             final int toPos = target.getAdapterPosition();
             
             return true;
         }
         public void onSwiped(ViewHolder viewHolder, int direction) {
             // remove from adapter
         }
 });

AndroidX Migrate

範例下載

Chip

Choice Chips 單選

圖片來源:https://material.io

Filter Chips 複選、篩選

圖片來源:https://material.io

Action Chips

圖片來源:https://material.io

Input Chips

圖片來源:https://material.io

Sample

圖片來源:https://material.io

Text Field

種類

<com.google.android.material.textfield.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/hint_text">

  <com.google.android.material.textfield.TextInputEditText
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>

</com.google.android.material.textfield.TextInputLayout>

Text Field組成

1. Container
2. Leading icon (optional)
3. Label text
4. Input text
5. Trailing icon (optional)
6. Activation indicator
7. Helper text (optional)

 Floating action button (FAB) 

來源:https://material.io

FAB

圖片來源:https://material.io

FAB組成

1.Container
2.Icon

Code

<androidx.coordinatorlayout.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">

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="16dp"
            android:src="@drawable/ic_add_black_24dp" />
            
</androidx.coordinatorlayout.widget.CoordinatorLayout>

不適合的情境

圖片來源:https://material.io

不適合的情境

圖片來源:https://material.io

Extended FAB

圖片來源:https://material.io

組成

1.Container
2.Icon (optional)
3.Text label

Code

<androidx.coordinatorlayout.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">

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:text="Add"
        app:icon="@drawable/ic_add_black_24dp" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

不適合的情境

圖片來源:https://material.io

不適合的情境

圖片來源:https://material.io

Snackbar

Snackbar

使用時機

Component 優先權 使用者互動
Snackbar Snackbars會自動消失,可能有互動
Dialog 可強制使用者選擇

Navigation drawer

側邊選單

使用時機

  • 有5個以上 top-level 目的頁面
  • 有2個以上的導覽階層
  • 需要在不相關的頁面快速的切換

Drawer 

Bottom

Navigation

網站來源:https://material.io/

style

<style name="cardStyle" parent="Widget.MaterialComponents.CardView">
    <item name="cardCornerRadius">8dp</item>
    <item name="cardElevation">8dp</item>
    <item name="android:clickable">true</item>
    <item name="rippleColor">#666666</item>
    <item name="android:checkable">true</item>
</style>

屬性

<com.google.android.material.card.MaterialCardView
    app:cardBackgroundColor="#00BCD4"
    app:cardCornerRadius="8dp"
    app:cardElevation="8dp"
    app:rippleColor="#666666"
    android:clickable="true"/>

Material CardView

基於CardView做出來的更多功能的Component

Sliders

圖片來源:https://material.io/

UI_Advance

By evanchen76

UI_Advance

  • 235