2015/4/27

Unicode Surrogate Pairs

特殊的中文字

這個字是個特別的中文字,它沒辦法在一般最通用的 UTF-8 網頁中被看見,也沒辦法儲存到一個定義為 UTF-8 encoding 的 MySQL Database 裡面。這個字在網頁上看不見,所以這篇文章的這個字都是用圖片貼上去的。

講到 Unicode 的問題,又需要去查看一串字元編碼歷史,淺釋 Unicode 這篇文章把字元編碼的來龍去脈講得很清楚了,就不在這邊贅述。

如果要完全解決文字編碼的問題,其實就是使用 UTF-32 或是 UTF-16 就好了,但是現在討論一個重點,為什麼 UTF-8 在網頁世界中,會是最流行的編碼方式,原因就是大部分的作業系統與系統程式都是英美語系的工程師做的,為了跟 ASCII code 相容,如果也能夠使用一樣的程式就處理掉多國語言編碼,這樣就天下太平了,程式不需要改太多。

像我們這樣撰寫網頁程式的工程師,其實早已經習慣撰寫網頁,就使用 UTF-8 編碼,一般也不會想到會遇到超過 UTF-8 規範定義的字元,如果使用者硬是在網頁上輸入了 這個字,對於 Java 來說,他是可以接受的,因為 Java 語言內部的編碼格式是 UTF-16,但對於資料庫來說,當我們要把資料放進 table 時,得到的卻是一個 SQLException。

SQL Error: 1366, SQLState: 22001

使用 UTF-8,調整 DB 設定也沒用

遇到 SQLExcpetion 的問題,很直覺會先想到是不是資料庫的編碼寫錯了,還是 MySQL J-Connector 出了什麼問題,連線時忘了設定 encoding。

但因為根本的原因在於 Java 可以容許使用 UTF-16 的字,但是 MySQL Database 以及 Browser 的網頁都是使用 UTF-8,所以不管我們再怎麼改設定,或是想方法,其實都是徒勞無功的。

Surrogate Pair

再看一下這一段 java 程式,雖然鍵入了四個中文字的字串,但實際上,卻因為第三個字有著不同的特性,最後輸出時,就把第三個字排除掉了。

Java 的 Character.isHighSurrogate 與 Character.isLowSurrogate 可以用來判斷是不是符合了上面描述的規則的特殊字。

        String text = "測試字";

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < text.length(); i++) {
            char ch = text.charAt(i);
            System.out.println("ch="+ch+":"+String.format("%02X", (int)ch)+"\n");
            if (!Character.isHighSurrogate(ch) && !Character.isLowSurrogate(ch)) {
                sb.append(ch);
            }
        }

        System.out.println("sb=" + sb.toString());

執行後輸出結果為

D:\temp>java Test4
ch=測:6E2C

ch=試:8A66

ch=?:D85D

ch=?:DE57

ch=字:5B57

sb=測試字

除了 這個字之外,其他的字都是 2 bytes,Java 內部的 UTF-16 會使用一個單位(2 byte)儲存 UCS-2 字碼,超過這個範圍的字碼則會拆解成「代理對(surrogate pair)」,用4 bytes儲存。

解決方案

  1. 忽略這個問題,畢竟遇到這些特殊字的機會並不多
  2. 儲存到 DB 前,使用上面的 isHighSurrogate 與 isLowSurrogate,將不合法的字元排除掉。
  3. 將 DB 與 網頁輸出都改為 UTF-16,但是 IE 沒有支援 UTF-16,只能用在 Chrome,不建議這樣做,因為 UTF-8 才是網頁的主流。

Reference

UTF-8 wiki

How to handle SQL state [HY000]; error code [1366]; Incorrect string value?

What is a surrogate pair in Java?

2015/4/20

Persona 面具 人物誌 目標用戶 Target Audience

Persona 這個字源自於希臘文,原意是「面具」的意思。當我們使用這個方法時,等於戴上了該使用者的面具,想像自己就是這個使用者。

當我們要創造新的產品或服務時,必須去理解使用者與他們的需求。當我們戴上了面具,站在使用者的立場去思考,可以發現使用者想要甚麼,使用者如何使用新產品的想法,而這個新產品到底應該是甚麼模樣?

Persona 的重點在於,幫助研發者以使用者的角度來思考,避免將自己當作使用者,來考慮系統設計,造成盲點。

Persona 的發明人:Alan Cooper

Alan Cooper於1983年開始使用「人物誌」(persona),當時他正進行電腦程式設計的「超級專案」(Super Project),之後成為微軟的視覺基礎(Visual Basic)程式。Cooper發覺產品設計中使用者同理心(user empathy)的重要性,並開始著手一連串在假設性產品(hypothetical product)與人物(persona)之間的行動對話。90年代中期,他開始將「人物」與「故事板」(storyboard)當作對客戶報告的主角。

Cooper 認為產品之所以令使用者卻步,原因並不是技術問題,而是介面設計的缺陷。許多軟體的介面設計不夠貼近使用者的需求和習慣,而是認為使用者該「學習」如何操作介面。因此他以「互動設計」(interaction design)取代慣稱的「介面設計」(interface design),並指出介面設計與互動設計之不同在於,介面設計是讓產品看起來很好,但互動設計則是從使用者的需求出發,而運用人物誌將是通往成功介面/互動設計的取徑。

Persona 的目的

  1. 以代表性人物濃縮特定使用者族群的資訊

  2. 在開發團隊中建立共同的使用者形象標準,避免因個人理解差異,造成目標用戶的誤解

  3. 提醒開發人員以 persona 為核心,進行產品開發

產生 Persona 的方法

人物誌(Persona)研究方法 文章中,透過小說、心理學等等不同的人物研究的角度,討論該如何建構一個正確的 persona。

以下描述小說寫作的方法:

在小說寫作中,角色描寫的要素有以下5點,分別是外觀、心理、背景、情感態度、個人特色:

  1. 外觀(Body)
    長相與身材、角色的動作型態、姿勢與穿著服裝

  2. 心理( Psyche)
    角色最底層的慾望和恐懼。心理面是建造生動角色的骨幹,掌握角色形成的核心,就能理解他們對人生和環境的整體態度。

  3. 背景(Background)
    不僅止是傳統的人口地理學的分類法,而是完整構建出角色的社會性特徵。

  4. 情感與態度(Emotions and attitudes)
    細緻的去談角色的嗜好和習慣,分析角色對生活周遭、器物、知識等等的觀感。

  5. 個人特色(Personal traits)
    在小說中,人物設計依角色發展完整度分成兩種,扁平角色和成熟角色。扁平角色(flat character)只具有一個或少數的角色特質,角色性格十分固定。成熟角色(rounded character)則擁有複雜多重的角色特質,行動難以預測,角色的層次越多越複雜,越令觀者好奇,進而對角色產生高涉入度,建立角色逼真性。

Lene Nielsen

Lene Nielsen (2007)提出發展人物誌的三個重要要素,可供設計者檢視:

  1. 資料蒐集:找到明確的產品使用者與建立產品假設,並提供後續驗證角色與產品設定的可靠數據與證明。

  2. 投入人物誌描述: 人物誌運用的主要目的不在於描繪出角色,而是以使用者需求為起點,創造解決方案。

  3. 組織接受(buy-in from the organization):確保所有的參與者都同意角色描寫和情境的兩個策略:詢問所有人的意見,並讓他們參與設計過程。

Persona 的十個步驟

Persona的十個步驟 這篇文章,將 Persona 的方法,切割為十項步驟。

  1. Find the Users 找出使用者

  2. Building a Hypothesis 建立假設
    將使用者分類,並對不同的團體命名

  3. Verification 驗證
    找出人物的基本資料,喜好、價值觀、工作領域、工作目標等等

  4. Finding Patterns 找出模式
    檢視使用者分類是否正確,是否要加入其他使用者團體

  5. Constructing Personas 建構人物誌
    (1)身體(名字、年齡、照片)
    (2)心理狀態(內向的人、外向的人)
    (3)背景(職業)
    (4)感情與態度(對科技的傾向、公司(傳遞者)、資訊)
    (5)個人特質

  6. Defining Situations 定義情境
    定義人物的需求,使用的情境

  7. Validation and Buy-in 確認與採納
    有沒有認識什麼人跟此人物誌的人相同,驗證先前的人物誌定義與情境是否正確

  8. Dissemination 知識的推播
    利用海報、會議、電子郵件等等方式,在組織當中分享人物誌的研究成果

  9. Creating Scenario 創作劇本
    既有環境下以現成目標來討論當人物誌這個角色使用了新技術後會發生甚麼事?製作劇本、使用案例、需求條件、詳細訂定規格。

  10. On-going Development 持續進展
    持續進行使用性測試、 收集新資料、所有關於與使用者互動的回饋意見。

http://www.hceye.org/downloads/Persona-Lene.jpg

新創事業使用 Persona 的方法

Persona 定義和運用 / What is persona & How to use? (視覺資料圖/Infographic)

針對每一類型的代表使用者,所創造出來的背景敘述。
Profile description created for each represented type of users

製作有效的Persona的最好方法,就是直接和一百位潛在的使用者不斷的瞭解現況: 包含面臨到的問題(problem statement)、目前的解決方案 (current solution)、情境(Context)、為何問題無法解決的阻礙因素(reason)、可能的解決方案(proposed solution)等相關資訊。 在這個學習和資料整理的過程,往往我們一開始PERSONA的定義,也會隨者面談人數的增加,讓我們更能辨識出真正的使用者。

  1. build multiple persona ASAP
  2. unstand context and concurrent solutions
  3. via MVP minimal viable product,validate business model
  4. ifentify eraly supporter,generate sales leads and sales

Persona 的優點與缺點

這幾篇文章,討論了 Persona 的優缺點
Persona典型用户
让人物角色站到你面前
人物誌 (Persona)

優點

  1. 明確的設計對象
  2. 幫助專注於設計目標
  3. 避免自參照設計(把自己當作 User)
  4. 避免邊緣設計,避免為不是設計目標的使用者做設計,產品不是拼裝車,設計是為了某一群人而做的,千萬不要企圖將產品設計成每一個人都可以用,通用不一定好用。
  5. 可以用在設計的每一個階段
    設計初期可以用在假設與驗證,驗證到底設計目標存不存在,設計方向有沒有偏差,避免設計出沒人要的產品。設計中期可以用來當作保持設計方向的工具。設計後期可用 Persona 來驗證產品到底有沒有符合需求。

缺點

  1. 需要花時間做研究,因為你需要招募使用者、組織一個訪談、然後提煉 Persona,這些都有時間與金錢成本

  2. 做出Shit Persona

    • persona 不像是一個真實的人物
    • persona 是不具有代表性的人物
    • 開發團隊不知道如何從此 persona 取得有價值的資訊,沒辦法將自己映射到 persona,藉此找出產品需求
    • persona 沒有涵蓋到

遇到不在既定義範圍的新客戶時該怎麼辦?

  1. 把新的客戶加入到客戶範圍
    新的使用者特性,會影響原本的使用者互動設計,如果偏離初始設計太多,可能會造成系統設計的混亂。但如果是些許的功能調整,增加使用者的base,對系統來說絕對是有相當正面的幫助。

  2. 排除此客戶
    如果不能加入到使用者的範圍,當然就只能選擇放棄。

將 Agile 的精神帶入 Persona

既有的 Persona 方法論,為了避免製作出來的 Persona 不具有代表性,或產生了上面的一些缺點,傾向於花更多時間,做前期的討論,做市場問卷調查,訪問更多的人(100 人),事實上,花越多時間做出來的 persona,雖然正確率會提高,但真的發生問題,產生錯誤的 persona 時,也更有可能會因為團隊前期過多的投資成本,而無法承認錯誤。

Agile 的精神就是透過快速的 iteration 週期變化,不斷地重新檢視整個開發過程中的問題,快速的反應與打擊。Persona 是用來定義使用者的範圍,也就是在定義問題的範圍,不斷地重複進行 persona 的驗證,也等同於整個開發團隊在不斷地調整自己、詢問自己,是不是真的了解了使用者,產品的目的與想要解決的問題,是不是真的存在。

References

Alan Cooper
人物誌 (用戶體驗))

給我「有感」的Persona

[設計與教學] 科技創新產品開發使用的Persona方法

Personas: Dead yet?

Love, Hate, and Empathy: Why We Still Need Personas

2015/4/13

應付 NullPointerException 的 java.util.Optional

因為不可預期的 NullPointerException 而造成系統 crash,這應該是所有寫程式的人都曾經遇到的情況,從資料的狀態來看,null 確實是一種特殊的存在狀態,也必須區分開來,但偏偏 Java 無法直接使用 null 物件,因為會產生 NullPointerException,接下來就討論一下 null 物件跟應對的方法。

Boundary Test

在測試理論的 boundary test 中,我們要注意資料、狀態變化與一些功能的特殊狀況,測試並檢驗程式會不會出錯。在 Java 中最常見的會引發系統 crash 的錯誤,就是 NullPointerException,系統會在遇到資料為 null 的情況下,發生不可預期的錯誤,就因為程式設計師不認為這個資料會是 null,而一旦存取到了一個 null 的物件,就會造成 NullPointerException 而中斷整個程式,讓系統 offline。

null 是不是一個正確值

程式設計師在一般的 Java 物件時,通常就會注意到這個物件有沒有被初始化,那怎麼還會遇到 NullPointerException 呢?

最重要的原因就是 String 這個最常見的物件,再加上初始化 String 物件有 syntax sugar,在撰寫程式時,通常 String 變數會存放在客製物件中,也常常不會發覺這個字串變數,到底有沒有初始化,只知道在 Data Bean 裡面 寫上 get/set method 而已。

null 對一個物件來說,也是一種正常的狀態,而這個狀態,代表的是這個物件所儲存的數值,還沒有被初始化。

null 跟 空字串 是不同的

不管是在資料庫或是在 Java 裡面,null 跟 空字串 都是不同的,也有不同的意義,null 基本上是一個標記,不暫用到任何記憶體或實體的空間。

而空字串 "" 就不同了,因為空字串是個正常的字串,長度為 0 的字串,在 Java 的字串 instance 中,就等於已經產生了一個長度為 0 的字串物件 instance,實際上是有佔用到記憶體的。

java.util.Optional

Java 8 提供一個新的 class java.util.Optional,可以封裝一個物件,並提供方法做 null 的檢測。基本上,所有可能會 return null 物件的 method 都應該改為 return Optional

建立 Optional Object

  • Optional empty()
    產生 empty Optional

  • Optional of(T value)
    產生 T value 的 Optional object,但如果 value 是 null,就會丟出 NullPointerException

  • Optional ofNullable(T value)
    產生 T value 的 Optional object,但如果 value 是 null,就會回傳 empty Optional

使用範例

    Optional<String> empty  = Optional.empty();
    System.out.println(empty);

    Optional<String> str = Optional.of("custom string");
    System.out.println(str);

    String nullableString = null; 
    Optional<String> str2  = Optional.ofNullable(nullableString);
    System.out.println(str2);

ifPresent, isPresent

# 檢查此 Optional 物件是不是 null
public boolean isPresent()

# 這是用 Lambda 語法,當 Optional 為 empty 時,就不執行後面的 Consumer。
public void ifPresent(Consumer<? super T> consumer)

使用的範例如下

Optional<String> str = Optional.ofNullable(null);
str.ifPresent(value -> System.out.println("Optional contains " + value));

if (str.isPresent()) {
    String value = str.get();
    System.out.println("Optional contains " + value);
} else {
    System.out.println("Optional is empty.");
}

Optional<String> str2 = Optional.ofNullable("custom string");
str2.ifPresent(value -> System.out.println("Optional contains " + value));

執行結果,只會看到有2行輸出

Optional is empty.
Optional contains custom string

get, orElse, orElseGet, orElseThrow, ifElse

# 取得 Optional 裡面存放的物件,但如果 Optional 物件是 null,就 throw NoSuchElementException
public T get()

# 取得 Optional 裡面存放的物件,但如果 Optional 物件是 null,就 return 物件 other
public T orElse(T other)

# 取得 Optional 裡面存放的物件,但如果 Optional 物件是 null,就呼叫 other 這個 supplier 的 get 取得下一個物件
public T orElseGet(Supplier<? extends T> other)

# 取得 Optional 裡面存放的物件,但如果 Optional 物件是 null,就呼叫 exceptionSupplier 的 get 產生的 Throwable Exception,例如  IllegalStateException::new
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X extends Throwable

使用範例

String defaultString = "default string";
Optional<String> optionalString = Optional.empty();
String resultString = optionalString.orElse(defaultString);
System.out.println("resultString: " + resultString);

optionalString = Optional.ofNullable("custom string");
resultString = optionalString.orElse(defaultString);
System.out.println("resultString: " + resultString);

optionalString = Optional.ofNullable(null);
optionalString.orElseThrow(IllegalStateException::new);

執行結果

resultString: default string
resultString: custom string
Exception in thread "main" java.lang.IllegalStateException
    at Main$$Lambda$1/8460669.get(Unknown Source)
    at java.util.Optional.orElseThrow(Optional.java:290)
    at Main.main(Main.java:44)

filtering and mapping with Lambdas

# 如果 Optional 裡面的 value 存在,且符合 predicate 的條件限制,就回傳這個 value,否則就回傳 empty Optional
public Optional<T> filter(Predicate<? super T> predicate)

# 如果 Optional 裡面的 value 存在,就執行 mapper function,如果 value 為 null,就回傳 empty Optional
public <U> Optional<U> map(Function<? super T,? extends U> mapper)

使用範例

Optional<Integer> emptyNumber = Optional.empty();
emptyNumber.filter(x -> x == 250).ifPresent(
        x -> System.out.println(x + " is ok!"));

Optional<Integer> cardNumber = Optional.of(new Integer(300));
cardNumber.filter(x -> x == 200).ifPresent(
        x -> System.out.println(x + " is ok!"));

cardNumber = Optional.of(new Integer(200));
cardNumber.filter(x -> x == 200).ifPresent(
        x -> System.out.println(x + " is ok!"));

執行結果

200 is ok!

IntStream 的 max 與 min 回傳了 OptionalInt

在 IntStream 裡面,max 以及 min 的回傳資料都是 OptionalInt,這是在提取一堆 Int 裡面的最大值,而這個最大值,當然有可能會因為給予的 IntStream 本身就不存在,導致根本找不到最大值而得到不存在的結果,為了描述這個「合理」的不存在的結果,就套用了 Optional 封裝這個結果。

使用範例

OptionalInt maxOdd = IntStream.of(10, 20, 30).filter(n -> n % 2 == 1).max();
if (maxOdd.isPresent()) {
    int value = maxOdd.getAsInt();
    System.out.println("Maximum odd integer is " + value);
} else {
    System.out.println("Stream is empty.");
}

執行結果

Stream is empty.

真的會使用 Optional 嗎?

關於這個問題,我的看法是:

  • 如果 null 是邏輯上正確的狀態,那就使用 Optional。
  • 如果有個物件變數,但不確定會不會是 null,要避開產生 NullPointerException,就用 Optional.ofNullable(object).orElse(defaultValue); 確保在遇到 null 的時候,會有預設值
  • 如果不應該發生 null,那就直接把初始值放進去就好了,不需要再用 Optional 封裝起來。

或許一般的程式設計中,不需要考慮到這個問題,畢竟如果在 javadoc 裡面看到一堆 method,回傳的物件是 Optional,使用起來也會產生一些困擾,畢竟不是 CustomClass 的物件,沒辦法在收到回傳物件後,就直接使用該物件的 method。

在沒有 Optional 之前,我們也可以使用 if( object==null ) 的方式去檢查是不是 null,用 object==null ? defaultValue : realValue 的方式,為物件設定預設值。

但也不需要排斥或拒絕使用 Optional,他等於是用了一個物件的方式,包裝 null 的狀態下的處理過程,因此就把 Optional 當作是一個習慣的寫法就好了。

重要的是程式設計師要注意 null

不管語言或語法的問題,就程式的邏輯來看,在設計程式語言時,就必須明確地將 null 與 "" 區分開來,因為他們本質上就是不一樣的兩個概念。

最重要的是程式設計師對 null 的認知,寫程式的時候,必須隨時意識到這個變數會不會有 null 的狀況,必須運用程式語言既有的機制,對自己的程式做出負責任的處理。

換句話說,到底這個物件會不會有 null 的情況,必須明確地在程式中,以各種方式標示出來,甚至可以在規避 NullPointerException 的前提下,率先在 method 的 return value 中加上 null 的檢查,並在 null 時改回傳 default value。

也許一個 @NotNull 或是 @Nullable 的 annotation,更明確地告訴程式設計師要注意 null 會更有用,但就不繼續討論了。

Reference

java2s Optional

Java 8 Optional Example

Java Gossip: 使用 Optional 取代 null

Bootstrap Grid System

Bootstrap 以 grid system 提供網頁畫面版面切割的功能,官方手冊上 Grid system 這個段落,將此功能定義為 a responsive, mobile first fluid grid system,這表示 grid 是符合 Responsive Web Design 的系統,而且在行動版畫面是以比例的方式 fluid 為主,如果是一個設計良好的 fluid 頁面,那麼在手機瀏覽該頁面時,就不會看到水平方向有產生 scroll bar,該說明內容也明確地提供了使用 grid 要注意的設定。

要注意的是,bootstrap 的 grid system 在 v2 與 v3 有一些不同的地方,以下我們只討論 v3 的 grid system。

fixed/fluid/elastic layout

Fixed vs. Fluid vs. Elastic Layout: What’s The Right One For You?

fixed layout 是以固定的寬度 px 設定元件的寬度,而 fluid layout 是以總寬度的百分比例,設定元件的寬度。elastic 則是認為,要混合使用 fixed 與 fluid layout。

elastic layout 認為除了考慮螢幕寬度之外,還要考慮 resolution,低解析度的螢幕就該要配置較大尺寸的文字與圖片。

以 bootstrap grid system 來說,是使用 fluid layout 來設計頁面 layout。因為它只考慮了寬度,沒有考慮解析度。

grid system

以 Grid 的方式設計網頁,並不只有 bootstrap 才提供這樣的功能,目前在討論 Responsive Web Design 時,都已經習慣以 grid 的方式進行網頁設計,所以有很多 web framework 都支援 grid system,例如 960gssusyblueprint

當 web designer 與 programmer 都能以 grid 的方式,進行網頁設計,這樣就能對網頁 layout 能有共同的認知,而不會在 web desinger 切割的 layout 圖片在實際上放入網頁時,發生偏移無法對齊的問題。

不過同樣都是在使用 grid system,必須要注意的是,共用的 grid 規則為何,因為每個 framework 使用的 gutter 與 padding 不一樣。

  1. 頁寬共分為幾欄,bootstrap 為 12 欄
  2. 欄和欄之間的間距 gutter,bootstrap 的 column 前後的 padding 為 15 px,所以 gutter 就是 15+15 = 30 px
  3. 主要文本和邊緣的間距 grid padding

ref:
使用 grid system 來設計你的網頁
Grid 的運用:PS外掛
Using the 960 Grid System as a Design Framework

bootstrap grid system

grid system 是以 rows 與 columns 的組合,建立頁面的 layout。

  1. 假設每一頁可切割為 12 個 columns
  2. row (行) 必須放在一個有 .container (fixed-width) 或是 .container-fluid (full-width,滿版,以比例方式設定欄寬) 的 div 中

  3. 類似 col-xs-6 的 css class,用來定義 column寬度,並放在 row 裡面,網頁內容放在 column 裡面

  4. grid columns 總共有 12 個,可以指定 3個 .col-xs-4 用來填滿 12 個 columns
  5. 如果一個 .row 有超過 12個 columns,則超過的部份就會自動折行

Grid Options

-- Extra Small devices (Phones) (<768px) SMall devices (Tablets) (>=768px) Medium devices (Desktops) (>= 992px) LarGe devices (Desktops) (>=1200px)
Grid Behavior 永遠都是 horizontal 一開始是收合的,如果超過寬度的設定值,就會顯示出來 同左 同左
Container width none(auto) 750px 970px 1170px
Class prefix .col-xs- .col-sm- .col-md- .col-lg-
# of columns 12 同左 同左 同左
Column width auto ~62px ~81px ~97px
Gutter width 30px (15px on each side of a column) 同左 同左 同左
Nestable Yes 同左 同左 同左
Offset Yes 同左 同左 同左
Column ordering Yes 同左 同左 同左

Nestable 就是在 row, column 裡面,可再加入一層 rows, columns,就像是在一個 table 的 column 裡面再放進一個 table
Offset 就是位移,當設定了 .col-md-offset-4 ,這個區塊就會向右位移 4 格 columns,兩個 columns 中間多了一塊空白區域
Column ordering 就是可以改變 column 的前後順序,當設定了 .col-md-push-3 ,則會在 .col-md 有作用時,將該區塊往右邊 push 3 格

grid system 的範例

有關 grid system 的範例可查閱這兩個網址
bootstrap grid
bootstrap grid examples

接下來,列出一些簡短的範例紀錄使用方法

當 md 有作用時,分為 8:4 兩塊

<div class="container">
    <div class="row">
        <div class="col-md-8">.col-md-8</div>
        <div class="col-md-4">.col-md-4</div>
    </div>
</div>

同時支援 Mobile, Desktop 的樣式,Mobile 時為 12:6,Desktop 時為 8:4

<div class="row">
    <div class="col-xs-12 col-md-8">.col-xs-12 .col-md-8</div>
    <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
</div>

同時支援 Mobile, Tablet, Desktop 的樣式,Mobile 時為 12:6,也就是兩行,Tablet 時為 6:6,Desktop 時為 8:4

<div class="row">
    <div class="col-xs-12 col-sm-6 col-md-8">.col-xs-12 .col-sm-6 .col-md-8</div>
    <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
</div>

同時支援 Mobile, Tablet 的樣式,Mobile 時為 6:6,因為加上了 clearfix 與 visible-xs-block,所以 xs 的部份重設了,就不會折行,Tablet 時為 4:4:4

<div class="row">
    <div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div>
    <div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div>
    <!-- Optional: clear the XS cols if their content doesn't match in height -->
    <div class="clearfix visible-xs-block"></div>
    <div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div>
</div>

支援 Tablet,第二區塊,會往右位移 4 格

<div class="row">
    <div class="col-md-4">.col-md-4</div>
    <div class="col-md-4 col-md-offset-4">.col-md-4 .col-md-offset-4</div>
</div>

Nesting columns,把 row 放入 col 裡面

<div class="row">
  <div class="col-sm-9">
    Level 1: .col-sm-9
    <div class="row">
      <div class="col-xs-8 col-sm-6">
        Level 2: .col-xs-8 .col-sm-6
      </div>
      <div class="col-xs-4 col-sm-6">
        Level 2: .col-xs-4 .col-sm-6
      </div>
    </div>
  </div>
</div>

Column Ordering: 利用 .col-md-push- 與 .col-md-pull- 改變區塊的順序,第一塊大小為 9,往右 push 3 格,第二塊大小為 3 ,往左 pull 9 格

<div class="row">
    <div class="col-md-9 col-md-push-3">.col-md-9 .col-md-push-3</div>
    <div class="col-md-3 col-md-pull-9">.col-md-3 .col-md-pull-9</div>
</div>

Responsive utilities: visible hidden

Responsive utilities 為了快速建立 mobile-friendly 的網頁,div 元件還可以根據 device 的狀態,來決定要 顯示 或是 隱藏。

css class devices
.visible-xs-* Extra small (less than 768px) visible
.visible-sm-* Small (up to 768 px) visible
.visible-md-* Medium (768 px to 991 px) visible
.visible-lg-* Larger (992 px and above) visible
.hidden-xs Extra small (less than 768px) hidden
.hidden-sm Small (up to 768 px) hidden
.hidden-md Medium (768 px to 991 px) hidden
.hidden-lg Larger (992 px and above) hidden
.visible-print-* Visible when print
.hidden-print Visible only to browser not to print

所有 .visible-device- 後面都可以加上 css 的 display property value: inline, block, inline-block,換句話說,.visible-xs-* 的設定就可以是 .visible-xs-inline 或是 .visible-xs-block 或是 .visible-xs-inline-block

References

Bootstrap Grid System in w3schools
Bootstrap 2.x 與 3 的重要差異(二):grid系統的變革
15 款相當不錯的 Twitter Bootstrap 開發工具

使用 Bootstrap 建置雛型網站
簡單的Bootstrap效果(v 3.0.0 / v 3.2.0版)與入門教學 (#2 範例修正與下載)

淺談CSS Grid system
《LayoutIt!》視覺化Bootstrap線上編輯器

Twitter Bootstrap 框架簡化網頁開發、兼容不同裝置,讓網頁工程師兼設計師!