บันทึกการ Redesign แอพอ่านบล็อกของตัวเอง ให้เป็น Material Design

Android Jun 19, 2020

เนื่องจากเราเองก็อยากลอง redesign แอพอ่านบล็อกตัวเอง จนย้ายบล็อกมาที่ Ghost แล้ว จึงต้อง Redesign แอพอ่านบล็อก MikkiPastel ใหม่

ความเดิมตอนที่แล้ว

จับคุณผีมาช่วยในการทำเว็บบล็อกกันเถอะ กับ Ghost CMS
เรื่องราวการดุ๊ปบล็อกมาที่ใหม่ ซึ่งเรื่องนี้มันยาว เรียกย้ายก็ไม่ได้ ตอนนี้ยังดุ๊ป แค่ content ใหม่ๆจะมีที่นี่กับ blacklens pub และอื่นๆใน medium

ทำ Color Index ใหม่

สีแดงเดิมทีเป็นสีแดงของ Android นำมาปรับเป็นสีที่เรียกว่า Maguro Red (#B71D25) นั่นเองงงและสีรองลงมาสืบเนื่องจากสีพื้นหลังเดิมของบล็อกนี้เป็นสีคล้ายๆแซลม่อนสุก จึงนำมาเปลี่ยนเป็น Light Pink Salmon (#FF9999) แทน

ปรับโปรเจกในแอพใหม่

ตัวแอพบล็อกเดิมนั้นใช้ blogspot API เขียนด้วยภาษา Java ดังนั้นเราจะเปลี่ยนมาเป็น API ที่เราเขียนเองโดยข้อมูลทั้งหมดจะเก็บไว้ใน Cloud Firestore และเขียนด้วยภาษา Kotlin ซึ่งพยายามปรับไปเรื่อยๆ โดยจะทำความเข้าใจและเริ่มใช้ MVVM แทนของเดิมที่เป็น MVP เพื่อให้สามารถเล่นอะไรใหม่ๆได้ >> ยังไม่เสร็จจ้า เดี๋ยวเล่าแยกถ้าเสร็จแล้ว

เพิ่มลูกเล่นด้วย Lottie

แน่นอนว่าเราเองขี้เกียจทำ view แบบไม่ค่อยสวย เลยไปหา Lottie ต่างๆที่เป็น open source จาก https://lottiefiles.com/ เพื่อเอาไปใช้ในหน้าต่างๆ โดยเริ่มจากในแอพก่อน และตามด้วยในเว็บ

ตอน loading ใช้เจ้าไดโนเสาร์ตัวนี้ซึ่งค่อนข้าง made sense ดี

Dino Loading on Lottiefiles. Free Lottie Animation
LottieFiles is a collection of animations designed for Lottie and Bodymovin - gone are the days of bugging your developer

ตอน error ใช้ตัวนี้ แต่เขียน text กำกับไว้ด้านล่าง พร้อมปุ่ม re-loading

Loading Error on Lottiefiles. Free Lottie Animation
In a rare event that our backend has issues sending data to users when taking our mobile questionnaire.. Use on your web, react, flutter, xamarin iOS and Android projects and apps

ตอนโหลดก็คิดว่าอันนี้ไว้ด้านบนก็ไม่เลวนะ

loading on Lottiefiles. Free Lottie Animation
loading. Use on your web, react, flutter, xamarin iOS and Android projects and apps

ทดลองเรียกใช้ Ghost API เพื่อดึงบล็อกของเราขึ้นมาแสดง

ตอนนี้เลยหาวิธีสักหน่อยว่าเขาทำกันยังไงบ้างนะ ในการเรียก API จาก Ghost ว่ามีอะไรบ้าง

ก่อนอื่นไปที่ Settings > Integration แล้วก็สร้าง CUSTOM INTEGRATIONS ขึ้นมา เราก็จะได้ Content API Key, Admin API Key, และ API URL

การเรียกใช้งานเบื้องต้น เข้าไปดูได้ที่เว็บไซต์นี้เลย

Ghost Content API Documentation
Ghost’s RESTful Content API delivers published content to the world and can be accessed by any client to render a website. Read more on Ghost Docs.

ก่อนอื่น ทดลองเรียกตัวนี้ก่อน ใน Postman ก็ได้เนอะ เป็น method get

"https://demo.ghost.io/ghost/api/v3/content/posts/?key=22444f78447824223cefc48062"

จากนั้นเรามาลองใช้เองบ้างเนอะ มันจะเป็น format ตามนี้

{API_URL}/ghost/api/v3/content/posts?key={CONTENT_API_KEY}

อันนี้เอาไว้เรียก post เนอะ แต่ด้วยความที่มันพ่น field ออกมาเยอะจนเราแบ่บบ เออมันเยอะไปนะ แถมไม่มี tag อีก อ้าววว

ดังนั้นเราใส่ parameter เพิ่ม โดยเพิ่ม tags เข้ามา include=tags และเอาเฉพาะแค่ไม่กี่ field ที่ใช้ในแอพ คือ เอาแค่ชื่อบล็อก คำโปรย รูป cover เวลาที่ publish แบบนี้ fields=title,url,feature_image,custom_excerpt,published_at ผลที่ได้คือมันจะมีเฉพาะ field ที่เราต้องการหล่ะ

ใน API จะส่งบล็อกมา 15 blog ต่อครั้ง ดังนั้นถ้าเราจะเรียกหน้าถัดไป หรือหน้าอื่นๆ ให้ใส่อันนี้เข้าไปด้วย page=2

สิ่งหนึ่งที่น่าสนใจก็คือ ตรงท้ายจะมีก้อนนึงที่ชื่อว่า meta และมี pagination ซึ่งบอกเลยว่าดีมากๆ เวลาเราทำ lazy load จะได้รู้ว่าเอ้ออออโหลดหมดยังอ่ะ จะได้ไม่ต้องเสีย request ในเรียก API แล้วไม่ได้อะไรกลับมาเนอะ

แต่ละอันบอกอะไรเราบ้างหล่ะ

  • page ตอนนี้อยู่ที่หน้าเท่าไหร่
  • limit หน้านี้มี limit กี่ item
  • pages ทั้งหมดมีกี่หน้า
  • total ทั้งหมดนี้มีกี่ item กันนะ
  • next หน้าถัดไปคือหน้าเท่าไหร่
  • prev หน้าก่อนหน้าคือหน้าเท่าไหร่ ในที่นี้คือ null ก็คือไม่มีหน้าก่อนหน้านี้นั่นเอง

นอกจากเรียกบล็อกแล้ว เรามีการเรียก tag ทั้งหมดในบล็อก จะเป็นประมาณนี้

{API_URL}/ghost/api/v3/content/tags?key={CONTENT_API_KEY}

ตัว field จะเหมือนใน post อ่ะ ไม่มีอะไรมาก

Redesign หน้าแอพหล่ะนะ

ปีที่แล้วเราเองก็ทำการ research เรื่อง Material Design จนเราได้ตัว item ที่เป็น CardView แบบนี้ ที่สามารถกดการ์ดไปอ่านบทความ หรือกดที่ Chip เพื่อดู content ใน tag นั้นๆได้ ซึ่งเรา adapt จาก Material Design มาด้วยหล่ะ

คอนเซปที่พยายามใช้คือ Single Activity ซึ่งก็นึกถึงบล็อกนี้ เจอเป็นอันดับหนึ่งด้วยจ้า

[Thai] Android Single-Activity Architecture แค่ Activity เดียวก็พอแล้ว?
Activity เป็นสิ่ง Android developer ทุกคนคุ้นเคยกันเป็นอย่างดี และ Activity ก็อาจเป็นสิ่งแรกที่เราได้เรียนรู้ ในการศึกษาการพัฒนา Application Android เลยด้วยซ้ำ และใน Application ที่เราพัฒนากัน จะมี…

ดังนั้นเราจึงพยายามยุบให้เหลือ Activity เดียว แต่ด้วยความที่ตัว API ของการเรียกบล็อกรวมกับแยก tag ไม่ต่างกันมาก จึงยุบเหลือ Fragment เดียวเลย แล้วเราจะทำยังไงต่อน้าา?

เพิ่ม Dropdown list ด้วย AutoCompleteTextView

แน่นอนกด tag หรือถ้าเราเลือก tag แล้วควรจะ refresh list ใหม่

เลยคิดว่าอาจจะเป็นอะไรสักอย่างกดแล้วเลือกจาก Bottom Sheet แต่พอเราไปเปิดในหน้าเว็บของ Material Design เจอสิ่งที่เรียกว่า dropdown menu นั่นเอง

https://material.io/develop/android/components/text-fields/

ใน document บอกวิธีใส่และ implement ไว้แล้วจ้า แต่เรามาอธิบายเพิ่มดีกว่า เดี๋ยวงงๆเนอะ

ก่อนอื่นไปที่ layout ที่เราต้องการ ใส่ไปแบบนี้

อธิบายคร่าวๆ คือ

  • ใน fragment_main.xml มี parent คือ TextInputLayout อันนี้ก็เป็น layout ด้านบน TextField ต่างๆ เราใส่ label และ icon ด้านซ้ายเข้าไปด้วย ส่วน AutoCompleteTextView คือมันจะเป็น auto complete แต่เราไม่อยากให้พิมพ์อะไรลงไป อยากให้เอาไปเลือกว่าอยากอ่าน blog ที่ติด tag ไหน เลยทำให้มันจะไม่เป็นกรอบสีของ colorPrimary นะ
  • ส่วน item_hashtag.xml เป็น item view ใน adapter ที่เราไม่ต้องสร้าง class adapter เอง เพียงยัด layout เข้าไป เดี๋ยวอธิบายต่อ

จากนั้นในโค้ดของเรา MainFragment.kt เราทำการ call API ที่เรียก tag ของ Ghost API แล้วเอาชื่อ tag ที่ได้ไปใส่ใน dropdown list มาดูตัวอย่างการใช้กันก่อน

val items = listOf("Material", "Design", "Components", "Android")
val adapter = ArrayAdapter(requireContext(), R.layout.list_item, items)
(textField.editText as? AutoCompleteTextView)?.setAdapter(adapter)

เรามี list ของของสิ่งหนึ่ง จากนั้นสร้าง ArrayAdapter แล้วเอาเจ้า layout ของ item ใส่ลงไปพร้อม list จากนั้นทำการ setAdapter ดังนั้นของเราจะเป็นประมาณนี้

ถ้าเรากด Chip แล้วให้เจ้า AutoCompleteTextView เปลี่ยนตัวหนังสือเป็น hashtag ตัวนั้น ทำอย่างไรดีนะ ง่ายๆเลยแค่นี้เอง

dropdownList.setText(hashtag, false)

จริงๆถ้าใส่ setText(text) อย่างเดียว มันจะมีความบัคตรงที่ พอกด dropdown จะเห็นตัวมันเองอย่างเดียว

ดังนั้นจึงใช้ setText(text, filter) ที่รองรับ API level 17 ขึ้นไปนะ และใส่ค่าเป็น false เพื่อให้แสดง dropdown list ทั้งหมดลงมาแบบนี้

แล้วถ้าตอนกด dropdown หล่ะ เหมือนมันเลือกได้ แต่ตัวหนังสือไม่เปลี่ยน ใช้ setOnItemClickListener เข้าช่วย แบบนี้

dropdownListsetOnItemClickListener { adapterView, view, position, id ->
    dropdownList.setText(yourList[position], false)
    //make something happen
}

หลังจากกด dropdown list หรือจิ้มที่ Chip เราจะให้เรียก API เพื่อ get blog เฉพาะ tag นั้นๆขึ้นมาให้

ต่อมาเรามาลองเพิ่มเจ้า Collapsing Toolbars กันอยู่จ้า จริงๆมันก็แอบมีความซํ้าซ้อนอ่ะ เพราะเราเองก็เห็นว่าบล็อกเราตอนนี้แสดงตาม tag อะไรอยู่ แล้วมี cover ด้านบนมาอีกด้วย ฮ่าๆ

ก่อนอื่น set theme ให้เป็น NoActionBar ก่อนเลยจ้า แต่ดันมีแต่ Light นี่สิ

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">

ในนี้อ่านแล้วจะงงๆนิดนึงเนอะ

Collapsing Toolbars - Material Components for Android
Create beautiful apps with modular and customizable UI components

เริ่มจาก layout กันก่อนเลย ตัว parent จะเป็น CoordinatorLayout ที่มีลูกเป็น AppBarLayout อันคือ app bar ด้านบน และ LinearLayout ที่เป็นส่วน filter tag และแสดง content นั่นเอง

และใน AppBarLayout มีอะไรเป็นลูกบ้างหล่ะ? มี CollapsingToolbarLayout เป็นตัวลูก ที่มี 2 ส่วน ก็คือ ส่วนที่แสดงแบบใหญ่ๆ กับส่วน toolbar คือหลังจากเลื่อนลงมา ประมาณนี้

และอันนี้คือโค้ด layout เนอะ เดี๋ยวเรามาอธิบายกัน

เราเขียนแผนภาพอธิบายคร่าวๆกับ UI จริง เดี๋ยวจะอธิบายต่อด้านล่างเลยจ้า
  • ใน layout หน้านี้ มี parent เป็น CoordinatorLayout ที่มีลูก 2 ตัวคือ AppBarLayout และ LinearLayout
  • ตัว LinearLayout อันนี้ ก็คือตัว Fragment ที่เราใส่เข้าไป ในการเลือก tag และแสดง content นั่นเอง โดยเราบอกให้มันอยู่ด้ายล่าง appbar นะ โดย set เข้าไปดังนี้
app:layout_behavior="@string/appbar_scrolling_view_behavior"
  • ตัว AppBarLayout ก็จะมี layout ลูกเป็น CollapsingToolbar ที่ข้างในจะแบ่งอีก 2 ส่วน ก็คือ FrameLayout ที่เอาไว้แสดง cover และ text ของ tag นั้นๆ และ Toolbar ไว้เพื่อ cover ด้านบนนั้นถูก collap ไปแล้ว ก็ให้แสดงขึ้นมา และเราไม่อยากให้มันหายไปตอนเลื่อนดู content ดังนั้นเราจะใส่เพิ่มไปว่า
app:layout_collapseMode="pin"
  • ใน FrameLayout นั้น เราอยากให้ตัว ImageView มันเป็นฝ่าย collapse มากกว่าตัว layout แม่ เพราะลองไปใส่ที่ FrameLayout พบว่ามันแปลกๆ มันไม่สวย เวลาที่ย่อตัว cover ออกแล้วจะเปลี่ยนเป็น Toolbar ที่มี text อ่ะ โดยใส่ไปดังนี้
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.5"

เจ้า layout_collapseParallaxMultiplier คือเราสามารถ set ให้เจ้า layout ที่ว่านั้น ถูก collapse ไปตอนไหน ซึ่งเราใส่ไป 0.5 ก็หมายถึง ให้มันเริ่มหดๆหายๆ เมื่อเลื่อน layout ชิ้นนี้ไปประมาณ 50% ซึ่งค่าสามารถปรับแต่งตามความสวยงามของเราเองได้เลยจ้า

  • เราสามารถ implement พวก style ต่างๆในโค้ดได้ด้วย เช่น เราอยากให้ตอนมันหุบลงมามี title เป็นสีขาว ก็สามารถ set ได้แบบนี้
collapsingToolbar.setCollapsedTitleTextColor(
    ContextCompat.getColor(context!!, android.R.color.white)
)

ผลที่ได้จะเป็นแบบนี้จ้า

สำหรับ CoordinatorLayout สามารถอ่านเพิ่มเติมได้ที่

Using Coordinator Layout in Android
In this blog, we will learn how to use Coordinator Layout in Android. Coordinator Layout is super-powered FrameLayout that is used to handle the physical transactions or animations between various views present in a particular Activity.
Handling Scrolls with CoordinatorLayout | CodePath Android Cliffnotes
https://guides.codepath.com/android/handling-scrolls-with-coordinatorlayout
Mastering the Coordinator Layout · Saúl Molinero
https://saulmm.github.io/mastering-coordinator

ปุ่มสามจุดแนวตั้งสียังแปลกๆอยู่ ให้ set style เป็น drawable จุดสามจุดสีขาวใหม่ตามนี้เลย

Menu item icon changes color
I’ve created a menu with a single item. &lt;menu xmlns:android=“http://schemas.android.com/apk/res/android” xmlns:app=“http://schemas.android.com/apk/res-auto”&gt; &lt;item android...

ส่วนการกดที่ CardView แล้วไป Chrome Custom Tab จะยกไปที่บล็อกนี้แล้วกัน แบบปรับปรุงบล็อกใหม่ซะเลย

มาลองทำแอปบล็อกของตัวเองกันดีกว่า ตอนที่ 3 : การดึง content มาแสดงใน custom chrome tab
หลังจากที่เราได้ blogger json api มาใช้ร่วมกับ Retrofit กับ Gilde กันไปแล้วต่อมาเราก็จะมาใช้ custom chrome tab เพื่อมาอ่านบทความที่เราเขียนกัน

ใน app version beta น่าจะเท่านี้แหละ

ปล. จริงๆ TWA ก็น่าสนใจนะ แค่ของ Ghost เนี่ย ไม่รองรับ PWA อ่ะ ฮืออออออ คือมันต้อง custom เพิ่มอ่า

ทำ PWA ให้กลายเป็นแอปแอนดรอยด์แบบง่าย ๆ ด้วย TWA — ตอนที่ 1 Introduction
มีเว็ปที่เป็น PWA อยู่แล้ว อยากจะทำให้เป็นแอปแอนดรอยด์? ใช้ TWA สิ จะได้ไม่ต้องง้อ Add to Home Screen อีกต่อไป ในยุคนี้ที่เว็ปสามารถทำอะไรได้หลากหลายมากขึ้น แถมยังมี PWA ที่จะช่วยให้เว็ปแอป…

ตัว github ของโปรเจกนี้จ้า

GitHub - mikkipastel/MikkiPastel-Android: Application for read MikkiPastel’s blog in android
Application for read MikkiPastel’s blog in android - GitHub - mikkipastel/MikkiPastel-Android: Application for read MikkiPastel’s blog in android

จบแล้ว ฝากร้านสิ รอไร

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.