นักพัฒนาส่วนใหญ่ล้วนจะคุ้นชินกับการ Custom Font ภายในแอพกันอยู่แล้วเนอะ เพราะว่าไม่ใช่ทุกแอพที่ออกแบบมาเพื่อให้ใช้ System Font ซึ่งวันนี้เจ้าของบล็อกก็ขอแชร์ประสบการณ์เรื่องของ Font เล่นๆซักหน่อยดีกว่า
เราจะ Custom Font ไปเพื่ออะไร?
แหม่ ก็แน่นอนอยู่แล้วเนอะว่าเรา Custom Font เพราะว่าอยากให้ Font ดูโดดเด่น เมื่อเทียบกับแอพทั่วๆไป และจริงๆแล้วการดีไซน์แอพบางครั้งก็ต้องใช้ Font ที่ทำให้ดูกลมกลืนและลงตัวกับดีไซน์รูปแบบนั้นๆ
ยกตัวอย่างเช่น Airbnb ที่ใช้ Custom Font ในแอพทั้งหมด จึงทำให้รู้สึกว่าสะดุดตากับ Font ที่ดูเข้ากันกับดีไซน์ภายในแอพ โดยเฉพาะตัวเลขของ Font ที่ทำให้รู้สึกสะดุดตาเป็นอย่างมากเวลาเอามาใช้แสดงราคาของที่พัก
การ Custom Font จึงไม่ใช่แค่ว่าอยากจะเลือก Font อะไรมาใช้ก็ได้ เพราะต้องดูความเหมาะสมกับดีไซน์และการใช้งานของแอพนั้นๆด้วยนะเออ
และนอกจากนี้การ Custom Font ยังสะดวกในการแก้ไขที่ตัว Font ได้โดยตรง ในขณะที่ System Font ไม่สามารถทำอะไรกับมันได้เลย
• อ่านง่าย ไม่ต้องใช้เวลาในการทำความเข้าใจตัวหนังสือ
• เหมาะสมกับดีไซน์ที่ออกแบบไว้ เพราะ Font ไม่ได้ทำให้แอพสวยและดูดีครับ แต่ Font เป็นเพียงแค่ส่วนหนึ่งที่ทำให้แอพสวยและดูดีต่างหาก ดังนั้นจึงควรใช้ Font ที่มีลักษณะเป็นไปในทิศทางเดียวกับดีไซน์ที่ใช้
• เป็น Font ที่ถูกลิขสิทธิ์ Font ไหนแจกให้ใช้ฟรีก็ไม่ต้องห่วงอะไร แต่ถ้า Font ไหนขายก็ควรซื้อให้ถูกต้อง
สำหรับงานแอพนั้นเจ้าของบล็อกแนะนำว่าไม่ควรใช้ Font มากกว่า 2 แบบขึ้นไปครับ เพราะลำบากนักพัฒนา ฮาๆ ทางที่ดีควรเป็น Font แบบเดียวแล้วใช้ทั้งแอพเลยดีกว่า อาจจะสามารถแยกเป็น Typeface ก็ได้เพื่อให้ข้อความแต่ละส่วนภายในแอพมีความแตกต่างกัน
ยกตัวอย่างเช่น Airbnb ที่ใช้ Custom Font ในแอพทั้งหมด จึงทำให้รู้สึกว่าสะดุดตากับ Font ที่ดูเข้ากันกับดีไซน์ภายในแอพ โดยเฉพาะตัวเลขของ Font ที่ทำให้รู้สึกสะดุดตาเป็นอย่างมากเวลาเอามาใช้แสดงราคาของที่พัก
การ Custom Font จึงไม่ใช่แค่ว่าอยากจะเลือก Font อะไรมาใช้ก็ได้ เพราะต้องดูความเหมาะสมกับดีไซน์และการใช้งานของแอพนั้นๆด้วยนะเออ
และนอกจากนี้การ Custom Font ยังสะดวกในการแก้ไขที่ตัว Font ได้โดยตรง ในขณะที่ System Font ไม่สามารถทำอะไรกับมันได้เลย
Font ที่จะเลือกมาใช้
ขอให้ยึดหลักพื้นฐาน 3 อย่างก็พอครับ• อ่านง่าย ไม่ต้องใช้เวลาในการทำความเข้าใจตัวหนังสือ
• เหมาะสมกับดีไซน์ที่ออกแบบไว้ เพราะ Font ไม่ได้ทำให้แอพสวยและดูดีครับ แต่ Font เป็นเพียงแค่ส่วนหนึ่งที่ทำให้แอพสวยและดูดีต่างหาก ดังนั้นจึงควรใช้ Font ที่มีลักษณะเป็นไปในทิศทางเดียวกับดีไซน์ที่ใช้
• เป็น Font ที่ถูกลิขสิทธิ์ Font ไหนแจกให้ใช้ฟรีก็ไม่ต้องห่วงอะไร แต่ถ้า Font ไหนขายก็ควรซื้อให้ถูกต้อง
สำหรับงานแอพนั้นเจ้าของบล็อกแนะนำว่าไม่ควรใช้ Font มากกว่า 2 แบบขึ้นไปครับ เพราะลำบากนักพัฒนา ฮาๆ ทางที่ดีควรเป็น Font แบบเดียวแล้วใช้ทั้งแอพเลยดีกว่า อาจจะสามารถแยกเป็น Typeface ก็ได้เพื่อให้ข้อความแต่ละส่วนภายในแอพมีความแตกต่างกัน
การ Custom Font เบื้องต้น
คงรู้กันอยู่แล้วเนอะว่า Text View ที่เรียกใช้กันสามารถกำหนด Custom Font ผ่านโค๊ดได้เลยแบบนี้
โดยที่ your_custom_font.tff ก็คือไฟล์ Font ที่เตรียมไว้แล้วในโฟลเดอร์ assets นั่นเอง
TextView textView = (TextView) findViewById(R.id.text_view);
Typeface typeface = Typeface.createFromAsset(getAssets(), "your_custom_font.ttf");
textView.setTypeface(typeface);
โดยที่ your_custom_font.tff ก็คือไฟล์ Font ที่เตรียมไว้แล้วในโฟลเดอร์ assets นั่นเอง
แต่ส่วนใหญ่จะไม่ใช่วิธีแบบนี้กัน เพราะว่า Text View ในแอพหนึ่งตัวจะมีจำนวนเยอะมากๆ ดังนั้นถ้าต้องมานั่งพิมพ์คำสั่งแบบนี้ทั้งหมดก็วุ่นวายกันพอดีเนอะ ดังนั้นวิธียอดนิยมที่ใช้กันก็คือการทำ Custom TextView โดยในคลาสนี้ใส่คำสั่ง Custom Font ไว้เลย
เวลาเอาไปใช้ก็ประกาศใน Layout XML แบบนี้ได้เลย
สะดวกสบายขึ้นเนอะ? เพราะว่าไม่ต้องมานั่งพิมพ์คำสั่งทุกอัน อันไหนอยากเปลี่ยนเป็น Font ที่ต้องการก็ประกาศไว้ใน Layout XML ได้เลย เวลาเรียกใช้งานก็เรียกมาเป็นคลาส TextView ได้เลย ไม่จำเป็นต้องเป็นคลาส CustomTextView และสามารถใช้กับ View ตัวอื่นๆได้ ไม่ว่าจะเป็น Button, RadioButton หรือ Toggle Button ก็ตาม
และอีกวิธีที่ดีกว่านี้ก็คือใช้ Library ที่ชื่อว่า Calligraphy ครับ ซึ่ง Library ตัวนี้จะใช้ Intentional บน Layout XML เพื่อให้สามารถกำหนด Custom Font ได้ง่ายขึ้น แต่มีข้อแม้ว่าต้องโยน Context เข้าไปให้ CalligraphyContextWrapper ด้วยให้กับ Activity ทุกๆตัว
เวลาใช้งานก็แค่กำหนดใน Layout XML แบบนี้
ชื่อ Font ที่กำหนดไว้ใน fontPath จะอ้างอิงจากในโฟลเดอร์ assets โดยอัตโนมัติ ถ้าอยู่ใน Sub Directory อีกที ก็สามารถใส่ Path ลงไปในนี้ได้เลย และมีเงื่อนไขว่าจะต้องประกาศ Ignore Missing Prefix ด้วย เพราะเดี๋ยว Android Studio จะ Build ไม่ผ่าน เนื่องจาก fontPath ไม่มี Prefix
และนอกจากนี้ Library ตัวนี้ยังสามารถกำหนด Default Custom Font ที่จะผลลัพธ์กับ View ทุกตัวที่ไม่ได้กำหนด fontPath ได้ด้วยนะเออ เหมาะมากกับแอพที่ต้องการเปลี่ยน Custon Font ด้วยคำสั่งเดียวแต่มีผลลัพธ์กับทั้งแอพ รวมไปถึงการทำ Custom Font ไว้ใน Style เพื่อเอาไปกำหนดใน Layout XML อีกที เพื่อให้สามารถใช้งาน Custon Font หลายๆตัวได้สะดวก (ไปอ่านใน Readme ของ Library ตัวนี้เองนะ)
ถ้าผู้ที่หลงเข้ามาอ่านใช้ Library อย่าง Calligraphy จะมีปัญหาอย่างหนึ่งก็คือมันไม่สามารถแสดงใน Layout Preview ได้ เพราะว่ามันจะทำงานเมื่อติดตั้งลงบนเครื่องและเปิดขึ้นมาจริงๆ ซึ่งต่างจากการทำ Custom View เพราะว่าสามารถแสดงให้เห็นใน Layout Preview ได้ทันที (แต่ถ้ามีการแก้ไขโค๊ดใน Custom View Class ก็จะต้อง Build Gradle ใหม่ก่อนนะ ถึงจะเห็นผลลัพธ์)
ดังนั้นเจ้าของบล็อกจึงชอบใช้วิธีสร้าง Custom View ขึ้นมาแทน เพื่อที่จะได้จัดบน Layout Preview ได้สะดวก
แต่ก็ควรรู้ไว้ด้วยนะว่าการ Preview Custom Font เนี่ย จะมีเงื่อนไขว่าต้อง Preview บน API 21 ขึ้นไปเท่านั้น (Lollipop) ไม่สามารถ Preview บนเวอร์ชันที่ต่ำกว่านั้น และจะแจ้ง Rendering Problems แบบนี้
ในเมื่อ Preview บนเวอร์ชันที่ต่ำกว่านั้นไม่ได้ก็ช่างมัน แต่เพื่อหลีกเลี่ยงข้อความ Rendering Problem จึงนิยมใช้ If เพื่อเช็คว่ากำหนดอยู่ใน Edit Mode และเป็นเวอร์ชันมากกว่า Lollipop หรือป่าว
ถ้าถามว่ามันทำให้ Preview บนเวอร์ชันต่ำกว่าได้มั้ย? ก็ไม่หรอก แค่มันเลี่ยงไม่ให้แสดงข้อความ Rendering Problem เฉยๆนั่นแหละ ดังนั้นถ้าอยาก Preview เพื่อดูการแสดงผลของ Custom Font ก็ให้กำหนด Preview เป็นเวอร์ชัน Lollipop ขึ้นไปนะ
จากภาพข้างบนจะเห็นว่า Font คนละแบบที่มีขนาด 18sp เท่ากัน กลับมีขนาดที่แสดงบนหน้าจอจริงๆไม่เท่ากัน รวมไปถึง Line Spacing ก็ไม่เท่ากันด้วย
ดังนั้นจึงเป็นสาเหตุที่ต้องใช้ Layout Preview เข้ามาช่วยในขณะที่จัด Layout เพราะไม่เช่นนั้นอาจจะทำให้เพี้ยนได้ และ Font ส่วนใหญ่นั้นจะมี Line Spacing (ระยะห่างระหว่างแต่ละบรรทัด) เยอะกว่า System Font ไม่สิ ต้องบอกว่า System Font มี Line Spacing น้อยกว่าปกติสิเนอะ
สำหรับขนาดของตัวหนังสือก็คงแก้ไขไม่ยาก ถ้าใหญ่เกินปกติก็ปรับให้มีขนาดเล็กลง แต่ถ้ามีขนาดเล็กกว่าปกติก็ปรับให้ใหญ่ขึ้นเท่านั้นเอง และสำหรับ Line Spacing ก็สามารถแก้ไขได้ด้วยการใส่คำสั่งในโค๊ด
เอ....ถ้าใส่ใน Layout XML นั่นก็หมายความว่าต้องใส่เกือบทุกอันสิ... ถ้าแบบนั้นย้ายไปใส่ไว้ในคลาส CustomTextView ไปเลยดีกว่ามั้ง
อยากจะปรับ Line Spacing มากน้อยแค่ไหนก็ตามใจชอบเลยนะ
ระวังเรื่อง Margin หรือ Padding ของ View ที่อาจจะไปทำให้ตัวหนังสือหายไปบางส่วนด้วย
ในกรณีที่เป็นเรื่องของ Line Spacing เจ้าของบล็อกก็จะเอาไฟล์ Font มาแก้ไขด้วยโปรเจคโดยตรงไปเลย เพราะขี้เกียจมานั่งใส่คำสั่งในโค๊ด ฮาๆ
แต่บางครั้งก็อาจจะเจอปัญหา Padding ของฟอนต์นั้นๆแสดงผลไม่ค่อยดีซักเท่าไรในแอพแอนดรอยด์ และบางครั้งมันแสดงบน Phone ได้นะ แต่พอลองแสดงบน Tablet กลับเพี้ยนซะงั้น
เฮ้อ จะบ้าตาย
แต่ปัญหานี้ก็สามารถแก้ไขได้ด้วยการไม่ใช้ Padding ที่มากับ Custom Font ตัวนั้นๆ ซึ่งสามารถกำหนดได้ทั้งแบบ XML และ Programmatically ดังนี้
หรือ
เพียงเท่านี้ก็จะช่วยให้ Padding ของ Custom Font ที่ใช้กลับมาดูดีขึ้นหน่อย แต่ก็อาจจะต้องปรับค่า Padding อีกเล็กน้อยเพื่อให้เข้าที่เข้าทาง
ถ้าบน Photoshop จะเรียกว่า Font Style และในงานสาย Typography จริงๆเค้าจะเรียกว่า Font Family นะจ๊ะ เพราะงั้นเวลาไปคุยกับ Graphic Designer ก็ให้ใช้คำว่า Font Style หรือ Font Family ดีกว่า
แต่เมื่อผู้ที่หลงเข้ามาอ่านใช้ Custom Font นั่นก็หมายความว่าจะต้องเตรียมไฟล์ Font โดยแบ่งตาม Typeface ทั้งหมด 4 ไฟล์
ถ้า Font ที่เอามาใช้ดันมีแค่ Regular อย่างเดียวก็เสียใจด้วยนะจ๊ะ
ซึ่งการเอามาใช้งานถ้าใช้ Library อย่าง Calligraphy เข้ามาช่วยก็สะดวกเลย เพราะทำเป็น Style แยกตาม Typeface แล้วเรียกใช้ตามต้องการได้ แต่ในกรณีที่ทำเป็น Custom View ก็จะต้องมีการแก้ไขโค๊ดเล็กน้อยเพื่อให้รองรับ Typeface หลายๆแบบ
ไม่ต้องตกใจกับโค๊ดที่รกยุบยับนะครับ เพราะในการรองรับ Typeface หลายๆแบบมันจะต้องเช็คว่า ณ ตอนนั้น Typeface ที่กำหนดไว้เป็นแบบไหน แล้วจึงเลือกไฟล์ Font ให้ตรงกับแต่ละ Typeface นั่นเอง
ถ้าอยากเปลี่ยน Typeface ผ่านโค๊ดก็ใช้คำสั่งแบบนี้
ที่ต้องทำอะไรวุ่นวายแบบนี้ก็เพราะว่าจะได้กำหนด Typeface ใน Layout XML โดยใช้คำสั่ง android:typeface ได้นั่นเอง
ในการใช้งานจริงใน Mobile App เจ้าของบล็อกคิดว่าคงไม่จำเป็นซักเท่าไรนักที่จะต้องใช้ทุกแบบ จริงๆใช้แค่ Regular, Bold, Italic และ Italic ก็น่าจะเพียงพอแล้ว เว้นแต่ว่าอยากจะเลือก Font Style แบบอื่นมาใช้แทนแบบเดิม เช่น รู้สึกว่าอยากให้ Regular มันมีขนาดบางกว่านี้หน่อย ก็ลองเอา Font แบบ Light มากำหนดเป็น Regular แทนก็ได้นะ (เพราะในแอนดรอยด์มันอิงจากชื่อไฟล์ ไม่ได้สนหรอกว่ามันคือรูปแบบไหน)
แต่ถ้าอยากให้เลือก Typeface ได้แบบครอบจักรวาลก็คงต้องสร้าง Method แยกขึ้นมาเองในคลาส Custom View เอาแทนนะ แล้วเวลาเรียกก็ต้องเรียกผ่านคลาส Custom View แทน ซึ่งไม่ค่อยแนะนำซักเท่าไร
เพราะบนแอนดรอยด์มันไม่เหมือนบน Photoshop ที่จะมี Anti-aliasing Setting ให้ปรับรอยหยักของเส้นให้ดูนุ่มนวลและสวยมากขึ้น ดังนั้นถ้า Font ที่ไม่ได้ออกแบบมาสำหรับขนาดเล็กๆเวลาไปแสดงบนหน้าจอความละเอียดต่ำจึงทำให้รู้สึกว่ามันไม่สวยและอ่านลำบากมาก
คงไม่สนุกเท่าไรนักถ้านั่งเขียนแอพจนเสร็จเรียบร้อย แล้วมาเจอปัญหาตอนทดสอบบนอุปกรณ์แอนดรอยด์ที่หน้าจอความละเอียดต่ำ ดังนั้นถ้าเป็นไปได้ตอนที่ดีไซน์ออกมาแล้วและกำลังจะเริ่มเขียนโค๊ดก็ลองทดสอบ Font กับความละเอียดหน้าจอต่ำๆดูก่อนนะ เผื่อไม่โอเคจะได้ตัดสินใจเปลี่ยนทัน
ถ้าผู้ที่หลงเข้ามาอ่านเจอปัญหานี้มีอยู่ 2 ทาง คือ "ทำใจ" กับไป "แสดงผลบน WebView แทน" ซึ่งการแสดงผลบน WebView จะทำให้แอพมี Performance ต่ำ ต้องคำนึงถึงจุดนี้ด้วยนะ
แต่ปัญหานี้จะหมดไปครับถ้าแอพทำงานอยู่บนอุปกรณ์แอนดรอยด์ที่เป็น Android 5.0 (Lollipop) ขึ้นไป เพราะบนเวอร์ชันนี้มีการปรับปรุงเรื่องของ Typography มากสมควร และปัญหาตัดคำภาษาไทยก็ถูกแก้ไขให้ดีขึ้นเช่นกัน (เฮ้ เฮ)
package com.akexorcist.myapplication;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
public class CustomTextView extends TextView {
public CustomTextView(Context context) {
super(context);
initTypFace();
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initTypFace();
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initTypFace();
}
private void initTypFace() {
Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "your_custom_font.ttf");
setTypeface(typeface);
}
}
เวลาเอาไปใช้ก็ประกาศใน Layout XML แบบนี้ได้เลย
<com.akexorcist.myapplication.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:gravity="center"
android:textColor="#f3620a"
android:textSize="22sp" />
สะดวกสบายขึ้นเนอะ? เพราะว่าไม่ต้องมานั่งพิมพ์คำสั่งทุกอัน อันไหนอยากเปลี่ยนเป็น Font ที่ต้องการก็ประกาศไว้ใน Layout XML ได้เลย เวลาเรียกใช้งานก็เรียกมาเป็นคลาส TextView ได้เลย ไม่จำเป็นต้องเป็นคลาส CustomTextView และสามารถใช้กับ View ตัวอื่นๆได้ ไม่ว่าจะเป็น Button, RadioButton หรือ Toggle Button ก็ตาม
และอีกวิธีที่ดีกว่านี้ก็คือใช้ Library ที่ชื่อว่า Calligraphy ครับ ซึ่ง Library ตัวนี้จะใช้ Intentional บน Layout XML เพื่อให้สามารถกำหนด Custom Font ได้ง่ายขึ้น แต่มีข้อแม้ว่าต้องโยน Context เข้าไปให้ CalligraphyContextWrapper ด้วยให้กับ Activity ทุกๆตัว
public class MainActivity extends AppCompatActivity {
...
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
...
}
เวลาใช้งานก็แค่กำหนดใน Layout XML แบบนี้
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="MissingPrefix">
...
<TextView
fontPath="your_custom_font.ttf"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:textSize="20sp" />
...
</LinearLayout>
ชื่อ Font ที่กำหนดไว้ใน fontPath จะอ้างอิงจากในโฟลเดอร์ assets โดยอัตโนมัติ ถ้าอยู่ใน Sub Directory อีกที ก็สามารถใส่ Path ลงไปในนี้ได้เลย และมีเงื่อนไขว่าจะต้องประกาศ Ignore Missing Prefix ด้วย เพราะเดี๋ยว Android Studio จะ Build ไม่ผ่าน เนื่องจาก fontPath ไม่มี Prefix
และนอกจากนี้ Library ตัวนี้ยังสามารถกำหนด Default Custom Font ที่จะผลลัพธ์กับ View ทุกตัวที่ไม่ได้กำหนด fontPath ได้ด้วยนะเออ เหมาะมากกับแอพที่ต้องการเปลี่ยน Custon Font ด้วยคำสั่งเดียวแต่มีผลลัพธ์กับทั้งแอพ รวมไปถึงการทำ Custom Font ไว้ใน Style เพื่อเอาไปกำหนดใน Layout XML อีกที เพื่อให้สามารถใช้งาน Custon Font หลายๆตัวได้สะดวก (ไปอ่านใน Readme ของ Library ตัวนี้เองนะ)
การแสดงผลของ Custom Font ใน Layout Preview
เวลา Custom Font นั้น เจ้าของบล็อกแนะนำว่าให้ระวังเรื่องการจัดตัวหนังสือใน Layour XML ด้วย เพราะว่า Font แต่ละตัวนั้นมีความแตกต่างกันในหลายๆอย่างไม่ว่าจะเป็นขนาด, ช่องไฟ หรือ Line Spacing ดังนั้นทางที่ดีควรใช้ Layout Preview ใน Android Studio เวลาที่จัด Layoutถ้าผู้ที่หลงเข้ามาอ่านใช้ Library อย่าง Calligraphy จะมีปัญหาอย่างหนึ่งก็คือมันไม่สามารถแสดงใน Layout Preview ได้ เพราะว่ามันจะทำงานเมื่อติดตั้งลงบนเครื่องและเปิดขึ้นมาจริงๆ ซึ่งต่างจากการทำ Custom View เพราะว่าสามารถแสดงให้เห็นใน Layout Preview ได้ทันที (แต่ถ้ามีการแก้ไขโค๊ดใน Custom View Class ก็จะต้อง Build Gradle ใหม่ก่อนนะ ถึงจะเห็นผลลัพธ์)
ดังนั้นเจ้าของบล็อกจึงชอบใช้วิธีสร้าง Custom View ขึ้นมาแทน เพื่อที่จะได้จัดบน Layout Preview ได้สะดวก
แต่ก็ควรรู้ไว้ด้วยนะว่าการ Preview Custom Font เนี่ย จะมีเงื่อนไขว่าต้อง Preview บน API 21 ขึ้นไปเท่านั้น (Lollipop) ไม่สามารถ Preview บนเวอร์ชันที่ต่ำกว่านั้น และจะแจ้ง Rendering Problems แบบนี้
ในเมื่อ Preview บนเวอร์ชันที่ต่ำกว่านั้นไม่ได้ก็ช่างมัน แต่เพื่อหลีกเลี่ยงข้อความ Rendering Problem จึงนิยมใช้ If เพื่อเช็คว่ากำหนดอยู่ใน Edit Mode และเป็นเวอร์ชันมากกว่า Lollipop หรือป่าว
public class CustomTextView3 extends TextView {
public CustomTextView3(Context context) {
super(context);
initTypFace();
}
public CustomTextView3(Context context, AttributeSet attrs) {
super(context, attrs);
initTypFace();
}
public CustomTextView3(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initTypFace();
}
private void initTypFace() {
if (isInEditMode() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return;
}
Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "your_custom_font.ttf");
setTypeface(typeface);
}
}
ถ้าถามว่ามันทำให้ Preview บนเวอร์ชันต่ำกว่าได้มั้ย? ก็ไม่หรอก แค่มันเลี่ยงไม่ให้แสดงข้อความ Rendering Problem เฉยๆนั่นแหละ ดังนั้นถ้าอยาก Preview เพื่อดูการแสดงผลของ Custom Font ก็ให้กำหนด Preview เป็นเวอร์ชัน Lollipop ขึ้นไปนะ
เพราะ Font ทุกตัวนั้นไม่ได้เหมือนกัน
ดังนั้นจึงไม่ต้องแปลกใจถ้า Custom Font แสดงผลแตกต่างไปจาก System Font เช่น ขนาด หรือ Line Spacingจากภาพข้างบนจะเห็นว่า Font คนละแบบที่มีขนาด 18sp เท่ากัน กลับมีขนาดที่แสดงบนหน้าจอจริงๆไม่เท่ากัน รวมไปถึง Line Spacing ก็ไม่เท่ากันด้วย
ดังนั้นจึงเป็นสาเหตุที่ต้องใช้ Layout Preview เข้ามาช่วยในขณะที่จัด Layout เพราะไม่เช่นนั้นอาจจะทำให้เพี้ยนได้ และ Font ส่วนใหญ่นั้นจะมี Line Spacing (ระยะห่างระหว่างแต่ละบรรทัด) เยอะกว่า System Font ไม่สิ ต้องบอกว่า System Font มี Line Spacing น้อยกว่าปกติสิเนอะ
สำหรับขนาดของตัวหนังสือก็คงแก้ไขไม่ยาก ถ้าใหญ่เกินปกติก็ปรับให้มีขนาดเล็กลง แต่ถ้ามีขนาดเล็กกว่าปกติก็ปรับให้ใหญ่ขึ้นเท่านั้นเอง และสำหรับ Line Spacing ก็สามารถแก้ไขได้ด้วยการใส่คำสั่งในโค๊ด
<com.akexorcist.myapplication.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/description1"
android:lineSpacingExtra="-6dp"
android:textColor="#f3620a"
android:textSize="20sp" />
เอ....ถ้าใส่ใน Layout XML นั่นก็หมายความว่าต้องใส่เกือบทุกอันสิ... ถ้าแบบนั้นย้ายไปใส่ไว้ในคลาส CustomTextView ไปเลยดีกว่ามั้ง
public class CustomTextView extends TextView {
...
private void initTypFace(AttributeSet attrs) {
...
setLineSpacing(getPixelFromDp(getContext(), -8 ), 1);
}
public static float getPixelFromDp(Context context, float dp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return dp * (metrics.densityDpi / 160f);
}
}
อยากจะปรับ Line Spacing มากน้อยแค่ไหนก็ตามใจชอบเลยนะ
ระวังเรื่อง Margin หรือ Padding ของ View ที่อาจจะไปทำให้ตัวหนังสือหายไปบางส่วนด้วย
ในกรณีที่เป็นเรื่องของ Line Spacing เจ้าของบล็อกก็จะเอาไฟล์ Font มาแก้ไขด้วยโปรเจคโดยตรงไปเลย เพราะขี้เกียจมานั่งใส่คำสั่งในโค๊ด ฮาๆ
แต่บางครั้งก็อาจจะเจอปัญหา Padding ของฟอนต์นั้นๆแสดงผลไม่ค่อยดีซักเท่าไรในแอพแอนดรอยด์ และบางครั้งมันแสดงบน Phone ได้นะ แต่พอลองแสดงบน Tablet กลับเพี้ยนซะงั้น
เฮ้อ จะบ้าตาย
แต่ปัญหานี้ก็สามารถแก้ไขได้ด้วยการไม่ใช้ Padding ที่มากับ Custom Font ตัวนั้นๆ ซึ่งสามารถกำหนดได้ทั้งแบบ XML และ Programmatically ดังนี้
android:includeFontPadding="false"
หรือ
setIncludeFontPadding(false);
เพียงเท่านี้ก็จะช่วยให้ Padding ของ Custom Font ที่ใช้กลับมาดูดีขึ้นหน่อย แต่ก็อาจจะต้องปรับค่า Padding อีกเล็กน้อยเพื่อให้เข้าที่เข้าทาง
การทำ Custom Font ให้รองรับ Typeface หลายแบบ
บนแอนดรอยด์นั้นรองรับ Font อยู่ 4 รูปแบบคือ Normal หรือ Regular (ปกติ), Bold (ตัวหนา), Italic (ตัวเอียง) และตัวหนาเอียง (Bold Italic) เพื่อให้สามารถกำหนดรูปแบบของ Font ให้มีความแตกต่างกันได้ ซึ่งรูปแบบเหล่านี้บนแอนดรอยด์จะถูกเรียกว่า Typefaceถ้าบน Photoshop จะเรียกว่า Font Style และในงานสาย Typography จริงๆเค้าจะเรียกว่า Font Family นะจ๊ะ เพราะงั้นเวลาไปคุยกับ Graphic Designer ก็ให้ใช้คำว่า Font Style หรือ Font Family ดีกว่า
แต่เมื่อผู้ที่หลงเข้ามาอ่านใช้ Custom Font นั่นก็หมายความว่าจะต้องเตรียมไฟล์ Font โดยแบ่งตาม Typeface ทั้งหมด 4 ไฟล์
ถ้า Font ที่เอามาใช้ดันมีแค่ Regular อย่างเดียวก็เสียใจด้วยนะจ๊ะ
ซึ่งการเอามาใช้งานถ้าใช้ Library อย่าง Calligraphy เข้ามาช่วยก็สะดวกเลย เพราะทำเป็น Style แยกตาม Typeface แล้วเรียกใช้ตามต้องการได้ แต่ในกรณีที่ทำเป็น Custom View ก็จะต้องมีการแก้ไขโค๊ดเล็กน้อยเพื่อให้รองรับ Typeface หลายๆแบบ
public class CustomTextView extends TextView {
public CustomTextView(Context context) {
super(context);
initTypFace(null);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initTypFace(attrs);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initTypFace(attrs);
}
private void initTypFace(AttributeSet attrs) {
if (isInEditMode() && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return;
}
int attrTextStyle = Typeface.NORMAL;
if (attrs != null) {
int[] attributes = new int[]{android.R.attr.textStyle};
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, attributes);
attrTextStyle = typedArray.getInt(0, 0);
typedArray.recycle();
}
String fontPath = "THSarabun-Regular.ttf";
Typeface typeface = getTypeface();
if (typeface != null) {
if ((typeface.getStyle() & Typeface.BOLD_ITALIC) == Typeface.BOLD_ITALIC ||
(attrTextStyle & Typeface.BOLD_ITALIC) == Typeface.BOLD_ITALIC) {
fontPath = "THSarabun-BoldItalic.ttf";
} else if ((typeface.getStyle() & Typeface.ITALIC) == Typeface.ITALIC ||
(attrTextStyle & Typeface.ITALIC) == Typeface.ITALIC) {
fontPath = "THSarabun-Italic.ttf";
} else if ((typeface.getStyle() & Typeface.BOLD) == Typeface.BOLD ||
(attrTextStyle & Typeface.BOLD) == Typeface.BOLD) {
fontPath = "THSarabun-Bold.ttf";
}
}
setTypeface(Typeface.createFromAsset(getContext().getAssets(), fontPath));
}
}
ไม่ต้องตกใจกับโค๊ดที่รกยุบยับนะครับ เพราะในการรองรับ Typeface หลายๆแบบมันจะต้องเช็คว่า ณ ตอนนั้น Typeface ที่กำหนดไว้เป็นแบบไหน แล้วจึงเลือกไฟล์ Font ให้ตรงกับแต่ละ Typeface นั่นเอง
ถ้าอยากเปลี่ยน Typeface ผ่านโค๊ดก็ใช้คำสั่งแบบนี้
TextView textView = (TextView) findViewById(R.id.text_view);
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
ที่ต้องทำอะไรวุ่นวายแบบนี้ก็เพราะว่าจะได้กำหนด Typeface ใน Layout XML โดยใช้คำสั่ง android:typeface ได้นั่นเอง
<com.akexorcist.myapplication.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sleeping For Less"
android:textStyle="bold"
android:textSize="20sp" />
ในความเป็นจริง Typeface หรือ Font Family นั้นมีมากกว่า 4 แบบ
ถ้าอิงจาก Font ของ Roboto ก็จะพบว่ามันมีรูปแบบมากถึง 12 แบบเลยนะในการใช้งานจริงใน Mobile App เจ้าของบล็อกคิดว่าคงไม่จำเป็นซักเท่าไรนักที่จะต้องใช้ทุกแบบ จริงๆใช้แค่ Regular, Bold, Italic และ Italic ก็น่าจะเพียงพอแล้ว เว้นแต่ว่าอยากจะเลือก Font Style แบบอื่นมาใช้แทนแบบเดิม เช่น รู้สึกว่าอยากให้ Regular มันมีขนาดบางกว่านี้หน่อย ก็ลองเอา Font แบบ Light มากำหนดเป็น Regular แทนก็ได้นะ (เพราะในแอนดรอยด์มันอิงจากชื่อไฟล์ ไม่ได้สนหรอกว่ามันคือรูปแบบไหน)
แต่ถ้าอยากให้เลือก Typeface ได้แบบครอบจักรวาลก็คงต้องสร้าง Method แยกขึ้นมาเองในคลาส Custom View เอาแทนนะ แล้วเวลาเรียกก็ต้องเรียกผ่านคลาส Custom View แทน ซึ่งไม่ค่อยแนะนำซักเท่าไร
จงเลี่ยง Font ที่ไม่เหมาะกับหน้าจอความละเอียดต่ำ
ถือว่าเป็นจุดที่พลาดกันได้ง่ายๆพอสมควร เมื่อพบว่า Font ที่เลือกมานั้นก็แสดงผลได้ปกติ แต่พอมาลองบนอุปกรณ์แอนดรอยด์ที่มีหน้าจอความละเอียดต่ำแล้วพบว่าตัวหนังสือมันบางเกินเพราะบนแอนดรอยด์มันไม่เหมือนบน Photoshop ที่จะมี Anti-aliasing Setting ให้ปรับรอยหยักของเส้นให้ดูนุ่มนวลและสวยมากขึ้น ดังนั้นถ้า Font ที่ไม่ได้ออกแบบมาสำหรับขนาดเล็กๆเวลาไปแสดงบนหน้าจอความละเอียดต่ำจึงทำให้รู้สึกว่ามันไม่สวยและอ่านลำบากมาก
คงไม่สนุกเท่าไรนักถ้านั่งเขียนแอพจนเสร็จเรียบร้อย แล้วมาเจอปัญหาตอนทดสอบบนอุปกรณ์แอนดรอยด์ที่หน้าจอความละเอียดต่ำ ดังนั้นถ้าเป็นไปได้ตอนที่ดีไซน์ออกมาแล้วและกำลังจะเริ่มเขียนโค๊ดก็ลองทดสอบ Font กับความละเอียดหน้าจอต่ำๆดูก่อนนะ เผื่อไม่โอเคจะได้ตัดสินใจเปลี่ยนทัน
ถ้าไม่ใช่ Lollipop ขึ้นไป ยังไงภาษาไทยก็ตัดคำห่วย
เข้าใจความรู้สึกนี้ใช่มั้ยล่ะ!! เพราะนักพัฒนาแอนดรอยด์ชาวไทยส่วนใหญ่ต้องเจอปัญหานี้อยู่แล้ว ปัญหาการตัดคำห่วยๆในภาษาไทย นี่มันปัญหาระดับชาติจริงๆ ถึงแม้ว่าจะหา Library อย่าง Thai Line Breaker มาใช้ก็ตาม ก็จะพบว่ามันตัดคำไม่สวยอยู่ดีถ้าผู้ที่หลงเข้ามาอ่านเจอปัญหานี้มีอยู่ 2 ทาง คือ "ทำใจ" กับไป "แสดงผลบน WebView แทน" ซึ่งการแสดงผลบน WebView จะทำให้แอพมี Performance ต่ำ ต้องคำนึงถึงจุดนี้ด้วยนะ
แต่ปัญหานี้จะหมดไปครับถ้าแอพทำงานอยู่บนอุปกรณ์แอนดรอยด์ที่เป็น Android 5.0 (Lollipop) ขึ้นไป เพราะบนเวอร์ชันนี้มีการปรับปรุงเรื่องของ Typography มากสมควร และปัญหาตัดคำภาษาไทยก็ถูกแก้ไขให้ดีขึ้นเช่นกัน (เฮ้ เฮ)
เพิ่มเติมเล็กน้อยเกี่ยวกับการเปลี่ยนแปลงทาง Typography หลังจาก Android 5.0
เนื้อหาส่วนนี้อ้างอิงจาก Session ที่ชื่อว่า Android Textual Layout จากงาน Android Dev Summit 2015 นะครับ ซึ่งเจ้าของบล็อกได้ไปฟังมาแล้วพบว่าน่าสนใจดีและเหมาะกับบทความนี้พอดี
บน Android 5.0 Lollipop ขึ้นไปได้ใช้ Roboto เวอร์ชัน 2 ซึ่งรองรับการแสดงผลที่ดีมากขึ้นและรองรับได้หลายภาษา และที่สำคัญคือ Roboto เป็น Font ที่ Open source ด้วยล่ะ และปัจจุบันมี Font ที่ชื่อว่า Noto ที่ทาง Google พัฒนาขึ้นมาเพื่อให้เป็น Font ที่รองรับได้ทุกภาษาทั่วโลก แต่ไม่ได้มาพร้อมกับตัวแอนดรอยด์หรอกนะ ต้องไปดาวน์โหลดมาใส่ในแอพเอง ซึ่งมีข้อดีคือสามารถ Localize ได้โดยไม่ต้องห่วงว่า Font ที่ใช้จะแสดงภาษานั้นๆได้หรือไม่ แถมรองรับ Emoji ด้วยนะ
และ Android 5.0 Lollipop ยังรองรับ OpenType สามารถปรับรูปแบบของ Font ผ่านโค๊ดได้เพิ่มมากขึ้น จากเดิมที่ทำได้แค่ไม่กี่อย่าง อย่างเช่นเรื่องของ Kerning, Ligatures, Number Forms และ Letter Spacing
Kerning คือระยะระหว่างตัวอักษรเฉพาะตัว เพราะว่าตัวอักษรบางคู่ก็สามารถเหลื่อมกันได้เพื่อให้สวยงาม อย่างเช่น AV (แค่สมมตินะ)
Ligatures คือการผสมระหว่างตัวอักษรสองตัว ซึ่งบางครั้งทำให้ดูไม่สวยและอ่านยาก
Number Forms คือลักษณะของตัวเลข
Letter Spacing คือระยะห่างระหว่างตัวอักษร
สำหรับ Kerning, Ligatures และ Number Forms สามารถกำหนดใน Layout XML ผ่าน android:fontFeatureSettings ได้เลย
อยากจะกำหนดอย่างใดอย่างหนึ่งก็ได้เหมือนกันนะ ไม่จำเป็นต้องกำหนดพร้อมกันทั้งหมด
ส่วน Letter Spacing สามารถกำหนดผ่าน android:letterSpacing
นอกจากนี้การมาของ Android 6.0 Marshmallow ยังมีการปรับปรุงเรื่องการตัดคำให้ดียิ่งขึ้นอีก สามารถกำหนดรูปแบบในการตัดคำได้
โดย Default จะกำหนดไว้เป็น Simple และที่เจ้าของบล็อกชอบก็คือ มันรองรับภาษาไทยด้วยนะเออ!!
ขอบคุณข้อความตัวอย่างจาก NuuNeoI (ไม่รู้จะเอาข้อความอะไรดี เห็นพี่แกโพสขึ้น Facebook ก็เลยเอามาเป็นตัวอย่างซะเลย)
และยังปรับให้ตัดคำแบบ High Quality อีกด้วย โดยปกติแล้วคำที่เกินจะถูกตัดขึ้นบรรทัดใหม่ แต่สำหรับการตัดคำแบบ High Quality ถ้ามีคำไหนยาวๆก็จะใช้เครื่องหมายขีดในการเชื่อมคำระหว่างบรรทัดให้
แต่เจ้าของบล็อกไม่แน่ใจนะว่าใช้กับภาษาไทยได้มั้ย แต่ที่แน่ๆคือการตัดคำแบบนี้จะใช้เวลา Process นานกว่าแบบธรรมดา
สามารถไปอ่านเรื่องราวของ Font Resource กันต่อได้ที่ [Android Code] ลองเล่น Font Resource ของเล่นใหม่จาก Android O
บน Android 5.0 Lollipop ขึ้นไปได้ใช้ Roboto เวอร์ชัน 2 ซึ่งรองรับการแสดงผลที่ดีมากขึ้นและรองรับได้หลายภาษา และที่สำคัญคือ Roboto เป็น Font ที่ Open source ด้วยล่ะ และปัจจุบันมี Font ที่ชื่อว่า Noto ที่ทาง Google พัฒนาขึ้นมาเพื่อให้เป็น Font ที่รองรับได้ทุกภาษาทั่วโลก แต่ไม่ได้มาพร้อมกับตัวแอนดรอยด์หรอกนะ ต้องไปดาวน์โหลดมาใส่ในแอพเอง ซึ่งมีข้อดีคือสามารถ Localize ได้โดยไม่ต้องห่วงว่า Font ที่ใช้จะแสดงภาษานั้นๆได้หรือไม่ แถมรองรับ Emoji ด้วยนะ
และ Android 5.0 Lollipop ยังรองรับ OpenType สามารถปรับรูปแบบของ Font ผ่านโค๊ดได้เพิ่มมากขึ้น จากเดิมที่ทำได้แค่ไม่กี่อย่าง อย่างเช่นเรื่องของ Kerning, Ligatures, Number Forms และ Letter Spacing
Kerning คือระยะระหว่างตัวอักษรเฉพาะตัว เพราะว่าตัวอักษรบางคู่ก็สามารถเหลื่อมกันได้เพื่อให้สวยงาม อย่างเช่น AV (แค่สมมตินะ)
Ligatures คือการผสมระหว่างตัวอักษรสองตัว ซึ่งบางครั้งทำให้ดูไม่สวยและอ่านยาก
Number Forms คือลักษณะของตัวเลข
Letter Spacing คือระยะห่างระหว่างตัวอักษร
สำหรับ Kerning, Ligatures และ Number Forms สามารถกำหนดใน Layout XML ผ่าน android:fontFeatureSettings ได้เลย
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/description1"
android:fontFeatureSettings="'kern' on, 'liga' off, 'onum'"
android:textSize="20sp" />
อยากจะกำหนดอย่างใดอย่างหนึ่งก็ได้เหมือนกันนะ ไม่จำเป็นต้องกำหนดพร้อมกันทั้งหมด
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/description1"
android:fontFeatureSettings="'kern' off"
android:textSize="20sp" />
ส่วน Letter Spacing สามารถกำหนดผ่าน android:letterSpacing
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/description1"
android:letterSpacing="0.1"
android:textSize="20sp" />
นอกจากนี้การมาของ Android 6.0 Marshmallow ยังมีการปรับปรุงเรื่องการตัดคำให้ดียิ่งขึ้นอีก สามารถกำหนดรูปแบบในการตัดคำได้
โดย Default จะกำหนดไว้เป็น Simple และที่เจ้าของบล็อกชอบก็คือ มันรองรับภาษาไทยด้วยนะเออ!!
ขอบคุณข้อความตัวอย่างจาก NuuNeoI (ไม่รู้จะเอาข้อความอะไรดี เห็นพี่แกโพสขึ้น Facebook ก็เลยเอามาเป็นตัวอย่างซะเลย)
และยังปรับให้ตัดคำแบบ High Quality อีกด้วย โดยปกติแล้วคำที่เกินจะถูกตัดขึ้นบรรทัดใหม่ แต่สำหรับการตัดคำแบบ High Quality ถ้ามีคำไหนยาวๆก็จะใช้เครื่องหมายขีดในการเชื่อมคำระหว่างบรรทัดให้
แต่เจ้าของบล็อกไม่แน่ใจนะว่าใช้กับภาษาไทยได้มั้ย แต่ที่แน่ๆคือการตัดคำแบบนี้จะใช้เวลา Process นานกว่าแบบธรรมดา
Font Resource ของเล่นใหม่ที่ไม่ต้อง Custom Font อีกต่อไป
หลังจากที่ลำบากลำบนเกี่ยวกับฟอนต์มานาน ในที่สุดแอนดรอยด์ก็ได้เพิ่ม Font Resource เข้ามาเพื่อให้ใช้ไฟล์ฟอนต์เป็น Resource ได้เลย แต่ทว่าก็ยังคงเจอปัญหาเกี่ยวกับพวก Padding เหมือนเดิม เพราะงั้นก็ต้องจัดการเองอีกทีอยู่ดีสามารถไปอ่านเรื่องราวของ Font Resource กันต่อได้ที่ [Android Code] ลองเล่น Font Resource ของเล่นใหม่จาก Android O