ทำไม เพจ MikkiPastel และเพจอื่น ๆ ถึงใช้ . ระหว่างบรรทัด มาอธิบายด้วย coding กัน
เห็นหัวข้อมันดู geek แน่ ๆ เลย จริง ๆ มันมีหลักการและเหตุผลบางอย่างประกอบ เราเลยมาอธิบายแบบง่าย ๆ แบบเห็นภาพว่าทำไม จากประสบการณ์ที่เคยทำแอพเกี่ยวกับ social มาก่อน
ทำไมถึงต้องใช้ .
ก่อนอื่นเลย หลาย ๆ คนคงจะเคยเห็น content ประมาณนี้จากเพจเรามาบ้างแล้ว และแน่นอนว่าเพจอื่นก็มี content ประมาณนี้เช่นกัน
ความเหมือนกัน ตรงเนื้อหา สังเกตได้ว่า ระหว่างบรรทัดมี . คั่นอยู่เหมือนกัน
ลองดูโพสที่เนื้อหาเหมือนกัน แต่ไม่มี . ทุกคนคิดเห็นอย่างไรบ้างนะ อ่านแล้วดูเป็นยังไง ชอบอันไหนมากกว่า อันไหนอ่านง่ายกว่า
การที่เราเขียนบรรทัดใหม่บน Facebook นั้น เราก็กด enter ไป
ที่นี้เราอยากเว้นอีกบรรทัดนึงเพื่อให้มันอ่านง่ายขึ้น แต่แล้วพอโพสไป มันก็เอา enter ที่เหมือนกันออกไป เลยเหมือนขึ้นบรรทัดใหม่นั่นเอง
พูดแบบ coding ง่าย ๆ คือ การที่เรา enter คือเป็นการขึ้นบรรทัดใหม่ เป็น \n
แล้วที่นี้เรา enter สักสามที มันจะเป็น \n\n\n
แต่พอมันเอาไปขึ้นหน้าโพสจริง มันจะลด \n
ที่เกินมา เหลือแค่ \n
เดียว ซึ่งอาจจะถูกตัดไปตอนโพสแหละ
ปล. บางแอพ \n\n
เป็น text block ใหม่นะ
ข้อมูลแต่ละ post ประกอบด้วยอะไรบ้าง
จากประสบการณ์ที่เคยทำแอพเกี่ยวกับ social มา เราพอจะรู้แหละว่าแต่ละแอพมันจะมีอะไรคล้าย ๆ กัน
สมมุติว่าหน้า feed ของเพจ เราเรียก 10 post ล่าสุดขึ้นมา อันนี้เราเรียกเป็น list ของก้อนที่เรียกว่า post ก็แล้วกัน ถ้าเป็น coding จะประมาณนี้ ArrayList<Post>
val profilePost = arrayListOf(
Post(...),
Post(...),
Post(...),
Post(...),
Post(...),
Post(...),
Post(...),
Post(...),
Post(...),
Post(...)
)
แต่ละ Post()
ข้างในก็จะมี content ต่าง ๆ ภายใน อย่าง item แรกเป็นประมาณนี้แล้วกัน
Post(
id = 100,
content = arrayListOf(
ContentItem(
type = ContentType.TEXT
text = "เคยสับสนไหมว่า Empty กับ Blank ต่างกันยังไง?\n.\nแน่นอน ทางนี้จำถูก จำผิดตลอดเลย55555555\nแล้วต่างกันยังไง ไปดูกัน\nกับตัวอย่างที่เป็นภาษา Kotlin ..."
),
ContentItem(
type = ContentType.IMAGE
imageUrl = "https://example.com/post.jpg"
)
),
// ตรงนี้ใส่ให้เต็มเฉย ๆ เอาไป render ว่าใครเป็นคนโพส โพสเมื่อไหร่
createdAt = "2023-04-16T18:38:36",
user = User(
userId = 6238545,
userName = "MikkiPastel",
avatarUrl = "https://example.com/avatar.jpg"
),
stat = Stat(
like = 100,
share = 2,
comment = 0
)
)
มาดู class ที่ใช้คร่าว ๆ จะมี Post()
กับ ContentItem()
เนอะ โดยตัว ContentType
เป็น enum class เนอะ ง่าย ๆ คือบอกว่า ContentItem ของเรานั้น มีทั้งหมด 4 type คือ text image video link
data class Post(
val id: Int,
val content: ArrayList<ContentItem>,
val createdAt: String
val user: User,
val stat: Stat
)
data class ContentItem(
val type: ContentType,
val text: String?,
val imageUrl: String?,
val videoUrl: String?
val linkUrl: String?
)
enum class ContentType {
TEXT, IMAGE, VIDEO, LINK
}
// ตรงนี้ใส่ให้เต็มเฉย ๆ เอาไป render ว่าใครเป็นคนโพส โพสเมื่อไหร่
data class User(
val userId: Int,
val userName: String,
val avatarUrl: String?
)
data class Stat(
val like: Int,
val share: Int,
val comment: Int
)
และแต่ละองค์ประกอบเรียงร้อยออกมาเป็น post ซึ่งแน่นอนว่า image video และ link มันจะอยู่ล่างสุดเสมอ สำหรับ Facebook นะ ส่วนแอพอื่นอาจจะวางพวก media อื่นสลับกับ text ได้
เอามาขึ้น UI จริง
หน้าที่ของหน้าบ้าน คือไปเรียก API มา แล้วเอาข้อมูลที่ได้มาแสดง
เบื้องต้นเราจะได้ 10 post เอามาขึ้น UI โดยมองว่าเราจะมีหน้าตาแบบนี้ 10 อัน เรียงกันแบบนี้
และในแต่ละอันจะมี content ของ post นั้น ๆ อยู่ด้วย เราก็ render โดยการ check type ของ element ว่าเป็นอันไหน แสดงตามที่ได้
อันนี้เป็นตัวอย่าง Adapter class ในการแสดง content หน้าตาประมาณนี้ แต่ละ type ก็จะมีการแสดงผลเป็นของตัวเอง
class PostContentAdapter(
private val contentItems: List<ContentItem>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemCount() = contentItems.size
override fun getItemViewType(position: Int): Int {
return contentItems[position].type.ordinal
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (ContentType.values()[viewType]) {
ContentType.TEXT -> TextViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_text_content, parent, false)
)
ContentType.IMAGE -> ImageViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_image_content, parent, false)
)
ContentType.VIDEO -> VideoViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_video_content, parent, false)
)
ContentType.LINK -> LinkViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_link_content, parent, false)
)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val contentItem = contentItems[position]
when (holder) {
is TextViewHolder -> holder.bind(contentItem)
is ImageViewHolder -> holder.bind(contentItem)
is VideoViewHolder -> holder.bind(contentItem)
is LinkViewHolder -> holder.bind(contentItem)
}
}
...
}
สำหรับ text ในการแสดงผล ก็แสดงผลตรง ๆ ตามที่ได้รับจาก API เลย
class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val textView: TextView = itemView.findViewById(R.id.textView)
fun bind(contentItem: ContentItem) {
textView.text = contentItem.text
}
}
ในเมื่อตอนสร้างโพสตัดให้เหลือ enter เดียวแล้ว
ดังนั้นการใส่ . เพื่อคั่นระหว่างบรรทัด เป็นการสร้าง text ที่มี . ทำให้ดูมีระยะห่างระหว่างกันมากขึ้นเนอะ ดูอ่านง่ายขึ้นนั่นเอง
ปล. ของเว็บเข้าใจว่าเราน่าจะ spilt \n แล้วมาขึ้นในแต่ละ element ของ content
ในมุมของ content creator
แน่นอนว่าเราอยากให้ content คุณภาพที่เราตั้งใจทำนั้น ทุกคนชอบ มี engagement ดี ๆ กันใช่ไหมล่ะ ซึ่งเราก็ไม่อยากให้คนอื่นเลื่อนผ่านไป
การเขียนให้อ่านได้ง่าย ก็เป็นหนึ่งในปัจจัยที่ทำให้เห็นโพสเราแล้วอยากอ่านต่อ ถ้าเราเห็น text ติดกันเป็นพรึ้ด เราก็คงไม่อยากอ่านใช่ไหมล่ะ
การใส่ . คั่นในแต่ละส่วนของบทความนั้น ช่วยแก้ปัญหา text ที่ติดกันเป็นพรึ้ด ให้ดูอ่านง่ายขึ้น
แต่การเขียนโพสมีหัวข้อใหญ่ และมีหัวข้อย่อยลงมาด้วย บางคนอาจจะใส่ hashtag ที่หัวข้อย่อยช่วย หรืออาจจะใส่ emoji นำหน้า และมีอีกหนึ่งวิธี คือ การใช้ . อีกหนึ่งบรรทัด ในการแยกเนื้อหาหัวข้อย่อยออกจากกัน
ตัวอย่างจ้า
.
หวังว่าทุกคนจะได้ประโยชน์ไม่น้อยก็มากเนอะ
ติดตามข่าวสารตามช่องทางต่าง ๆ และทุกช่องทางโดเนทกันไว้ที่นี่เลย แนะนำให้ใช้ tipme เน้อ ผ่าน promptpay ได้เต็มไม่หักจ้า
ติดตามข่าวสารแบบไว ๆ มาที่ Twitter เลย บางอย่างไม่มีในบล็อก และหน้าเพจนะ