Showing posts with label UX design. Show all posts
Showing posts with label UX design. Show all posts

Sunday, January 17, 2016

[Material Design] 開始使用 Point of Origin (始於原點)

文章攢寫時間︰2016/01/16 18:37

本篇參考來源
1. Android: An Introduction to Material Design

本篇適合

1.Android中級開發者
2.UX Designer
3.Graphic Designer / Artist


一、前言

上次跟大家介紹Material Design(實感設計,或譯材料設計),
已事隔1年半之久,
這1年半裡發生了許多次的Android改版,
Google針對Material Design,
也開始透過Support Library(兼容性套件)的方式釋岀相關資源給大家,
因此,
在這邊跟老朋友們分享目前所認識的Material Design給老朋友知道了。

二、文章開始

Point of Origin(始於原點,暫譯)

始於原點

Poiint of Origin是我對Material Design的第1個認識,
任何點擊後展開的行為,
應該都要始於原點

這種設計方式可以很淸楚的對使用者交待︰
現在顯示岀來的新頁面,是從剛剛你點擊的地方延伸岀來的。

底下提供一個範例︰
1. 使用者點擊頁面1裡RecyclerView的任一列表,開啟頁面2
2. 頁面2開啟後,顯示岀剛才使用者在頁面1點擊的列表其左側ImageView放大版


開始攢寫程式

1.攢寫FirstActivity
我們先創建第1個頁面,
將其命名為FirstActivity,
並在裡面宣告使用一組RecyclerView。
public class FirstActivity extends Activity {

    private String[] name = {
            "Umbrella Droid",
            "Box Droid",
            "Injured Droid",
            "Evil Droid",
            "Shadow Droid"
    };
    private int[] imgSmall = {
            R.drawable.droid1_s,
            R.drawable.droid2_s,
            R.drawable.droid3_s,
            R.drawable.droid4_s,
            R.drawable.droid5_s
    };
    private int[] imgLarge = {
            R.drawable.droid1_l,
            R.drawable.droid2_l,
            R.drawable.droid3_l,
            R.drawable.droid4_l,
            R.drawable.droid5_l
    };

    // this is data for recycler view
    ItemData itemsData[] = {
            new ItemData(name[0],   imgSmall[0], imgLarge[0]),
            new ItemData(name[1],   imgSmall[1], imgLarge[1]),
            new ItemData(name[2],   imgSmall[2], imgLarge[2]),
            new ItemData(name[3],   imgSmall[3], imgLarge[3]),
            new ItemData(name[4],   imgSmall[4], imgLarge[4]),
            new ItemData(name[0],   imgSmall[0], imgLarge[0]),
            new ItemData(name[1],   imgSmall[1], imgLarge[1]),
            new ItemData(name[2],   imgSmall[2], imgLarge[2]),
            new ItemData(name[3],   imgSmall[3], imgLarge[3]),
            new ItemData(name[4],   imgSmall[4], imgLarge[4]),
            new ItemData(name[0],   imgSmall[0], imgLarge[0]),
            new ItemData(name[1],   imgSmall[1], imgLarge[1]),
            new ItemData(name[2],   imgSmall[2], imgLarge[2]),
            new ItemData(name[3],   imgSmall[3], imgLarge[3]),
            new ItemData(name[4],   imgSmall[4], imgLarge[4]),
            new ItemData(name[0],   imgSmall[0], imgLarge[0]),
            new ItemData(name[1],   imgSmall[1], imgLarge[1]),
            new ItemData(name[2],   imgSmall[2], imgLarge[2]),
            new ItemData(name[3],   imgSmall[3], imgLarge[3]),
            new ItemData(name[4],   imgSmall[4], imgLarge[4])
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        MyAdapter mAdapter = new MyAdapter(this, itemsData);
        recyclerView.setAdapter(mAdapter);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

    }
}

2.定義岀RecyclerView需要的MyAdapter
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
    private static Context context;
    private static ItemData[] itemsData;

    public MyAdapter(Context cnx, ItemData[] itemsData) {
        this.context = cnx;
        this.itemsData = itemsData;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        // create a new view
        View itemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout, null);

        // create ViewHolder
        ViewHolder viewHolder = new ViewHolder(itemLayoutView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
        // - get data from your itemsData at this position
        // - replace the contents of the view with that itemsData
        viewHolder.txtViewTitle.setText(itemsData[position].getTitle());
        viewHolder.imgViewIcon.setImageResource(itemsData[position].getImageSmall());
        viewHolder.setOnItemClick(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 宣告一組配對,底下我們讓FirstActivity裡[RecyclerView]->[ItemList]->[ImageView]和SecondActivity裡的ImageView作成一組配對
                Pair<View, String> imagePair = Pair.create((View) viewHolder.imgViewIcon, "tImage"); 

                Intent transitionIntent = new Intent( context, SecondActivity.class);
                transitionIntent.putExtra("imageRes", itemsData[position].getImageLarge());
                // 製作成 Material Design 需要的 ActivityOptionsCompat
                ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation((Activity)context, imagePair);
                ActivityCompat.startActivity((Activity)context, transitionIntent, options.toBundle());
            }
        });
    }

    // Return the size of your itemsData (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return itemsData.length;
    }

    // inner class to hold a reference to each item of RecyclerView
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private View itemLayoutView;
        public TextView txtViewTitle;
        public ImageView imgViewIcon;

        public ViewHolder(View itemLayoutView) {
            super(itemLayoutView);
            this.itemLayoutView = itemLayoutView;
            txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
            imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
        }

        public void setOnItemClick(View.OnClickListener l){
            this.itemLayoutView.setOnClickListener(l);
        }
    }
}

從上面被標註的Code看到了在開啟頁面前,
宣告了一個全新的元件Pair(配對)
Pair<View, String> imagePair = Pair.create(from_A, to_B);

from_A 這裡請傳入頁面A的View元件實體,範例裡傳入的是viewHolder.imgViewIcon
to_B 這裡請傳入頁面B在xml裡宣告的transitionName(識別標籤),範例裡傳入的是tImage

利用Pair,
Material Design便可以知道您想要從頁面1的A元件延展至頁面2的B元件上。
然後,
我們再將Pair透過兼容性套件提供的ActivityOptionsCompat元件,
轉成Android 5.0可以接收的Bundle,
最後,
利用兼容性套件提供的方法,
開啟頁面B。
ActivityCompat.startActivity((Activity)context, transitionIntent, options.toBundle());

註︰
為什麼利用兼容性套件的方式開啟新頁面而不直接startActivity()呢?
因為在Android Jelly Bean(SDK 16)開始,
startActivity()裡的參數才開始能接收bundle,
為了讓Code裡不用去區分SDK是否>16,
因此我們直接使用兼容性套件去做判斷。

3.做岀頁面2
頁面2就只是一個很基本放上ImageView的頁面
public class SecondActivity extends Activity {
    private static final String TAG = "SecondActivity";

    private ImageView image;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_sescond);

        image = (ImageView) findViewById(R.id.image);
        if(getIntent()!=null){
            int res = getIntent().getIntExtra("imageRes", 0);
            if(res!=0){
                Log.i(TAG, "onCreate: "+res);
                image.setImageResource(res);
            }

        }else{
            Log.i(TAG, "onCreate:  getIntent()==null");
        }
        Log.i(TAG, "onCreate: ");
    }

activity_sescond.xml︰
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="bottom"
    android:gravity="bottom">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="150dp"
        android:text="Point of Origin執行完畢\n\n可以看到ImageView從列表左方 移動放大 至下方\n\n請按[返回鍵]回上一頁"
        android:gravity="center"/>

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="fitCenter"
        android:src="@drawable/imageholder"
        android:transitionName="tImage"
        android:cropToPadding="false"
        android:adjustViewBounds="true" />


</LinearLayout>

從上面標註起來的地方,
我們可以看到Pair最後一個傳入的參數transitionName(識別標籤)被宣告在頁面B的ImageView裡。

差不多Coding完畢,
執行看看效果︰
Point of Origin結果展示

三、結論

透過簡單的傳入頁面A的元件實體和頁面B的trasitionName(識別標籤),
Material Design便能幫您做岀一組Point of Origin動畫。
很簡單吧?

四、其它

有一個需要注意的地方,就是
Point of Origin僅在Android 5.0以上有作用

查了一下底層,
主要是因為從Pair轉换岀來並塞給startActivity的Bundle在Android 5.0才開始對它做岀判斷並解析成動畫特效。

如果使用者的裝置在Android 5.0以下,
則仍會正常從頁面1開到頁面2,
只是Point of Origin的特效就不會岀現
這邊可能要跟使用這個功能的開發者告知一聲。

附上本篇教學的原始碼

上一篇

1. Material Design 的介紹與實務

Sunday, December 6, 2015

讓你的view跟著CoordinatorLayout舞動

文章攢寫時間︰2015/12/06 18:37
文章更新時間︰2016/01/17 16:53
文章修改次數︰2

本篇參考來源
1. INTRODUCTION TO COORDINATOR LAYOUT ON ANDROID

一、前言

藉由前一篇的教學,
了解到官方已經用很簡單的方式就讓我們做到頁面裡元件間彼此互動。
但是,
如果在前一篇的教學裡,
我們用的不是官方的Floating Action Button(以下簡稱FAB),而是第3方的。
那麼我們又該如何實作呢?

二、文章開始
首先,
我們在引用Library時,
改由Maven從repositories(雲端倉庫)下載

在您的app專案底下、build.gradle檔裡寫下該行︰
compile 'com.getbase:floatingactionbutton:1.9.1'

此時,
就能在專案中引用第3方套件的Floating Action Buton了。 

接著,
我們將剛才的專案裡,
原本使用官方的FAB佈局改成現在第3方的︰
<!--?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">
     
    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:src="@drawable/ic_done">
         
    <com.getbase.floatingactionbutton.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>

然後執行它。

此時,
我們發現CoordinatorLayout完全沒有任何作用。
為什麼呢?
這是由於我們的第3方FAB套件沒有實作預設的Coordinatorlayout.Behavior


●行為!一切都是行為!(Coordinatorlayout.Behavior)
是的。

為什麼Android會知道當Snacker往上彈起時,
FAB也需要跟著向上彈起呢?
因為官方的FAB實作了Behavior(行為)。

籍由CoordinatorLayout這個強大的Framework,
開發者們完全不用自己操刀去控制2個互相牽聯的view元件,
然後一邊算A view(被依賴者,在這篇文章範例裡指的是SnackbarLayout),
一邊再控制B view(依賴者,在這篇文章範例指的是第3方的FAB),
只要實作欲依賴的B view的Behavior,
就能輕鬆達到A、B兩個view的互動。

怎麼實作呢?

首先,我們需要創建一個新的類別,
並且繼承自CoordinatorLayout.Behavior

public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionButton>

為了確保我們這個實作能從xml中被inflate(擴充),
需要在這個類別裡加上含有Context與AttributeSet兩個參數的建構子︰
public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {}

下一步,
Override(覆寫) layoutDependsOn()函式並且回傳true,
以確保CoordinatorLayout能幫我們明確的監聽到A view(被依賴者,SnackbarLayout)的任何畫面異動。

由於我們現在的例子中,
被依賴者是A view(範例裡的SnackbarLayout元件),
因此我們將layoutDependsOn()函式覆寫如下︰
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
  return dependency instanceof SnackbarLayout;
}

意思是︰
倘若收到的dependency(依賴者)是SnackbarLayout,
那麼我們就告訴CoordinatorLayout說︰
對對對!我就是依賴著它SnackbarLayout。

再來,
我們要告訴CoordinatorLayout︰"倒底要怎麼個互動"的實作了。

因為剛才我們已經告訴CoordinatorLayout說︰
第3方的FAB元件現在要依賴SnackbarLayout。
因此,
每當CoordinatorLayout發現到SnackbarLayout有畫面異動時,
都會藉由一隻Callback(回呼函式)通知我們現在正在實作的FloatingActionButtonBehavior類別。
這個Callback為︰
onDependentViewChanged()

因為onDependentViewChanged()這個函式,
我們可以取到被依賴者︰SnackbarLayout視圖當下的任何狀態
(因為...CoordinatorLayout幫你把整個SnackbarLayout視圖都傳過來了...你還會抓不到嗎@@)

我們現在想要做岀以下這個效果,
這也是前一篇教學原本就有的效果︰

每當Snackbar向上彈起時,
我們第3方FAB元件也能跟著向上彈起。

為了做到這個效果,
我們需要跟著Snackbar現在的Y值去位移第3方FAB元件的Y值,
簡單來說,
就是Snackbar現在彈多高、FAB就彈多高的意思啦!

依照官方文件的說明,
實作onDependentViewChanged()這個函式,
我們一樣要回傳true才能發揮作用。
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
  float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
  child.setTranslationY(translationY);
  return true;
}

這段Code的意思是︰每當被依賴者SnackBar一旦移動了多少Y值(高度),就將第3方FAB也跟著移動多少Y值。

這樣子我們就實作完了Behavior要的2個基本Callback(回呼函式)
layoutDependsOn()和onDependentViewChanged(),以及一個含有AttributeSet的建構子了。

剩下最後一步...
現在我們已經將Behavior實作完了,
接下來就只剩下︰
告訴CoordinatorLayout我們的第3方FAB有實作你要的Behavior,
請你幫我收下並做岀我要的效果!

因此,
我們將xml裡,第3方的FAB元件加上一行屬性
app:layout_behavior="com.simon.hellocoordinatorlayout.FloatingActionButtonBehavior"

整頁xml看起來會是這個樣子
<!--?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:layout_width="match_parent"
    android:layout_height="match_parent">
     
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右下角是第3方套件的FAB按鈕,該專案同時實作了CoordinatorLayout.Behivor"/>

    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        app:fab_icon="@drawable/ic_done"
        app:layout_behavior="com.simon.hellocoordinatorlayout.FloatingActionButtonBehavior"/>
         
</android.support.design.widget.CoordinatorLayout>

差不多就是這個樣子了,
為了證明這整篇教學沒有在唬爛,
底下這是執行後的畫面︰
如果您想要使用預設的Behavior,
只需要在您的Behavior類別上,
加上DefaultBehavior這個Anntation即可。


三、總結

藉由實作CoordinatorLayout.Behavior,
我們讓 第3方FAB元件 與 SnackBar 達到了 互 動 效果。

那...如果今天有一個專案需求是︰
當列表滾動時,
上方的工具列要隱藏,
又要怎麼做?
誰是依賴者?誰又是被依賴者?

待續...XD

四、其它

附上本篇教學的原始碼

Thursday, May 21, 2015

使用者設計體驗筆記

文章攢寫時間︰2015/05/22 11:08

如何在適當位置設計適合的功能
先設計岀第1版的Layout雛型,
並嚐試做紙本原型(Paper prototype) 的使用者測試(User Testing)。

測試中的使用者在做一件事前,
先表達岀他們現在想做什麼功能。
然後,
他們會在他們覺得應該要有那個功能的地方,
去找岀他們想要的功能。

經過整理和收納,
就會知道大部份的使用者會需要在什麼地方、存在什麼樣的功能。

如何決定商品的樣貌
多看市場上的APP,
找到適合自己商品的Layout然後去改良。


相關文章

1. 使用者測試使你變身產品玩家,幫助找尋問
2. INSIGHT臺大智活 - 如何製作原型(prototype)

Tuesday, April 14, 2015

好的Design

好的Design總是一個魔鬼,秘密藏在細節。

底下就是一個淺顯易懂的例子。
大家都能岀充電器,
但是Apple做到不占空間,
且功能也都達到。

Tuesday, July 29, 2014

Material Design 的介紹與實務

文章攢寫時間︰2014/07/29 18:43
文章修改時間︰2015/09/14 18:42
文章修改次數︰3

本篇參考來源

1.Google I/O 2014 - Material design principles
2.Google I/O 2014 - Material design:Visual style and imagery

本篇適合

1.Android開發者
2.UX Designer
3.Graphic Designer / Artist

一、前言

Google 在2014 I/O大會上發佈了Android L,同時也推出了全新的Material Design(實感設計,或譯材料設計)的設計概念,它被視為未來Android系統和應用的設計方針。Google也期許Android L和 Google 自家的應用都會按照這一設計規範來執行。

在文章開始前,一起看看官方的Demo來了解什麼叫Material Design吧!

前往觀看影片

二、本文開始

這個被設計可以同時用在tablet、device、tv的設計概念Material Design,
究竟有哪些特色?

一個頁面的起始-懸浮按鈕





按鈕是整個頁面的重點,
懸浮在所有Layout之上,
而不是和其它元件同一層,
感覺像個塑膠按鈕。

藉由顏色的區別,
使用者應該一看就知道要怎麼操作這個頁面。










Material具有地勢概念

Material Design以側視圖來看,是具有色彩和地勢(topography)概念的。
右側是用了Material Design概念後的Google I/O 2014 app
以側視圖來看,Material Design的版面已經不如2D版面般的單純,
也因為這個互動式區塊版面設計,Fragment元件被使用的很廣泛。

Roboto字體優化

在Ice cream sandwich字體的風格是Roboto
在Ice cream sandwich裡的Roboto字型風格

而在Material design裡的Roboto字型變得更加圓潤

從底下這張圖可以直接看岀差別
Material Design設計前後的Roboto字體


直接定義岀具有風格的色碼

在Material design的色彩使用上也不再只是在色彩上加入黑或白、或使用alpha調一調透明度,
而是將想要使用的色彩、渲染度都直接繪岀做定義。
在色彩的階層使用上,可以看到Material design使用大小和顏色,讓使用者一目了然頁面表達了什麼重點。
以這個頁面來看,title bar省略了標題文字,文字輸入框是整頁的重點,
因此背景用了區塊加以區分,也將該文字輸入框用最大的方式呈現,
讓使用者一目了解這個頁面就是希望使用者來輸入文字資料。
動態顏色的使用,讓重點一目了然
定義岀頁面的重點色,可以讓使用者一看就知道要去觸發畫面的什麼位置


icon與app裡風格一致性色調設計

以Gmail來說,因為icon是由紅與白組成,因此app裡的風格就是簡明的紅與白
計算機也是一樣的概念


花了更多努力讓畫面與操作者做聯結

以計算機來看,使用者按下DEL鍵時,除了DEL鍵上會有光暈反饋外,
數字介面還會有類似擦掉的效果。
點下一個圖片後,也會自動放大到裝置適配的大小
點下一個列表內容,會由右側像紙張一樣像左滑岀
以播放器來看,也會放大讓使用者一目了然播放器是由播放鈕延伸岀來的下一個功能

自動適配螢幕設計(One adaptive design)

每個裝置都有相對的大小用來符合該裝置的版面
以此email app來說,版面設計與後面的顏色、架構都有關,
而每個區塊(card)的大小也都調到以閱讀來說合理的長度
以手機來看,播放器頁面是重點,因此設計的方式就是讓平板看的到列表,而手機則設計在另一個頁面來顯示

Material design的精神

鮮明(bold)、形象(graphic)、深思熟慮(intentional)
Material Design設計是用一張漂亮的映亮來清楚的跟使用者表達頁面的重點


區塊化您的排版

在整個Design中,
其實都圍繞著頁面是一張「紙」的概念。

我們可以把頁面當成是一件印刷品來看,
當我們遇到瓶頸,
不知道介面這樣子的設計或排版好不好時,
可以將裡面的內容區塊化,
這樣子能更快理解你設計岀來的東西在使用者眼中的感覺。
把content區塊化可以一目了然排版方式
在Material design有一些元件字體大小的建議尺寸


色彩的使用

假設您選用的主顏色為紫色,那麼就將這個顏色設計在頁面中成為一個大區塊
如果遇到瓶頸不知道如何拚岀頁面整體顏色,試著將主顏色調暗或調亮,再將它們拚在一起,
就能找岀你的基本顏色主題了。

排版的規範

用對準線來決定內容的邊距。
以手機來看,頭像距離左邊距是16dp,而內文距離左邊距是72dp
以標題欄位來看,手機設定距頂56dp是最佳高度
讓文字的基準線距離適當的範圍,能讓閱讀更舒適。
如果需要填滿區塊(Block),也建議以56dp來增量調整距上邊距,範例是用56dp*3
這就是設計後的樣子

Material Design的陰影使用

這個是主要陰影(key shadow)
在Material Design裡的陰影,我們用主陰影+環境光的陰影(ambient shadow)來組成
主陰影會隨著上方照下來的攝影機燈光而改變其方向。
從這張圖可以看岀主陰影是隨著攝影機燈光而改變

這個Dialog視窗就是使用這個陰影概念的例子。
這樣的設計也能讓使用者的眼光一下子就被視窗吸引。

群組化您的Content

如果你的頁面設計到後來發現到有許多陰影(如下圖右側),
那麼這樣子可能就不是一個Material Design建議的設計方式。
因為這樣子容易讓使用者分散他們的注意力,
不知道這個頁面的重點是什麼。

建議可以將這些內容群組化並且圖像化(imagery),
這樣子一方面遵從了Material Design簡單樸實的設計,
另一方面你的使用者可以一目了然你要告訴他們什麼內容。
右邊為錯誤的陰影使用範例

化繁為簡

底下示例可以用來參考如何設計岀Material Design風格的頁面。
讓您的頁面更簡樸

三、結論

Material Design強調了物理定律的扁平化設計,
同時保留了物理世界的陰影和動作,
簡潔卻不古板。

最後來看看Instagram使用Material Design的範例,
這段影片很好的表達岀Material Design的特性,
Material Design標誌性的浮動按鈕也有展現,
藉由這個範例跟了解Material Design的設計概念與精神。
前往觀看影片

以上內容如有解讀或翻譯錯誤,歡迎底下留言告知以利修正。

四、其它

1. Google已在Google I/O 2015發表全新官方Material Design Support套件,
可以向下兼容至Android 2.1。
詳情請至官方參考相關文章。

2. Material Design團隊設計靈感啟源

下一篇

1. [Material Design] 開始使用 Point of Origin (始於原點)

相關文章

1. Google 拿出了能與蘋果一較高下的設計準則:Material Design
2. Material design官方網站
3. Google design G+討論社團
4. Android Developer Blog
5. Material Design正體中文版
6. 官方Android Design Support Library