เบื้องหลังการสร้าง UI สำหรับคูปองเพื่อใช้งานในแอป LINE MAN ที่เป็นมิตรต่อเพื่อนร่วมทีม — ตอนที่ 2

Akexorcist
Life@LINE MAN Wongnai
3 min readJan 28, 2021

--

หลังจากที่เล่าเรื่องราวและที่มาของ UI Component ตัวใหม่ที่ผมต้องสร้างขึ้นมาเพื่อนำไปใช้งานในแอป LINE MAN โดยมีชื่อฟีเจอร์ว่า Coupon Book และในตอนสุดท้ายก็ได้ตัดสินใจเลือกที่จะสร้างขึ้นมาด้วย Canvas API

และในบทความนี้ผมก็ขอพาเข้าสู่เนื้อหาของโค้ดหรือคำสั่งต่าง ๆ ที่จะอยู่เบื้องหลังในการสร้าง UI Component ตัวนี้นั่นเอง

บทความทั้งหมดของชุดนี้

การสร้างรูปคูปองด้วย Canvas API

โดย Custom View บนแอนดรอยด์จะรองรับ Canvas API ในตัวอยู่แล้ว เพราะในคำสั่ง onDraw(canvas: Canvas?) หนึ่งใน Lifecycle ของ View ส่ง Canvas เข้ามาให้ด้วย เพื่อให้นักพัฒนาสามารถใช้คำสั่งต่าง ๆ ของ Canvas API ใน Custom View ตัวนั้น ๆ ได้เลย

และคำสั่งของ Canvas API ที่เป็นหัวใจสำคัญของ UI ตัวนี้ก็คือ drawPath(path: Path, paint: Paint) เพราะเราสามารถใช้ Path เพื่อสร้างเส้นที่ลากต่อกันให้เป็นรูปคูปองได้

Path ใน Canvas API

เพื่อให้ได้ Path เป็นรูปคูปอง จะต้องใช้คำสั่งในลักษณะแบบนี้ (ยังไม่ถูกถึงการคำนวณตำแหน่งของ Path ในแต่ละจุด)

โดย reset() จะเป็นการเคลียร์ค่าที่มีอยู๋ใน Path (เวลาเอาตัวแปรเดิมมาใช้ซ้ำ) แล้วจึงใช้คำสั่ง moveTo(...) เพื่อกำหนดจุดเริ่มต้นของ Path

เมื่อดูจากโค้ดตัวอย่างจะเห็นว่านอกจากคำสั่งพื้นฐานอย่าง reset(), moveTo(...) และ close() แล้ว นอกนั้นก็จะเป็นคำสั่ง arcTo(...) ทั้งหมด

ทั้งนี้ก็เพราะว่าเวลาใช้คำสั่ง arcTo(...) ในแต่ละครั้ง ไม่ว่าตำแหน่งสุดท้ายของเส้น Arc จะไปจบที่ไหน การใช้ Path ก็จะลากเส้นตรงไปหาจุดเริ่มต้นของเส้น Arc ตัวถัดไปให้โดยอัตโนมัตินั่นเอง

และคำสั่ง close() ที่ใช้ในบรรทัดสุดท้ายของการสร้าง Path ก็จะทำให้จุดสุดท้ายของ Path มาบรรจบที่จุดเริ่มต้นของ Path ให้โดยอัตโนมัติ

ปัจจัยที่มีผลต่อการคำนวณตำแหน่งในแต่ละจุดของ Path

  • ความหนา (Width) ของเส้นขอบ (Border)
  • รัศมีความโค้ง (Radius) ของแต่ละมุม (Corner)
  • รัศมีครึ่งวงกลม (Radius) ของขอบ (Edge) แต่ละด้าน

และในการคำนวณสำหรับ Canvas API จะคำนวณเป็นหน่วย Pixel ทั้งหมด

การคำนวณตำแหน่งในแต่ละจุดของ Path

เพื่อให้ง่ายต่อการคำนวณตำแหน่งแต่ละจุดของ Path ให้สมมติค่าสำหรับปัจจัยทั้งหมดขึ้นมาเพื่อใช้หาสูตรคำนวณของตำแหน่งแต่ละจุดนั่นเอง

ผมจึงสมมติว่า

  • พื้นที่ของ Canvas (ซึ่งก็คือพื้นที่ของ View) มีขนาด 100 x 100px
  • เส้นขอบ (ซึ่งก็คือเส้นของ Path) มีความหนา 2px
  • มุมโค้งมีรัศมี 10px
  • ครึ่งวงกลมของขอบแต่ละด้านมีรัศมี 20px

ในการอ้างอิงตำแหน่ง X หรือ Y เพื่อคำนวณด้วยสมการที่จะใช้ในการสร้าง Custom View จะมีอยู่ทั้งหมด 3 รูปแบบด้วยกัน

  • อ้างอิงจากจุดเริ่มต้นของพื้นที่ทั้งหมด (Origin Point)
  • อ้างอิงจากค่าสูงสุดของพื้นที่ทั้งหมด (Max Point)
  • อ้างอิงจากกึ่งกลางของพื้นที่ทั้งหมด (Center Point)

และการคำนวณทั้งหมดจะมีเรื่องความหนาของเส้น Path เข้ามาเกี่ยวข้องด้วย เพราะเวลาลากเส้นไปทางใดทางหนึ่งจะกินพื้นที่ด้านข้างโดยคำนวณจาก line_width / 2 เสมอ จึงต้องมีการคำนวณเผื่อความหนาของเส้น Path ด้วยทุกครั้ง

ตัวอย่างเส้นที่มีจุดเริ่มต้น (1, 1) และจุดปลายทาง (20, 1)

จุดเริ่มต้นของ Path

โดยจะเริ่มจากคำนวณตำแหน่งของจุดเริ่มต้นของ Path เสียก่อน เมื่อดูจากโค้ดก่อนหน้านี้ จุดเริ่มต้นจะเป็นจุดเริ่มต้นของเส้น Arc ที่อยู่มุมขวาบนนั่นเอง ซึ่งจะคำนวณได้จาก

ดังนั้นคำสั่ง moveTo(...) ก็จะได้ออกมาเป็น

เมื่อลองแทนค่าที่สมมติขึ้นมาก็จะได้เป็นตำแหน่ง (89, 1) นั่นเอง

เส้น Arc สำหรับมุมโค้ง

ในการกำหนดเส้น Arc ให้กับ Path นั้นจะใช้หลักการกำหนดพื้นที่สี่เหลี่ยมสำหรับวงกลม แล้วกำหนดส่วนของรัศมีวงกลมที่ต้องการ

สี่เหลี่ยมจัตุรัสจะได้เป็นส่วนโค้งของวงกลม
สี่เหลี่ยมผืนผ้าจะได้เป็นส่วนโค้งของวงรี

นั่นหมายความว่าผมจะต้องกำหนดพื้นที่สี่เหลี่ยมจัตุรัสเพื่อวาดมุมโค้งนั่นเอง โดยเส้น Arc สำหรับมุมโค้งขวาบนจะคำนวณได้จาก

และเมื่อต้องการให้ลากส่วนของวงกลมจากข้างบนไปทางขวามือก็จะได้ค่าสำหรับ Start Angle และ Sweep Angle เป็น

พื้นที่สีฟ้าคือพื้นที่สี่เหลี่ยมที่ได้จากการคำนวณในสมการก่อนหน้านี้เพื่อใช้วาดเส้น Arc

ซึ่งจะได้คำสั่งสำหรับมุมโค้งขวาบนเป็นแบบนี้

เส้น Arc ครึ่งวงกลมสำหรับขอบด้านข้าง

ในการลากเส้น Arc ให้เป็นครึ่งวงกลมสำหรับขอบด้านข้างรูปคูปองจะใช้หลักการเดียวกับมุมโค้ง ต่างกันแค่พื้นที่สี่เหลี่ยมและองศาของรัศมีวงกลมที่เริ่มจากคนละฝั่งและลากไป 180°​ (Sweep by) นั่นเอง

แต่เนื่องจากเป็นครึ่งวงกลมที่เว้าเข้ามาในพื้นที่ของคูปอง จึงทำให้การคำนวณชวนสับสนนิดหน่อย เพราะพื้นที่สี่เหลี่ยมจะมีบางส่วนที่อยู่นอกพื้นที่ของ Canvas อีกทั้งยังต้องชดเชยตำแหน่งจากความหนาของเส้นเพื่อให้สามารถสร้างเส้น Arc ได้ตามที่ต้องการ

พื้นที่สีฟ้าคือพื้นที่สี่เหลี่ยมที่ได้จากการคำนวณเพื่อใช้วาดเส้น Arc

สำหรับเส้น Arc แบบครึ่งวงกลมที่กึ่งกลางของขอบฝั่งขวาจะคำนวณได้จาก

ซึ่งจะได้ออกมาเป็นคำสั่งแบบนี้

และอย่างที่บอกไปในตอนแรกว่าการสร้างเส้น Arc ใน Path เดียวกันจะเกิดเส้นตรงที่เชื่อมระหว่างเส้น Arc ทั้ง 2 ให้โดยอัตโนมัติ ดังนั้นถ้าการคำนวณนั้นถูกต้องเส้นดังกล่าวก็จะต้องลากเป็นเส้นตรงแนวตั้งเพื่อเชื่อมทั้ง 2 จุดเข้าด้วยกัน

ทำเหมือนเดิมจนครบทุกด้านเพื่อให้เป็นรูปคูปองดั่งที่ต้องการ

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

แต่ถ้าอยากดูสูตรคำนวณทั้งหมด ก็สามารถเข้าไปดูได้ที่ Coupon UI — All Formulas (Gist GitHub) เลยครับ

อาจจะดูเหมือนเสร็จเรียบร้อยแล้ว แต่จริง ๆ ยังเหลือขั้นตอนที่ทำให้ UI Component ตัวนี้มีความเป็น Android Developer-Friendly มากขึ้นอีกนะครับ​

และขั้นตอนต่อไปจะเป็นอย่างไร โปรดติดตามอ่านต่อไปในตอนที่ 3 นะครับ

ถ้าคุณชอบบทความนี้ก็สนับสนุนด้วยการกด Clap หรือ Share ให้บทความของผม แต่ถ้าอยากมาเป็นส่วนหนึ่งของทีมพัฒนาแอป LINE MAN ผู้ช่วยส่วนตัวในชีวิตประจำวันของคนไทย ก็สามารถเข้ามาดูรายละเอียดของงานในแต่ละตำแหน่งกันได้ที่ careers.lmwn.com นะครับ 😉

--

--

Lovely android developer who enjoys learning in android technology, habitual article writer about Android development for Android community in Thailand.