มาแล้ว Codelab สอนใช้ Android Design Support Library จากงาน I/O Rewind Bangkok

ทำแอพฯให้หวือหวาสไตล์ Material Design

Posted on 22 Jun 2015 15:22 | 91422 reads | 0 shares
 

พูดถึง Material Design คงไม่มีคนสายแอนดรอยด์คนไหนไม่รู้จักเพราะมันกลายเป็นปรัชญาการออกแบบที่ได้รับความนิยมอย่างสูงจนเปลี่ยนโลกการออกแบบได้ไปเป็นที่เรียบร้อย

อย่างไรก็ตาม การนำ Material Design มาใช้ในการเขียนแอพฯแอนดรอยด์กลับทำได้ยุ่งยากเพราะ UI Component ต่างๆเช่น Floating Action Button (FAB) มีให้ใช้แต่บน Android Lollipop (API Level 21+) เป็นต้นไปเท่านั้น การจะนำ Material Design ไปใช้บนรุ่นก่อนหน้าจึงต้องพึ่งพาอาศัย 3rd Party ที่มีคนทำแจก ถึงจะแก้ปัญหาได้ระดับหนึ่ง แต่ความลำบากคือต้องไปคอยหาไลบรารี่มาเสริมตามความต้องการ ตัวไหนเวอร์คตัวไหนไม่เวอร์คก็ต้องลองเสี่ยงกันไป

ล่าสุดทีมแอนดรอยด์ก็ได้ทำสิ่งที่ทุกคนรอคอยมานานอย่างการออก Support Library ตัวใหม่ล่าสุดนามว่า Android Design Support Library ที่พ่วงเอา UI Component หลักๆในการทำ Material Design ไว้อย่างครบครันไว้ในไลบรารี่มาตรฐานตัวเดียว วันนี้เราเลยจะมาเขียนบล็อกสอนวิธีการใช้งาน Component แต่ละตัวกันครับ

และนี่คือปลายทางที่เราจะทำไปจนเสร็จ ลองกดเล่นวีดีโอดูได้ครับ

จุดเริ่มต้นคือ Activity เปล่าๆที่มี DrawerLayout ครอบไว้เพื่อให้สามารถดึง Navigation Drawer ออกมาจากด้านข้างได้

ในที่นี้ก็แอบปรับสีของแอพฯไว้เรียบร้อยตามหลักของ Material Theme

        <item name="colorPrimary">#2196F3</item>
        <item name="colorPrimaryDark">#1565C0</item>
        <item name="colorAccent">#E91E63</item>

Step 1: Clone Source Code from Github

สำหรับคนที่อยากทำตามไปพร้อมกับ Blog นี้ สามารถไป Clone Source Code ได้ที่ GitHub ครับ ตัว MainActivity ที่ติดมาด้วยคือปลายทาง โดยให้เริ่มต้นที่ CodeLabActivity แล้วค่อยๆทำตามไปพร้อมกันทีละขั้นครับ =)

อย่างแรกที่ทุกคนต้องทำเองให้ผ่านก่อนคือ ... ต้องรันให้ได้ครับ ซึ่งจริงๆควรจะรันได้เลย แต่บางคนอาจจะยังลง SDK มาไม่ครบ ก็ทำให้เรียบร้อยครับ

Step 2: ใส่ Dependency

เริ่มเลยละกันนะ! เริ่มต้นด้วยการใส่ Dependency ให้กับโมดูลเราก่อนเลยครับดังนี้

compile 'com.android.support:design:22.2.1'

ซึ่ง Design Support Library มี Depends ตัว Support v4 และ AppCompat v7 ไว้อยู่แล้ว ดังนั้นการใส่บรรทัดข้างบนนี้ไป คุณจะสามารถใช้ Component ที่อยู่ในไลบรารี่ทั้งสองได้ทันทีครับ

เพียงเท่านี้แอพฯของคุณจะก็จะสามารถเข้าถึง Component ที่อยู่ในไลบรารี่เป็นที่เรียบร้อยแล้ว ทั้งนี้ทั้งนั้น ตัวอย่างจาก Github เราได้ใส่ไว้ให้เรียบร้อยแล้ว ดังนั้นไม่ต้องใส่ซ้ำอีก แต่ในกรณีที่คุณจะทำโปรเจคของคุณเอง อย่าลืมใส่บรรทัดข้างต้นด้วยครับ

Step 3: ใส่ปุ่ม FAB

Floating Action Button (FAB) นับเป็นปุ่มที่เป็นสัญลักษณ์ของ Material Design ได้เป็นอย่างดี ดังนั้นเรามาเริ่มด้วยการใส่ปุ่มดังกล่าวไว้ในหน้าจอเลยละกันด้วยการแปะ FloatingActionButton ที่ครอบด้วย FrameLayout ลงไปวางไว้เป็น Content ของ DrawerLayout แทนที่ TextView ที่มีอยู่ในไฟล์ activity_code_lab.xml ดังนี้

<android.support.v4.widget.DrawerLayout ...
    xmlns:app="http://schemas.android.com/apk/res-auto"
    ....>

    <FrameLayout
        android:id="@+id/rootLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fabBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|right"
            android:src="@drawable/ic_plus"
            app:fabSize="normal" />

    </FrameLayout>

    ...

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

โดย android:layout_gravity="bottom|right" เป็นการบอกให้ปุ่มวางไว้ด้านขวาล่างของจอ ส่วน android:src เป็นการระบุ Resource ID ของไอคอนที่ต้องการแสดงบนปุ่ม (ในที่นี้ใช้ภาพขนาด 40dp) สุดท้าย app:fabSize="normal" เป็นการบอกให้ปุ่มมีขนาดมาตรฐาน ซึ่งก็คือ 56dp แต่ถ้าต้องการปุ่มเล็กเพื่อใช้ในบางกรณี สามารถปรับค่าตรงนี้ให้เป็น mini ได้เช่นกัน จะได้เป็นปุ่มขนาด 40dp แทน

เพียงเท่านี้คุณก็จะได้ปุ่ม FAB เอามาใช้งานแล้ว (ง่ายป่ะล่าาาา) และนี่คือผลการรันบน Android 4.4 ครับ

screenshot20

แต่พอไปรันบน Android 5.0 ผลกลับเป็นแบบนี้ ...

screenshot17n

ทั้งนี้ทั้งนั้น ไม่มีอะไรนอกจากคำว่า "บั๊ก" ครับ ทางทีมแอนดรอยด์ทราบแล้วว่ามีปัญหานี้ จะแก้ไขใน Release หน้า แต่ถ้าต้องการใช้ตอนนี้เลยต้องใช้ Configuration Qualifier ในการแก้โดยแยกเคสเอาว่าถ้าอยู่บน API Level 21 เป็นต้นไป ให้เว้น Margin ด้านขวาและด้านล่างมาอย่างละ 16dp เอง แต่ถ้ารันบนแอนดรอยด์เวอร์ชั่นต่ำกว่านั้นก็เป็น 0dp เหมือนเดิมครับ เพราะมันเว้นไว้อยู่แล้ว

res/values/dimens.xml

    <dimen name="codelab_fab_margin_right">0dp</dimen>
    <dimen name="codelab_fab_margin_bottom">0dp</dimen>

res/values-v21/dimens.xml

    <dimen name="codelab_fab_margin_right">16dp</dimen>
    <dimen name="codelab_fab_margin_bottom">16dp</dimen>

res/layout/activity_code_lab.xml

    <android.support.design.widget.FloatingActionButton
        ...
        android:layout_marginBottom="@dimen/codelab_fab_margin_bottom"
        android:layout_marginRight="@dimen/codelab_fab_margin_right"
        .../>

และนี่คือผลการทำงานครับ ขยับมาแล้ว

 

screenshot23

ซึ่งเงาตรงนี้จะเป็นค่า Default ที่เป็น Best Practices อยู่แล้วคือ 6dp ในสถานะไม่ถูกกดและ 12dp ในขณะที่ปุ่มถูกกด แต่ถ้าอยากจะปรับระยะเงาก็สามารถกำหนดที่ app:elevation สำหรับเงาปุ่มตอนสถานะปกติและ app:pressedTranslationZ สำหรับระยะเงาปุ่มตอนถูกกดได้ครับ

ส่วนเรื่องสี โดยปกติแล้วปุ่ม FAB จะเอาสีจาก colorAccent มาใช้ แต่ถ้าต้องการปรับเป็นสีอื่น ก็สามารถปรับได้ที่ Attribute app:backgroundTint คร้าบผม

วิธีการ Handle การกดคลิกก็ทำได้เหมือนปุ่มทั่วไป แปะคำสั่งดังต่อไปนี้ไว้ต่อท้ายฟังก์ชั่น initInstances ในไฟล์ CodeLabActivity.java กันครับ

    FloatingActionButton fabBtn;

    ...

    private void initInstances() {
        ...

        fabBtn = (FloatingActionButton) findViewById(R.id.fabBtn);
        fabBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

เป็นอันเรียบร้อยสำหรับปุ่ม FAB

Step 4: เล่นกับ Snackbar

Snackbar หรือแถบด้านล่างที่เด้งขึ้นมาบอกสถานะแล้วหายไปได้ หลักการคล้ายๆ Toast เพียงแต่มันจะกลายเป็นส่วนหนึ่งของหน้า Activity แทนที่จะมาทับหน้าจอเหมือน Toast

snackbar

ซึ่งไม่ใช่แค่หลักการที่คล้าย Toast แต่โค้ดดิ้งก็เหมือนกันทุกประการ สามารถเรียกมันขึ้นมาด้วยคำสั่งง่ายๆตามรูปแบบ

Snackbar.make(someView, "Hello. I am Snackbar!", Snackbar.LENGTH_SHORT)
        .setAction("Undo", new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        })
        .show();

โดย Parameter แรกคือ View หรือ Layout ที่จะให้ Snackbar เด้งขึ้นมา ตามปกติเราจะใช้ Layout ของหน้าจอกันครับ ซึ่งในที่นี้ก็คือ FrameLayout ที่ครอบปุ่ม FAB อยู่นั่นเอง ส่วนคำสั่ง setAction เป็นการกำหนดปุ่มทางด้านขวา ถ้าไม่ต้องการมีปุ่มก็สามารถลบส่วนของคำสั่งนี้ทิ้งไปได้เลย

เอาหละ มาทำจริงกันเลย ก่อนอื่นให้ findViewById เจ้า FrameLayout ออกมาแล้วทำการเด้ง Snackbar บน FrameLayout ตัวนี้ แก้ไขโค้ดตามนี้ครับ

    FrameLayout rootLayout;

    ...

    private void initInstances() {
        ...

        rootLayout = (FrameLayout) findViewById(R.id.rootLayout);

        fabBtn = (FloatingActionButton) findViewById(R.id.fabBtn);
        fabBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Snackbar.make(rootLayout, "Hello. I am Snackbar!", Snackbar.LENGTH_SHORT)
                        .setAction("Undo", new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {

                            }
                        })
                        .show();
            }
        });
    }

ลองคลิกที่ปุ่ม FAB ดู จะพบว่า Snackbar เด้งขึ้นมาแล้ว

screenshot24

แต่ปัญหาคือ Snackbar ทับปุ่ม FAB ไปซะอย่างงั้น ซึ่งตามหลักการมันก็ถูกแล้วเพราะ FAB และ Snackbar ไม่มีความเกี่ยวข้องอะไรกัน

แต่ถ้ามองในแง่ของ UX แล้ว มันบาปมากที่มันทับกันแบบนี้ จึงไม่ต้องแปลกใจที่ทีมพัฒนาไลบรารี่จึงจัด Layout ตัวใหม่มาเพื่อให้ View ที่อยู่ภายในสามารถทำงานร่วมกันได้อย่างลงตัว ตรงตามชื่อของมันว่า CoordinatorLayout

Step 5: ใส่ CoordinatorLayout

CoordinatorLayout คือ Layout ที่มีคุณสมบัติทำให้ View ที่อยู่ภายในทำงานร่วมกันได้ ทั้งนี้ทั้งนั้นมันไม่มีเวทมนตร์หรือสิ่งมหัศจรรย์อะไรที่อยู่ดีๆมันก็ใช้งานได้ แต่เพราะว่า View ที่อยู่ด้านในทุกตัวเป็น View ที่ออกแบบมาให้ทำงานกับ CoordinatorLayout ได้นั่นเอง ซึ่ง FAB และ Snackbar ก็เป็นสองตัวในนั้น

จุดนี้ให้เปลี่ยน FrameLayout ที่ครอบ FAB อยู่ให้กลายเป็น CoordinatorLayout แทนได้เลยครับ

res/layout/activity_code_lab.xml

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/rootLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        <android.support.design.widget.FloatingActionButton
            ... />
    </android.support.design.widget.CoordinatorLayout>

และอย่าลืมไปเปลี่ยนประเภทตัวแปรของ rootLayout ใน CodeLabActivity.java ให้เป็น CoordinatorLayout ด้วย ไม่งั้น Crash นะจ๊ะ

    //FrameLayout rootLayout;
    CoordinatorLayout rootLayout;

    //rootLayout = (FrameLayout) findViewById(R.id.rootLayout);
    rootLayout = (CoordinatorLayout) findViewById(R.id.rootLayout);

แต่หลังจากย้าย FAB ไปไว้ใน CoordinatorLayout ปรากฏว่าบน Android ต่ำกว่า Lollipop Margin ของปุ่มกลับหายไปอย่างไร้ร่องรอย จนปุ่มแนบชิดกับขอบไป ทั้งๆที่ถ้าเอา FAB ไปใส่ไว้ใน Layout แบบอื่น จะมี Margin มาให้เลย

screenshot20
FAB ใน FrameLayout

screenshot19n
FAB ใน CoordinatorLayout

ไม่รู้ว่าอันไหนเป็น Behavior ที่ถูกต้อง แต่ที่แน่ๆมันต้องมีแค่อันเดียว คงต้องรอแก้ไขใน Release หน้า ส่วนตอนนี้ต้องแก้ไข Margin ไปตามกรณีเองครับ

res/values/dimens.xml

    <dimen name="codelab_fab_margin_right">16dp</dimen>
    <dimen name="codelab_fab_margin_bottom">16dp</dimen>

เรียบร้อย และนี่คือผลการทำงานครับ นอกจาก FAB จะขยับตาม Snackbar ด้วยตัวเองแล้ว เรายังสามารถ Swipe to dismiss เจ้า Snackbar ได้อีกด้วย!

อย่างไรก็ตาม Animation การเคลื่อนกลับของปุ่มไปยังตำแหน่งเดิมตอน Swipe to dismiss กลับกระตุกและไม่สวยงามเหมือนตอน v22.2.0 ถือเป็น Behavior ที่น่าจะเรียกว่าบั๊กได้ คงต้องรอเวอร์ชั่นใหม่สำหรับการแก้ไขเช่นกันครับ

ยังไงแล้ว จากนี้หากคิดจะใช้ Android Design Support Library ให้ใส่ CoordinatorLayout ไว้ก่อนเลยครับ เพราะมันถือเป็นส่วนหลักของโครงสร้างโค้ดในไลบรารี่นี้เลย เดี๋ยวจะได้เห็นในลำดับต่อไป

Step 6: เลิกใช้ ActionBar เปลี่ยนมาใช้ Toolbar

อันนี้ไม่ได้อยู่ใน Android Design Support Library แต่การจะใช้ Component ที่เหลือจำต้องใช้ Toolbar ในการทำงานแทน ActionBar ซึ่งเอาจริงๆแล้ว นาทีนี้เราแนะนำให้เลิกใช้ ActionBar แล้วเปลี่ยนมาใช้ Toolbar แทนจะดีกว่า เนื่องจากยืดหยุ่นมากกว่าและไลบรารี่ที่ออกมาในพักหลังจะทำงานร่วมกับ Toolbar เกือบหมด รวมถึงไลบรารี่ที่อยู่ใน Androd Design Support Library นี้ด้วย

วิธีแทนที่ ActionBar ด้วย Toolbar ก็ทำได้ง่ายๆ เริ่มด้วยการปิด Action Bar ของ Activity นั้นๆก่อนโดยเซตที่ Theme ของแอพฯเพิ่มเข้าไปว่า

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

จากนั้นแปะ Toolbar ลงไปใน CoordinatorLayout ไว้ด้านบนของ FAB ดังนี้

    <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" />
        <android.support.design.widget.FloatingActionButton
            ...>
        </android.support.design.widget.FloatingActionButton>
    </android.support.design.widget.CoordinatorLayout>

แล้วก็ย้าย Action Bar ไปสิงสถิตบน Toolbar ให้เรียบร้อยด้วย Java Code ด้านล่างนี้

    Toolbar toolbar;

    private void initInstances() {
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ...
    }

ถึงตอนนี้จะรันได้แล้ว แต่เดี๋ยวก่อน ... อย่างที่เราบอกว่าของที่อยู่ภายใต้ CoordinatorLayout จะต้องเป็นของที่ทำงานกับ CoordinatorLayout ได้ แต่ Toolbar เป็นของที่เกิดขึ้นมาก่อน จึงไม่ต้องแปลกใจที่มันจะยังทำงานร่วมกันไม่ได้

Android Design Support Library จึงมาพร้อมกับสิ่งที่เอาไว้ Wrap Bar ด้านบนของแอพฯเอาไว้โดยเฉพาะ (ซึ่งในที่นี้คือ Toolbar นั่นเอง) ไม่ต้องทำอะไรมาก แค่ Wrap Toolbar ด้วย AppBarLayout เท่านี้ก็เรียบร้อย อ่ะ ทำตามมม

    <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>

หลังจากเปลี่ยน Action Bar เป็น Toolbar แล้ว การทำงานของ DrawerLayout จะเปลี่ยนไปด้วยเพราะ Toolbar กลายเป็น Content ของแอพฯไปแล้ว และนี่คือผลการทำงานของการแปะ Toolbar + AppBarLayout ลงไป

เรียบร้อยไปอีกหนึ่งขั้นตอน จากนี้หากจะใช้ Toolbar แนะนำให้ใช้ AppBarLayout ครอบไปเลยครับ แค่ได้เงากลับมาก็ดีใจน้ำตาไหลแล้ว =)

Step 7: ใส่ Content แอพฯ

ปุ่ม FAB ก็แปะแล้ว Toolbar ก็ใส่แล้ว ต่อไปมาแปะ Content แอพฯลง Activity กัน

ก็ง่ายๆเลย ใส่ปุ่มไปสัก 2 ปุ่มละกันเนอะ โดยใส่ไว้ระหว่าง AppBarLayout และ FAB ครับ

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            >
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Yo Yo"
                />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Yo Yo"
                />
        </LinearLayout>

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

แต่พอรันออกมาจะพบว่าปุ่มไปซ้อนอยู่ด้านใต้ AppBarLayout ซะอย่างนั้น

screenshot29

เดากันได้มั้ยว่าเพราะอะไร? นั่นก็เพราะว่า LinearLayout ไม่ได้ออกแบบมาให้ทำงานร่วมกับ CoordinatorLayout นั่นเองงงง แต่สำหรับกรณีนี้มันไม่มี Layout พิเศษอะไรมาครอบแล้วครับ แต่สามารถใช้ LinearLayout ตัวเดิมแต่เพิ่ม Attribute ที่กำหนดไว้ขึ้นมาตัวนึงได้ทันทีว่า

<LinearLayout
    ...
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    ...
    >

คราวนี้ Content ก็จะกระดึ๊บลงมายังตำแหน่งที่ถูกต้องแล้วจ้า ง่ายๆแค่นี้แล

screenshot30

จบไปอีกหนึ่งขั้นตอน

Step 8: เล่นกับ TabLayout

Tab เป็น UI ที่ถูกเอามาใช้ในการเขียนแอพฯแอนดรอยด์มาโดยตลอดอยู่แล้ว รวมถึงตอนเป็น Material Design ด้วย ก่อนหน้านี้ถ้าจะใช้ Tab ต้องไปโหลด Source Code ของตัว SlidingTabLayout/SlidingTabStrip มาลงในโปรเจคเอง แต่ตอนนี้ไม่ต้องแล้วเพราะ TabLayout ที่ใช้งานในแบบเดียวกันถูกเอามาใส่ไว้ใน Android Design Support Library แล้ว พร้อมฟีเจอร์ที่เพิ่มขึ้นอีกด้วย

คำถามคือเราควรจะเอาเจ้า TabLayout นี้ไปแปะที่ไหนดี? ตาม UX Guideline ของแอนดรอยด์พวก Tab ต่างๆควรจะอยู่ด้านบน ซึ่งจะให้ดีมันควรจะอยู่เหนือเงาของ AppBarLayout ดังนั้นเราเลยจะแปะมันลง AppBarLayout ด้านใต้ Toolbar ครับ โดยมันจะเรียงต่อกันโดยอัตโนมัติเพราะ AppBarLayout นั้น Inherit มาจาก LinearLayout แนวตั้งนั่นเอง

        <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 Code ในการเพิ่ม Tab

    TabLayout tabLayout;

    private void initInstances() {
        tabLayout = (TabLayout) findViewById(R.id.tabLayout);
        tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
        tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
        tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));

        ...
    }

ง่ายๆแค่นี้แล ผลการทำงานเป็นดังนี้ครับ

screenshot31

สังเกตดูว่าฟอนต์ของ Tab ดันเป็นสีดำทั้งๆที่ควรเป็นสีขาว ทั้งนี้เพราะเรายังไม่ได้กำหนด Theme ให้มันนั่นเอง วิธีการกำหนด Theme ก็ใช้วิธีเดียวกับที่เราใช้กำหนด Theme ให้ Toolbar นั่นเอง

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

เป็นสีขาวแล้วครับ

screenshot32

เราสามารถควบคุม TabLayout เองทั้งหมดตามแบบข้างต้น หรือจะทำงานร่วมกับ ViewPager ให้ไปดึง PageTitle จาก Adapter ของ ViewPager ได้โดยอัตโนมัติด้วยเช่นกัน โดยใช้คำสั่ง setupWithViewPager(...) ครับ เคสนี้น่าจะได้ใช้บ่อยทีเดียว

โดย TabLayout มี Attribute อยู่สองตัวที่ใช้ปรับรูปแบบการแสดงผลได้คือ

app:tabMode - ตั้งเป็น fixed เมื่อต้องการให้ TabLayout ขยับไม่ได้นั่นแปลว่าทุก Tab จะแสดงพร้อมกันในหน้าเดียวซึ่งไม่ดีนักหากมี Tab เยอะ ในกรณีที่คิดว่า Tab น่าจะเยอะจนแสดงผลบนหน้าจอเดียวไม่พอก็สามารถตั้งเป็น scrollable เพื่อให้มัน Scroll ไปมาได้เช่นกัน

app:tabGravity - ตั้งเป็น fill ถ้าต้องการให้ Tab ทั้งหลายกระจายเต็มพื้นที่หรือตั้งเป็น center ถ้าต้องการให้ Tab ทั้งหลายกองอยู่ตรงกลาง โดย Attribute นี้จะมีผลได้ก็ต่อเมื่อคุณตั้ง tabMode เป็น fixed เท่านั้น

การแสดงผลในโหมดต่างๆเป็นดังนี้ครับ

tabmodetabgravity

จบกับ TabLayout เพียงเท่านี้ครับ

Step 9: ทำให้ AppBarLayout Scroll หายออกไปจากจอตอน Scroll Content

Android UX Guideline นึงของ Bar ด้านบน (ในที่นี้ขอเรียกว่า AppBar) ที่ประกาศออกมาตอน Material Design คือมันสามารถหายไปได้ตอนที่ Content ของแอพฯถูก Scroll ซึ่งมีคนทำไปหลายเจ้าแล้วก่อนหน้านี้เพราะมันช่วยให้มีพื้นที่ในการแสดงผลแอพฯมากขึ้นจริงๆ

ตรงนี้ก็ถูกเพิ่มเข้ามาใน Library ของ Android Design Support Library ไว้เช่นกันและไม่ต้องทำเองอีกต่อไป เพียงใส่ Attribute เข้าไปให้ Toolbar ตัวเดียวเท่านั้น ที่เหลือมันจะทำให้เลยโดยอัตโนมัติ

เพื่อทดสอบฟีเจอร์นี้ เราจำเป็นต้องทำให้ Content เรา Scroll ได้เสียก่อน ด้วยการเพิ่มจำนวนปุ่มเข้าไปอีกสัก 20 ปุ่ม

                <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Yo Yo"
                    />
                ...
                <!-- Add 20 more buttons here -->
                ...
                <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Yo Yo"
                    />

จากนั้นเอา ScrollView ครอบ LinearLayout เพื่อให้มัน Scroll ได้ครับ และ อย่าลืมเอา layout_behavior ย้ายมาอยู่ที่ ScrollView ด้วย เพราะตอนนี้มันเป็นลูกของ CoordinatorLayout แทนที่ LinearLayout เรียบร้อย

        <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>

คราวนี้ไปเพิ่ม Scroll Flags ให้กับ Toolbar ดังนี้

<android.support.v7.widget.Toolbar
    ...
    app:layout_scrollFlags="scroll|enterAlways" />

คราวนี้ลองรันดู

อ้าว ทำไมไม่เกิดอะไรขึ้นหละเนี่ย ไหนว่า Toolbar จะ Scroll หลบไปด้วยหละ?

เดาออกมั้ยครับว่าทำไม? ... เหตุผลเดิมๆ เพราะ ScrollView ไม่สามารถทำงานร่วมกับ CoordinatorLayout ได้นั่นเอง ! คราวนี้จะใช้อะไรดีหละ? คำตอบคือต้องใช้ NestedScrollView ที่อยู่ใน Android Support Library v4 แทนครับ ตัวนั้นถูกออกแบบมาใหม่เลยสามารถใช้งานร่วมกับ CoordinatorLayout ได้ทันที

<android.support.v4.widget.NestedScrollView ...>
    <LinearLayout ...>
        ...
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

และด้วยเหตุผลเดียวกัน ListView ก็ไม่สามารถทำงานกับ CoordinatorLayout ได้เช่นกัน ต้องใช้ตัวใหม่ที่เพิ่งเกิดมาไม่นานอย่าง RecyclerView แทนเท่านั้นครับ

หลังจากเปลี่ยนเป็น NestedScrollView ก็เรียบร้อย ใช้งานได้เรียบร้อยแล้ว เย้ เย

จะเห็นว่า TabLayout จะไม่ Scroll ไปด้วย เพราะเราไม่ได้ใส่ Flags ให้มันไปนั่นเอง หากอยากให้มัน Scroll หายไปจากจอด้วยก็ใส่เหมือนเดิมเข้าไปให้ TabLayout ด้วย

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

ผลการทำงาน

คราวนี้มาดูโหมดการทำงานกัน สงสัยมั้ยว่า scroll และ enterAlways ที่ใส่ไปคืออะไร? จริงๆ layout_scrollFlags สามารถใส่ได้ 4 ค่าด้วยกัน

scroll - ใส่แล้ว View นั้นๆจะ Scroll ร่วมไปกับ Content ด้วย เป็น Flag ที่ต้องใส่เป็นพื้นไว้สำหรับ View ที่ต้องการให้ Scroll ไปด้วยได้

enterAlwaysCollapsed - ถ้า View นั้นๆกำหนดค่า minHeight ไว้ ตอนดึงลงมันจะลงมาให้สูงเท่า minHeight (เรียกโหมดนี้ว่าโหมด Collapsed) และจะ Scroll ขยายจนเต็มขนาดก็ต่อเมื่อส่วนของ Content Scroll ถึงบนสุดแล้ว ใช้งานร่วมกับ scroll ด้วยเครื่องหมาย | เช่น scroll|enterAlwaysCollapsed

 

อย่างไรก็ตาม การทำงานเหมือนจะไม่ตรงตามสเปคที่ระบุเพราะตอนดึงลง View นั้นๆกลับไม่ตกลงมาตามค่า minHeight แต่จะลงมาทีเดียวตอน Content ถูก Scroll จนถึงด้านบนสุด

enterAlways - เป็นการบอกว่าแค่ลาก Content ลงก็เผยตัวมาได้เลยนะ (เรียกอีกอย่างว่า Quick Return) ใช้งานร่วมกับ scroll เช่นกันครับ scroll|enterAlways

exitUntilCollapsed - เป็นการบอกว่าให้ View นั้นๆหดเล็กลงจนถึงขนาด minHeight (โหมด Collapsed) แล้วค้างไว้เช่นนั้น จากนั้น Content ถึงจะมีสิทธิ์ถูก Scroll หนีหายออกไปจากจอ ยกตัวอย่างเช่นถ้าเราแก้โค้ดของ Toolbar เป็น

<android.support.v7.widget.Toolbar
    ...
    android:layout_height="192dp"
    android:gravity="bottom"
    android:paddingBottom="12dp"
    android:minHeight="?attr/actionBarSize"
    app:layout_scrollFlags="scroll|exitUntilCollapsed"
    />

ผลการทำงานจะเป็นดังนี้ครับ

โหมดนี้จะใช้เป็นประจำกับ Component ที่จะพูดถึงในส่วนถัดไปครับ

เรียบร้อย ง่ายนิดเดียวใช่ม้า =)

Step 10: เอา TabLayout ออก

เนื่องจากในตัวอย่างถัดๆไปเราจะเล่นกับ Toolbar มากขึ้น และ TabLayout จะไม่ถูกเอามาใช้แล้ว เราเลยขอลบ TabLayout ทิ้งออกจาก Layout ก่อนครับ

<!--android.support.design.widget.TabLayout -->

และจาก Java Code ด้วย

        //tabLayout = (TabLayout) findViewById(R.id.tabLayout);
        //tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
        //tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
        //tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));

เรียบร้อย ลุยต่อเลย

Step 11: ทำให้ Toolbar ย่อขยายได้

Android UX Guideline นึงของ Bar ด้านบน (ในที่นี้ขอเรียกว่า AppBar) ที่ประกาศออกมาตอน Material Design คือมันสามารถย่อขยายได้ ไม่ใช่ Fix ตายตัวอีกต่อไป ซึ่งก็คล้ายกับตัวอย่างที่เราทำให้ดูในส่วนของ exitUntilCollapsed แต่มันยังไม่สมบูรณ์แบบ ดูจากที่ Toolbar ถูกผลักออกไปจากจอจนไม่สามารถกด Hamburger Icon ได้

Android Design Support Library จึงจัดเตรียม Layout อีกตัวนึงเพื่อทำให้ Toolbar สามารถย่อขยายได้อย่างถูกต้อง นามว่า CollapsingToolbarLayout การใช้งานก็ง่ายๆ

- ครอบ Toolbar ไปได้เลย แต่ยังต้องอยู่ภายใต้ AppBarLayout

- เอา layout_scrollFlags ออกจาก Toolbar

- ใส่ layout_scrollFlags ที่ CollapsingToolbarLayout แทนด้วยโหมด scroll|exitUntilCollapsed

- ปรับความสูง AppBarLayout ให้เป็นความสูงสูงสุดของ AppBar ตอนขยายออกมา ในที่นี้ขอกำหนดเป็น 256dp

ผลสุดท้ายโค้ดจะออกมาหน้าตาแบบนี้

<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" />

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

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

ผลการทำงานครับ

สังเกตดูว่าพวกปุ่ม Hamburger ยังคงถูก Scroll จนพ้นจอไปอยู่ เราสามารถสั่งให้มัน Pin ไว้ที่แหน่งเดิมได้ด้วยการเติม Attribute ด้านล่างนี้เข้าไปให้กับ Toolbar

<android.support.v7.widget.Toolbar
    ...
    app:layout_collapseMode="pin"
    />

คราวนี้ Toolbar ก็จะถูก Pin ให้เรากดได้ตลอดเวลาแล้วครับ

อีกสิ่งที่เราเจอคือข้อความที่อยู่ใน Toolbar เดิมกลับหายไปอย่างไร้ร่องรอย ตรงนี้เราต้องใส่เข้าไปเองด้วยคำสั่ง setTitle(String) ในส่วนของ Java Code ครับ

CollapsingToolbarLayout collapsingToolbarLayout;

private void initInstances() {
    ...
    collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
    collapsingToolbarLayout.setTitle("Design Library");
}

ผลการทำงาน

สังเกตดูว่าฟอนต์ยังเป็นสีดำอยู่ เพราะเรายังไม่ได้กำหนดธีมให้กับสิ่งที่เพิ่มขึ้นมานั่นเอง วิธีคือให้กำหนด android:theme ที่ AppBarLayout ได้เลยครับ

<android.support.design.widget.AppBarLayout
    ...
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

คราวนี้ฟอนต์ก็จะกลายเป็นสีขาวแล้ว

สังเกตดูว่าตัวอักษรจะมีการ Transition ระหว่างโหมดหด (Collapsed) และโหมดขยาย (Expanded) โดยอัตโนมัติอย่างสวยงาม แต่ถ้าเราต้องการปรับตำแหน่งในโหมด Expanded ก็ทำได้ด้วยการกำหนด Margin ผ่าน Attribute ได้ 4 ตัวด้วยกัน ได้แก่ app:expandedTitleMarginapp:expandedTitleMarginBottomapp:expandedTitleMarginEnd และ app:expandedTitleMarginStart

รวมถึงหากอยากปรับรูปแบบของฟอนต์แสดงผลในโหมด Collapsed และโหมด Expanded ก็กำหนด Text Appearance ผ่าน Attribute app:collapsedTitleTextAppearance และ app:expandedTitleTextAppearance ตามลำดับ ได้ทันที

ในตัวอย่างนี้ขอลองปรับ Margin Start ให้เป็น 64dp ดูครับ

<android.support.design.widget.CollapsingToolbarLayout
    ...
    app:expandedTitleMarginStart="64dp">

ผลการทำงาน

สวยงาม =)

Step 12: ใส่ภาพพื้นหลังให้ App Bar

หลายต่อหลายกรณีเราก็อยากได้ App Bar ที่มีภาพสวยๆ ไม่ใช่สีทึบๆแบบตอนนี้ แน่นอนว่าเจ้า CollapsingToolbarLayout ที่ Inherit มาจาก FrameLayout นั้นเปิดให้เราสามารถซ้อนภาพไว้ด้านใต้ Toolbar ได้ทันทีด้วย ImageView สุดรัก ใส่โค้ดเบาๆไว้เหนือ Toolbar ได้เลยครับ

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:src="@drawable/header" />

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

ผลการทำงาน

ภาพปรากฏแล้ว แต่ดันมีแถบสีฟ้าขึ้นมาบังซะอย่างงั้น ซึ่งจริงๆแล้วไม่ใช่อะไร มันคือ Background ของ Toolbar นั่นเอง จุดนี้เราสามารถเอาบรรทัดด้านล่างนี้ออกจาก Toolbar ได้เลยครับ

android:background="?attr/colorPrimary"

ผลการทำงาน

จะเห็นว่าภาพยังขึ้นไปแบบทื่อๆอยู่ หากอยากทำให้มันเลื่อนแบบสวยๆก็สามารถเปิดโหมด Parallax ได้ด้วยการกำหนด Collapse Mode

<ImageView
   ...
   app:layout_collapseMode="parallax" />

ผลการทำงาน

หากต้องการปรับค่าความหน่วงก็กำหนดตัวคูณได้ที่ Attribute ด้านล่างนี้ด้วยค่าระหว่าง 0.0-1.0

app:layout_collapseParallaxMultiplier="0.7"

อันนี้ไปลองกันเองได้เลยครับ

สุดท้ายเราจะเห็นว่าตอน App Bar หุบมันยังเป็นภาพอยู่ ทั้งๆที่ตอนอยู่โหมดนี้เรามักจะต้องการให้กลายเป็นสีทึบ จะทำยังไงหละทีนี้? ตัว CollapsingToolbarLayout จัดให้อีกแล้วด้วยการกำหนดสีที่ต้องการไว้ที่ Attribute ชื่อ app:contentScrim ได้เลย เช่น

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

ผลการทำงาน

เรียบร้อยแล้วกับส่วนของ App Bar สวยงามได้ด้วยโค้ดไม่กี่บรรทัด =)

Step 13: เล่นกับ Navigation Drawer

ตอนนี้ Drawer Menu ที่ดึงมาจากด้านซ้ายยังเป็นพื้นที่สีขาวๆอยู่ ก่อนหน้านี้หากจะทำเมนูตรงนี้เราต้องมานั่งทำ UI เอาเองด้วย LinearLayout หรือ ListView ซึ่งก็ถือว่าลำบากอยู่พอสมควร

แต่ความสบายมาเยือนแล้วด้วย NavigationView ใน Android Design Support Library ที่สามารถเสกเมนูขึ้นมาด้วยวิธีง่ายแสนง่าย

ก่อนอื่นสร้าง View สำหรับ Header ขึ้นมาก่อน (มีอยู่ในโปรเจคที่ให้ Clone จาก Github ไปแล้ว)

res/layout/nav_header.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    >
    
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/nav_header_bg"
        android:scaleType="centerCrop" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/nuuneoi"
        android:layout_gravity="bottom"
        android:layout_marginBottom="36dp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_margin="16dp"
        android:text="nuuneoi"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

</FrameLayout>

แล้วก็กำหนดเมนูต่างๆใน Menu Resource

res/menu/navigation_drawer_items.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="all">
        <item
            android:id="@+id/navItem1"
            android:icon="@drawable/ic_action_location_found_dark"
            android:title="Home"/>
        <item
            android:id="@+id/navItem2"
            android:icon="@drawable/ic_action_location_found_dark"
            android:title="Blog"/>

        <item
            android:id="@+id/navItem3"
            android:icon="@drawable/ic_action_location_found_dark"
            android:title="About"/>

        <item
            android:id="@+id/navItem4"
            android:icon="@drawable/ic_action_location_found_dark"
            android:title="Contact"/>
    </group>

</menu>

คราวนี้เอา NavigationView ไปแปะไว้ในส่วนของ Drawer Menu ใน Activity (แทนที่ LinearLayout เดิม)

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

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        app:itemIconTint="#333"
        app:itemTextColor="#333"
        app:menu="@menu/navigation_drawer_items" />

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

เพียงเท่านี้ Drawer Menu ของเราก็จะโผล่มาอย่างสวยงามดั่งเสกได้แล้ว เย้ !

ข้อดีของ Navigation Drawer นั้นมีมากโข ไม่ใช่แค่ทำให้สร้างเมนูออกมาง่ายๆ แต่ยังวัดขนาดเมนูที่เหมาะสมในแต่ละหน้าจอให้อัตโนมัติด้วย สะดวกขึ้นกว่าเดิมมากๆครับ

วิธีการ Handle เมนูต่างๆก็ทำได้ง่ายนิดเดียว ด้วยการตั้ง Listener ผ่านคำสั่ง setNavigationItemSelectedListener ดังนี้

    NavigationView navigation;

    private void initInstances() {
        ...

        navigation = (NavigationView) findViewById(R.id.navigation);
        navigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem menuItem) {
                int id = menuItem.getItemId();
                switch (id) {
                    case R.id.navItem1:
                        break;
                    case R.id.navItem2:
                        break;
                    case R.id.navItem3:
                        break;
                }
                return false;
            }
        });
    }

ในการใช้จริงสามารถปรับรูปแบบของ Header และไอคอนของเมนูแต่ละตัวตามที่ต้องการได้เลยคร้าบผม

Step 14: ทำ EditText ให้ทันสมัยด้วย TextInputLayout

สุดท้ายแล้วสำหรับ Codelab ตัวนี้ จบด้วยเรื่องเบาๆอย่างการแปลง EditText ธรรมดาๆให้กลายเป็น EditText ในรูปแบบ Material Design ที่แสดง Hint และ Error Message ให้เห็นตลอดเวลา แทนที่ตัวเดิมที่ Hint หายไปตอนเราพิมพ์ข้อความลงไป

วิธีการใช้งานก็ง่ายแสนง่าย แค่เอา TextInputLayout ครอบ EditText ไป เพียงเท่านี้ก็จะได้ EditText สุดเจ๋งมาใช้แล้ว

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Username" />
</android.support.design.widget.TextInputLayout>

ในที่นี้เราลองแปะไปสองตัวใน NestedScrollView และนี่คือผลการทำงานครับ

ง่ายสุดๆใช่มั้ยหละ =)

สรุป

Android Design Support Library นับเป็น Support Library ที่ล้ำค่าและตอบโจทย์การทำแอพฯแอนดรอยด์ในขวบปีนี้จริงๆ อย่างไรก็ตามคงต้องรอให้ไลบรารี่สมบูรณ์ปราศจากบั๊กก่อน จากนั้นเตรียมเอาไปใช้งานจริงกันได้เลยจ้าาา

ก็คงจบแต่เพียงเท่านี้ครับ ค่อนข้างยาวแต่หวังว่าจะมีประโยชน์ครับ =)


Updated [24/07/2015]: อัพเดต v22.2.0 เป็น v22.2.1

สัปดาห์ที่ผ่านมา ทีมแอนดรอยด์ปล่อย Android Design Support Library เวอร์ชั่นใหม่ v22.2.1 ที่แก้บั๊กไปเยอะพอสมควร แต่ก็มีบั๊กใหม่โผล่มาด้วยเช่นกัน ทางเราทดสอบเสร็จแล้วก็เลยอัพเดตบทความนี้ให้เป็นของ v22.2.1 แล้ว และขอสรุปสิ่งที่เจอในไลบรารี่เวอร์ชั่นใหม่ได้ดังนี้

บั๊กที่แก้ไขแล้ว

- TabLayout ตอนนี้ใช้งานร่วมกับ AppBarLayout ได้อย่างสมบูรณ์แบบแล้ว ลากได้อย่างสบายและไม่มีปัญหาเรื่องการเรนเดอร์อีกต่อไป

- FloatingActionButton บน Android 5.0+ มาพร้อม Drop Shadow แล้ว จากเดิมที่ต้องเติมเองในเวอร์ชั่นก่อน คราวนี้ก็ไม่จำเป็นต้องใส่ app:borderWidth เองอีกต่อไป

screenshot18n_1

- exitUntilCollapsed ใช้งานได้ตามที่ควรจะเป็นแล้ว

บั๊กที่ยังอยู่

- FloatingActionButton บน Android 5.0+ ยังคงไม่มาพร้อม Margin Right/Bottom แต่บน Android รุ่น pre-Lollipop กลับมี Margin มาด้วย ต้องใช้ Configuration Qualifier กำหนดค่า Margin เอง

screenshot20

screenshot17n

บั๊กใหม่หรือ Behavior แปลกๆ

- ใน Android pre-Lollipop เวลาใส่ FAB ลงไปใน CoordinatorLayout เจ้า Margin ที่เคยมีก็มลายหายไป แต่ถ้าเอาไปใส่ไว้ใน Layout ตัวอื่นกลับมี Margin มาด้วย ไม่รู้ว่าอันไหนถูกต้อง แต่ต้องมีแค่อันเดียวแหละ คงต้องรอเวอร์ชั่นหน้าแล้วมาแก้ไขอีกที ส่วนตอนนี้ต้องจัดการกำหนด Margin เองไปก่อนครับ

screenshot20n
When placed inside FrameLayout

screenshot19n
When placed inside CoordinatorLayout

- Animation การเคลื่อนที่กลับของปุ่ม FAB ตอน Snackbar ถูก Swipe to Dismiss ดูแปลกๆและไม่ลื่นสวยงามเหมือนก่อนหน้านี้

ก็ประมาณนี้ครับ ยังไงก็รอเวอร์ชั่นต่อไปเนอะ ทางทีมน่าจะพัฒนากันอยู่อย่างต่อเนื่องครับ =)

ผู้เขียน: nuuneoi (Android GDE, CTO & CEO at The Cheese Factory)
นักพัฒนาแบบ Full-Stack ที่มีประสบการณ์ในการพัฒนาแอพฯแอนดรอยด์มากว่า 6 ปีและอยู่ในวงการพัฒนาแอพฯมือถือมากว่า 12 ปี มีความสนใจทางด้าน Infrastucture, Service Side, Design, UI&UX, Hardware, Optimization, Cooking, Photographing, Blogging, Training, Public Speaking และรักที่จะแชร์เรื่องราวให้ผู้คนได้อ่านได้ฟังกันผ่าน Blog