#AndroidStudio ตอนที่ 8.1 สิ่งที่ทำได้ และไม่ได้ในการใช้ HTML ใน TextView

#AndroidStudio ตอนที่ 8.1 สิ่งที่ทำได้ และไม่ได้ในการใช้ HTML ใน TextView  

<introduction>
หลังทบทวนที่ได้ใช้โปรแกรม Android Studio แล้วพบ ปัญหา และทางออกในหลายวิธี แล้วแบ่งปันในกลุ่มเฟส สมาคมโปรแกรมเมอร์ไทย ก็มีเพื่อนมาร่วมแลกเปลี่ยน แชร์โฮมเพจดี ๆ มาให้อ่าน หลังเขียนเรื่องที่อยากเขียนแล้ว ก็นำเรื่องที่อ่านเพิ่มเติมมาเขียนในข้อที่ 8 มีอะไรอีกมากที่ต้องเรียนรู้ ทั้ง 1) properties อีกอื้อ 2) ต้องเปลี่ยนตนเองตามการเปลี่ยนรุ่นของเครื่องมือ 3) การเลือกใช้เครื่องมือที่เหมาะสมกับงาน

Google เจ้าของ Android Studio ได้เตรียมเครื่องมือไว้เยอะ บางเรื่องบางงานก็ใช้ได้หลายเครื่องมือ ยกตัวอย่างง่าย ๆ สำหรับงานง่าย ๆ ก็มี layout ให้เลือกตั้งเยอะ และตอบโจทย์สำหรับงานง่ายได้ทุก layout แต่ถ้าต้องออกแบบ layout เชิงลึกถึงจะเห็นว่างานแบบลึกนั้น จะต้องใช้ layout แบบใดที่มีคุณสมบัติเฉพาะ และใช้ layout แบบอื่นไม่ได้จริง ๆ

TextView ไม่รองรับ HTML แต่โดยดี จึงมีเรื่องมาเล่า ดังนี้ 1) ใส่ HTML Tag เข้า TextView ใน Layout โดยตรง 2) ใส่ HTML Tag ใน string ที่อยู่ใน strings.xml แล้วอ้างอิงจาก Textview ใน layout 3) การส่ง img ใน strings.xml ไป TextView ใน layout ที่แสดงผลเฉพาะตัวอักษรหนา ส่วนภาพไม่ออก 4) การส่ง img ใน MainActivity.java ไป TextView ผ่าน ImageGetter แบบใช้ได้ 5) การส่ง img ใน strings.xml เข้า MainActivity.java ไป TextView ผ่าน ImageGetter แบบใช้ได้ 6) การส่ง img ใน strings.xml เข้า MainActivity.java ไป TextView ผ่าน ImageGetter แบบสั่ง replace แทนกำหนด Html Tag 7) ปรับ ImageGetter กันดีกว่า 8) การใช้ drawableStart แทน drawableLeft

</introduction>

กำลังเล่าเรื่อง การใช้โปรแกรม Android Studio สู่เพื่อนนักพัฒนา ผ่าน Blog

<process>

1. ใส่ HTML Tag เข้า TextView ใน Layout โดยตรง

ต.ย.1 การใช้ HTML Tag ใน TextView แบบนี้ไม่ได้ เกิด Error : Build ไม่ผ่าน

android:text="<b>hello</b>"

ต.ย. 2 การใช้ Entity Reference ใน TextView แบบนี้ไม่ได้ เกิด Error : Build ไม่ผ่าน

android:text="&lt;b>hello&lt;/b>"

ต.ย. 3 การอ้างอิงข้อมูลมาใช้ ต้องเคยถูกประกาศไว้ใน strings.xml จึงจะถูกต้อง แบบนี้ดี

android:text="@string/data1"

2. ใส่ HTML Tag ใน string ที่อยู่ใน strings.xml แล้วอ้างอิงจาก Textview ใน layout 

การใช้ HTML เขียนใน strings.xml แล้วอ้างไปแสดงผลใน layout.xml ทันที แบบนี้ เป็นวิธีที่ถูกต้อง มีคำแนะนำไว้ใน guide ของ  android.com ว่าสามารถใช้ <b> <i> หรือ <u> ได้ เพื่อประกอบการแต่ข้อความที่แสดงใน TextView

ปัญหา คือ การใส่ img หรือภาพไปพร้อมข้อความใน TextView ต้องเพิ่ม code พิเศษ

<string name="data"><b>hello world</b></string>
android:text="@string/data"

3. การส่ง img ใน strings.xml ไป TextView ใน layout ที่แสดงผลเฉพาะตัวอักษรหนา ส่วนภาพไม่ออก

แบบนี้ไม่พบ error แต่ภาพไม่แสดง ต้องมี code มาช่วย จึงจะแสดงภาพใน TextView

<string name="data"><b>hello world</b> <img src="lp01" /></string>

4. การส่ง img ใน MainActivity.java ไป TextView ผ่าน ImageGetter แบบใช้ได้

บรรทัดที่ 46 มี HTML Tag ที่ทำให้ ตัวอักษรหนา และเรียกภาพมาแสดงได้จริง
ประกาศข้อมูลแบบ String ที่อยู่ภายใน MainActivity.java เอง
แบบที่่ 1 ที่ใช้งานได้ build ผ่าน และแสดงผลถูกต้อง

String mData = "<b>hello</b> <img src=\"lp01\" />";

แบบที่่ 2 ที่ใช้งานได้ เหมือนแบบแรก

String mData = "<b>hello</b> <img src=lp01 />";

แบบที่่ 3 ที่ใช้งานได้ เหมือนแบบแรก

String mData = "<b>hello</b> <img src='lp01' />";

แบบนี้ error ต้องแก้ไขทันที

String mData = "<b>hello</b> <img src="lp01" />";

บรรทัดที่ใช้ได้ทั้ง 3 ตัวอย่างข้างต้น
จะถูกส่งต่อไปให้ class ImageGetter ที่ประกาศใต้ class MainActivity
ก่อนจะส่งภาพให้ TextView ที่อยู่ใน Layout จะใช้ setText( .. )
ก็ต้องแปลงข้อมูลกันก่อน ด้วย Html.fromHtml( .. ) และใช้ ImageGetter ที่นี่
แต่ fromHtml มีหลายรุ่น จึงใช้ IF ตรวจสอบว่ารุ่นไหน ต้องเรียกใช้แบบใด

htmlTextView.setText(Html.fromHtml(mData, new ImageGetter(),null)); 

ตัวอย่าง Code ทั้งหมด 55 บรรทัดที่ใช้ใน MainActivity.java อยู่ข้างล่างนี้

https://gist.github.com/thaiall/bcd1c4a61fd16bf1cfb5dd32fc274d93

5. การส่ง img ใน strings.xml เข้า MainActivity.java ไป TextView ผ่าน ImageGetter แบบใช้ได้

เปลี่ยนจากการกำหนดค่าคงที่ (Constant) เป็นการอ้างอิงค่า (Reference) จาก R.string.data หากไม่แก้ไข MainActivity.java อีก มาดูกันว่าต้องกำหนดข้อมูลใน strings.xml อย่างไร เพื่อทำให้แสดงภาพใน TextView

String mData = res.getString(R.string.data);

ใช้ Entity Reference คือ &lt; ทำให้ภาพออก เห็นตัวอักษา แต่ตัวอักษาไม่หนา

<string name="data"><b>hello</b> &lt;img src="lp01" /></string>

ใช้ Entity Reference คือ &lt; แทน < ทำให้เห็นทั้งภาพ และตัวอักษรหนา

<string name="data">&lt;b>hello&lt;/b> &lt;img src="lp01" /></string>

ใช้ Entity Reference คือ &lt; แทน < แบบไม่มี Quote  หรือ “” ก็ได้

<string name="data">&lt;b>hello&lt;/b> &lt;img src=lp01 /></string>

 

6. การส่ง img ใน strings.xml เข้า MainActivity.java ไป TextView ผ่าน ImageGetter แบบสั่ง replace แทนกำหนด Html Tag

หากไม่ต้องการใช้ HTML Tag ระบุชื่อภาพ แต่ใช้การแทนที่ ก็สามารถเพิ่ม code ใน MainActivity.java ได้ โดยมีตัวอย่างข้อมูลใน strings.xml ดังนี้

 <string name="data">pic01 pic02 pic03</string>

เพิ่มคำสั่ง for สั่ง replace ข้อมูลใน mData หากพบ pic0 ก็ให้เปลี่ยนเป็น Img Tag ตามรูปแบบที่ต้องการ ถือเป็นการลดรูปของข้อมูลภาพใน strings.xml ไม่ต้องพิมพ์ยาว ๆ

String mData = res.getString(R.string.data);
for(int i=1;i<=3;i++) {
 mData = mData.replace("pic0" + i, "<img src=lp0" + i + " />");
}

หากกำหนดข้อมูลใน strings.xml ให้ตัวอักษรเข้ม และปัดบรรทัด
ยังต้องใช้ Entity Reference คือ &lt; แทนเครื่องหมาย < ดังอธิบายในข้อ 5

<string name="data"><b>hello</b><br/>pic01</string>

แม้จะใช้คำสั่ง replace แทนที่ < เป็น &lt; ก็ไม่ทำให้ผลลัพธ์เปลี่ยนแปลง
เหมือนกับไม่เกิดการแทนที่เกิดขึ้น ก็เพียงแต่เล่าว่า วิธีนี้ใช้มาแล้ว

mData = mData.replace("<", "&lt;");
mData = mData.replace("<b>", "&lt;b>");

แล้วถ้าข้อมูลพิมพ์ [ แทนเครื่องหมาย < แล้วสั่ง replace
ผลลัพธ์จะออกมาเป็นการแสดง source code คือ <b>hello</b><br/>และภาพ

<string name="data">[b>hello[/b>[br/>pic01</string>
mData = mData.replace("[", "&lt;");

7. ปรับ ImageGetter กันดีกว่า

7.1 บรรทัดที่ 34 คือ setBounds (.. )
เนื่องจากผมเขียนโปรแกรมทดสอบเกี่ยวกับภาพ เห็นว่าภาพใหญ่เกินไป
จึงปรับให้ขนาดเล็กจิ๋ว กว้าง 48 * 2 =96 pixel สูง 64 *2 =128 pixels
ถ้าต้องการใช้ความกว้างสูงของตัวภาพ แล้วทำให้ใหญ่ 2 เท่า หรือเล็กครึ่งหนึ่ง ก็ทำได้

d.setBounds(0,0,d.getIntrinsicWidth(), d.getIntrinsicHeight());

7.2 หากจะปรับขนาดภาพให้เป็นตามชื่อภาพ
เราก็มีชื่อภาพในตัวแปร source แก้โปรแกรม โดยเปลี่ยนบรรทัด 34 เป็น 4 บรรทัดข้างนี้
แล้วปรับขนาดได้ตามชอบใจ

if(source.equals("lp01"))
    d.setBounds(0,0,48 * 2, 64 * 2);
else
    d.setBounds(0,0,300, 400);

7.3 ปรับการตรวจสอบ id
ตรวจสอบค่าของ getPackageName(); พบมีค่า com.thaiall.www.myapplication
ทำให้โปรแกรมที่เขียนขึ้นสามารถบรรทัดที่ 18 – 21 เพราะมีบรรทัดที่ 17 อยู่แล้ว

String p = getPackageName();

8. การใช้ drawableStart แทน drawableLeft
ได้แชร์ blog ตอนที่ 8 ในเฟสกลุ่ม สมาคมโปรแกรมเมอร์ไทย
แล้วคุณ Somkiat Ake Khitwongwattana แนะนำให้อ่าน Advance Android TextView ที่เขียนโดย Chiu-Ki Chan พอเข้าไปก็เห็นว่ามีคุณสมบัติที่ชื่อ drawableTop, drawableBottom และอีกเยอะที่ทดสอบแล้วใช้ได้เลยใน TextView แต่ drawableLeft ที่น่าจะใช้ได้พบว่า Android Studio ที่ผมใช้เค้าให้เปลี่ยนเป็น drawableStart และ drawableEnd ซึ่งเป็นการเปลี่ยนแปลงตลอดระหว่างการเปลี่ยนรุ่นของเครื่องมือที่ใช้พัฒนา นี่เป็นตัวอย่าง code ที่ปรับมาจากคำแนะนำที่ไปอ่านมา และผมไม่ใช้ lp01 เพราะรูปใหญ่เกิน จึงเลี่ยงไปใช้ ic_insert_emoticon_black_24dp และใน Page ของ Chiu-Ki Chan ยังมี Guide ที่น่าสนใจอีกมาก

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/app_name"
    android:drawableStart="@drawable/ic_insert_emoticon_black_24dp"
    android:drawableEnd="@drawable/ic_insert_emoticon_black_24dp"
    android:drawableTop="@drawable/ic_insert_emoticon_black_24dp"
    android:drawableBottom="@drawable/ic_insert_emoticon_black_24dp" />

</process>

สรุปว่าวิธีที่ใส่ภาพ และ html อยู่ข้อ 5 
ส่วนข้อ 6 เป็นการเล่าเพิ่มเติม เรื่อง code

<website_guide>
http://chiuki.github.io/advanced-android-textview/#/
</website_guide>

หมายเหตุ 
ถ้าสนใจติดตามเนื้อหาในบล็อกนี้ สามารถ subscribe ด้วย email ที่อยู่ข้างขวา หรือ click here

#Android Studio ตอนที่ 8 การใส่ภาพและคำสั่ง HTML ใน TextView ต่างกันทั้ง Java, Layout และ String

#Android Studio ตอนที่ 8 การใส่ภาพและคำสั่ง HTML ใน TextView
ต่างกันทั้ง Java, Layout และ String

<introduction>
อารัมภบท
การทำให้ตัวอักษรหนา หรือ Bold ใน TextView มีเงื่อนไขต่างกันทั้งใน Java, Layout และ String ในตอนนี้จะเน้นว่าทำอย่างไรให้แสดงผลได้ ไม่อธิบายส่วนของปัญหา เพราะการใส่ Html Tag ใน TextView ไม่เหมือนการเขียนเว็บเพจด้วยภาษา HTML พบว่า มีข้อจำกัดมาก ถามกันเยอะใน stackoverflow.com ไปอ่านใน guide ของ android.com พบว่า Supported HTML elements include:

  • <b> for bold text.
  • <i> for italic text.
  • <u> for underline text.

อันที่จริงยังมี Tag ที่ได้รับการสนับสนุนมากกว่านี้ ถ้าอ่านเพิ่มเติมที่ abhiandroid.com และ grokkingandroid.com หากจะให้สรุปก็คงเห็นพ้องกับ Developer หลายท่านว่าการใช้ Html บน Android Studio มี 2 คลาสที่รองรับคือ TextView กับ WebView ถ้าให้ดีควรเลือกใช้ WebView เพราะ TextView สนับสนุนการใช้ HTML น้อยกว่า โดยเฉพาะการใส่ Image ที่ต้องสร้าง Class ImageGetter มาช่วย

แต่ TextView เป็น Class ที่นิยมใช้งานกัน การพยายามใส่ Image และ HTML ลงไปก็น่าจะมาถูกทาง เพียงแต่ Android Studio มี Class อื่นที่สนับสนุนงานด้าน HTML มากกว่า หากใช้งาน HTML จริงจัง คงต้องไปใช้ WebView แต่ถ้างานเล็ก ๆ จะใช้ TextView ก็น่าจะพอรับมือไหว ตัวอย่างในตอนนี้จึงมุ่งมั่นจะใช้ TextView เพื่อแสดง image และ HTML Tag แล้วลองติดตามดูครับ งานนี้ Activity Template เริ่มต้นผมเลือกใช้ คือ Basic Activity

สรุปกระบวนการได้ .. ขั้นตอน ประกอบด้วย 1) ทำความเข้าใจ Project ที่นำมาพัฒนาต่อ 2) ทดลองใส่ Tag:img ใน strings.xml 3) สร้างคลาส ImageGetter เพื่อให้แสดงภาพ ตาม Tag:img 4) สร้าง layout ใหม่ และ string ที่มี HTML tag หลายตัว 5) ทำให้ปุ่มสั่งเปิด Layout ใหม่ได้ และ Tag:img ทำงานได้ 
</introduction>

กำลังเล่าเรื่อง การใช้โปรแกรม Android Studio สู่เพื่อนนักพัฒนา ผ่าน Blog

<process>
1. ทำความเข้าใจ Project ที่นำมาพัฒนาต่อ
สร้าง New Project แบบ Basic Activity ชื่อ imageintextview
จะมีปุ่มที่เป็น FloatingActionButton ใน activity_main.xml
ตามที่เล่าใน ตอนที่ 7 : ข้อ 6 กับข้อ 7 ต่อไปปุ่มนี้ก็จะเป็นปุ่มเปลี่ยนบทความให้กับ App
ให้มองหา app:srcCompat=”@android:drawable/ic_dialog_email”
เปลี่ยนเป็น app:srcCompat=”@android:drawable/ic_media_play”

ส่วนปุ่มนี้จะไปเปิด layout ตัวใด ให้ขึ้นมาแสดงผล
ยังไม่ได้ปรับใน MainActivity.java รอไปทีละขั้น เพราะยังไม่ถึงเวลาของขั้นนี้
แต่เห็นว่า ตอนนี้มี <include layout=”@layout/content_main” />
อยู่ใน activity_main.xml ซึ่งจะเรียก content_main.xml เมื่อเริ่มต้นอยู่แล้ว

2. ทดลองใส่ Tag:img ใน strings.xml
2.1 เริ่มต้นจากการไปหาภาพตัวอย่างมา 5 ภาพ
แล้วใส่ภาพที่หาได้ เข้าไปใน drawable หามา 5 ภาพ คือ lp01.jpg ถึง lp05.jpg
เรื่องนี้เล่าใน ตอนที่ 6 ข้อ 1 มีแบบผ่าน Windows Explorer และผ่าน Android Studio

2.2 ใส่ข้อมูลใน string.xml ประกอบด้วย ข้อความแบบหนา ข้อความธรรมดา และแท็กภาพ

<string name=”data1″>
<b>hello world!</b> Can you see me? <img src=”lp01″ />
</string>

ใน layout_main.xml ที่ Template เตรียมมาให้ใน Basic Activity นั้น
มองหา android:text=”Hello World!”
เปลี่ยนเป็น android:text=”@string/data1″
ถ้า run ตอนนี้
จะเห็นข้อความ Hello world! เป็นตัวหนา
ส่วนข้อความ Can you see me? เป็นตัวธรรมดา
แต่ภาพ lp01 ไม่เห็นว่ามีภาพนี้ออกมา
อธิบายในเบื้องต้น คือ TextView ไม่สนับสนุนการใส่ Tag:img แแบบนี้
ถ้าให้เห็นต้องทำขั้นต่อไป

 

3. สร้างคลาส ImageGetter เพื่อให้แสดงภาพ ตาม Tag:img
3.1 จะใช้งาน Tag:img ใน TextView ได้ต้องมีคลาส ImageGetter ใน MainActivity.java ก็เริ่มจากไปหาคัดลอกคลาสนี้มา แล้วเปลี่ยน packagename เป็น ของท่าน ของผมใช้ com.thaiall.www.imageintextview ใน code กำหนดขนาดภาพที่ผ่านคลาสนี้ให้กว้าง 48*2 และ 64*2 โดยตัวอย่างนี้มี 2 ส่วนคือ ส่วนของ import และส่วนของคลาส ให้สร้างไว้ภายใน class MainActivity
ปล. พอมี ImageGetter แล้ว ในหัวข้อ 3.4 จะเรียกใช้ผ่าน Html.fromHtml ()

import android.text.Html;
import android.os.Build;
import android.graphics.drawable.Drawable;

private class ImageGetter implements Html.ImageGetter {
public Drawable getDrawable(String source) {
int id;
id = getResources().getIdentifier(source, “drawable”, getPackageName());
if (id == 0) {
// the drawable resource wasn’t found in our package, maybe it is a stock android drawable?
id = getResources().getIdentifier(source, “drawable”, “com.thaiall.www.imageintextview“);
}
if (id == 0) {
// prevent a crash if the resource still can’t be found
return null;
}
else {
Drawable d;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //>= API 21
d = getResources().getDrawable(id, getApplicationContext().getTheme());
} else {
d = getResources().getDrawable(id);
}
//d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
d.setBounds(0,0,48 * 2,64 * 2);
return d;
} } }

3.2 การทำให้มีข้อมูลปรากฎใน TextView ผ่าน MainActivity.java
เริ่มต้นจากตั้งชื่อให้ TextView ใน content_main.xml
โดยเพิ่มว่า android:id=”@+id/textView”

3.3 เปลี่ยน data1 และเพิ่ม data2
เพื่อแสดงเทคนิคการจัดการ Tag:img ว่า ใส่แบบ data2 แล้วไม่สำเร็จ
ต้องใส่ Tag:img แบบ data1 ใน strings.xml แล้วใช้ java ช่วยจัดการ

<string name=”data1″>data1 : ok imglp01 imglp02 imglp03</string>
<string name=”data2″>data2 : error <img src=’lp01′ /> <img src=”lp02″ /></string>

3.4 หลังเตรียมข้อมูลใน strings.xml ก็มาเตรียม code ที่จะจัดการกับ Tag:img
โดยเพิ่ม code ข้างล่างนี้ใน MainActivity.java เพื่อจัดการ content_main.xml

import android.content.res.Resources;
import android.widget.TextView;
Resources res = getResources();
String mData1 = res.getString(R.string.data1);
String mData2 = res.getString(R.string.data2); // error here
String mData3 = “data 3 in java file : ok <img src=\”lp04\” /> <img src=lp05 />”;
for(int i=1;i<=3;i++) {
mData1 = mData1.replace(“imglp0” + i, “<img src=lp0″ + i + ” />”);
}
String[] mList ={mData1,mData2,mData3};
String mData = android.text.TextUtils.join(“<br/>”, mList);
TextView htmlTextView = (TextView)findViewById(R.id.textView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
htmlTextView.setText(Html.fromHtml(mData, Html.FROM_HTML_MODE_LEGACY, new ImageGetter(), null)); // for 24 api and more
} else {
htmlTextView.setText(Html.fromHtml(mData, new ImageGetter(),null)); // or for older api
}

https://gist.github.com/thaiall/9fcead1b4fb4baea6b0751957e5df56f

4. สร้าง layout ใหม่ และ string ที่มี HTML tag หลายตัว 
4.1 สร้าง layout_main.xml
แล้วลาก TextView มาวาง เหมือนใน ตอนที่ 7 ข้อ 4
แล้วเติม ScrollView คลุม TextView ตาม ตอนที่ 7 ข้อ 5 ด้วย
พบว่า id ของ TextView คือ android:id=”@+id/textView2″

4.2 เตรียม myArticle ใน strings.xml ที่มีข้อมูลครบถ้วน
จากการไปอ่านบทความของ grokkingandroid.com ทำให้รู้ว่าใน string ต้องใช้ Entity reference คือ &lt; ตอนปิด Tag ไม่จำเป็นต้องใช้ &gt; ก็ได้ หากส่งเข้า TextView ผ่านฟังก์ชัน setText() ในโปรแกรม Java อย่างที่ใช้คือ MainActivity.java

<string name=”myArticle”>
&lt;h1&gt;ไอทีในชีวิตประจำวัน #611 เมื่อเพื่อนขอใช้โทรศัพท์เข้าสื่อสังคม&lt;/h1&gt;&lt;br/&gt; อาจมีสักครั้งที่เพื่อนสนิท หัวหน้า กิ๊ก หรือแฟนขอยืมสมาร์ทโฟนของเรา …
</string>

https://gist.github.com/thaiall/b220a4cb2b8137f747648c56a5bd086d

 

5. ทำให้ปุ่มสั่งเปิด Layout ใหม่ได้ และ Tag:img ทำงานได้
การปรับเพิ่ม MainActivity.java สำหรับรองรับการสั่งเปิด layout_main.xml
เริ่มจากการเพิ่มคำสั่งใน Activity เมื่อคลิ๊กปุ่ม
โดยเพิ่มใน Block : onClick ของ fab.setOnClickListener(..)
เพื่อคุม FloatingActionButton
ทำตาม ตอนที่ 7 ข้อ 6 มีคำสั่งเพียงบรรทัดเดียวที่ต้องเพิ่ม

setContentView(R.layout.layout_main);

แล้วตามด้วยชุดคำสั่งที่ต้องเพิ่ม

Resources res = getResources();
// for layout_main.xml
String mArticle = res.getString(R.string.myArticle);
for(int i=1;i<=3;i++) {
mArticle = mArticle.replace(“imglp0” + i, “<img src=lp0″ + i + ” />”);
}
TextView htmlTextView2 = (TextView)findViewById(R.id.textView2);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
htmlTextView2.setText(Html.fromHtml(mArticle, Html.FROM_HTML_MODE_LEGACY, new ImageGetter(), null)); // for 24 api and more
} else {
htmlTextView2.setText(Html.fromHtml(mArticle, new ImageGetter(),null)); // or for older api
}

 

 

 

https://gist.github.com/thaiall/1e23a3bcf79ee293b6b4baea14bcfd9a

</process>

<website_guide>
+ http://abhiandroid.com/ui/html
+ http://www.grokkingandroid.com/..html-fromhtml/ (webview better)
+ https://developer.android.com/../string-resource.html (&lt;)
+ https://stackoverflow.com/..strings-xml-in-android (spanned)
https://developer.android.com/studio/projects/templates.html
</website_guide>

หมายเหตุ 
ถ้าสนใจติดตามเนื้อหาในบล็อกนี้ สามารถ subscribe ด้วย email ที่อยู่ข้างขวา หรือ click here

#AndroidStudio ตอนที่ 6 การใช้ชื่อภาพแบบซีรี่กำหนดลำดับการแสดงภาพผ่านคลาส TextUtils แทน String.join

#AndroidStudio ตอนที่ 6 การใช้ชื่อภาพแบบซีรี่กำหนดลำดับการแสดงภาพ
ผ่านคลาส TextUtils แทน String.join

<introduction>
นอกจากข้อมูล (String) ที่เล่าไปก่อนหน้านี้ ก็ตามด้วยเรื่อง ภาพ (Image) เข้ามาประกอบการนำเสนอ เคยเล่าในตอนที่ 4 แสดงข้อมูลธรรมดาผ่าน TextView กับตอนที่ 5 แสดงข้อมูลผ่าน NavigationView ที่ทำให้ข้อมูลดูลื่นไหลได้ก็ด้วยการใช้คลาส ScrollView เพื่อทำ Scrolling ได้ เมื่อไปค้นดูพบว่ามีนักพัฒนาเขียนอะไร ทิ้ง Question & Solution ไว้มากมายใน stackoverflow.com คำตอบส่วนใหญ่ผมก็ได้จากที่นั่นหละ

ค้นเรื่องภาพ ก็ไปอ่าน Blog ของ akexorcist.com เล่าเรื่อง Drawable Resource และแนะนำเรื่อง Android Drawable Importer ก็คงต้องหาโอกาสไปอ่านสักวัน แต่ตอนนี้จะเน้นการส่งภาพเข้า Android Studio และนำมาแสดงผลอย่างง่าย คำว่าง่าย ก็ต้องดูว่างง่ายแบบ Front-End หรือ Back-End ทีแรกว่าจะทำภาพ slide ง่าย ๆ แต่ผมตั้งโจทย์ไว้ว่าจะทำ Layout ให้ง่ายที่สุด

งานนี้เลยต้องปรับ Back-End ให้ Front-End ทำงานได้ง่าย สรุป คือ 1) เตรียมเฉพาะ TextView มาเก็บลำดับภาพ และ 2) ImageView มาเก็บภาพ เพียง 2 คลาส แล้วแทนที่ภาพใหม่ทับที่เดิมไปเรื่อย ดังนั้นจะทำงานนี้ให้สำเร็จต้องเข้าไปเขียน Java เยอะหน่อย ไปเจอปัญหาเรื่อง String.join ที่ไม่มีให้ใช้ กว่าจะยอมรับไปใช้ TextUtils.join ก็ไล่อ่าน Blog จนมั่นใจว่าเราต้องเปลี่ยนคำสั่งเชื่อมต่อ String กันแล้ว
</introduction>

<process>
กระบวนการในการพัฒนา APP
เพื่อคลิ๊กไปบนภาพ แล้วเปลี่ยนเป็นภาพต่อไป ไหลไปเช่นนี้วนไปไม่รู้จบ
มีขั้นตอน ดังนี้

1. สร้าง New Project แบบ Empty Activity
เพราะไม่ต้องการความเป็นอัตโนมัติมากนัก ปรับเองบ้าง เมื่อ Project พร้อมทำงานแล้ว
ก็เข้าไป Right Click ที่ res แล้ว New Folder ชื่อ drawable-mdpi
หลังจากนั้นในกรณีมีภาพในเครื่องคอมพิวเตอร์ที่พร้อมนำมาใช้
สำหรับผมเตรียมภาพ lp01.jpg ถึง lp05.jpg แล้ว
ภาพทุกภาพมีขนาด 480*640 เป็นภาพแนวตั้ง
ก็ให้ไปเปิดรายการภาพด้วย Windows Explorer
แล้ว Right Clip บนภาพที่เตรียมไว้ในเครื่องคอมพิวเตอร์ด้วย Windows Explorer
เลือก copy ซึ่งเลือกได้หลายภาพพร้อมกัน โดยใช้ปุ่ม ctrl หรือ shift เข้าช่วย

1.1 วิธีแรก เปิด Project ใน Android Studio แล้วส่งเข้าไป
โดยกลับไปกด Right Clip บน folder : drawable ใน Project
เลือก paste
จะมี Windows ขึ้นถามว่าลงในห้อง drawable หรือ drawable-mdpi
ก็ให้เลือก drawable-mdpi แฟ้มที่ลงไปในห้อง drawable จะมี (mdpi) ต่อท้าย

1.2 วิธีที่สอง เปิด Folder ด้วย Windows Explorer แล้วส่งเข้าไป
เพราะ Project ของ Android Studio ไม่ลึกลับ เห็น Folder Structure คล้ายกัน
ของผมเข้าไปที่ C:\Users\[User Name]
\AndroidStudioProjects\[Project Name]\app\src\main\res\drawable-mdpi
เคย copy ภาพมาแล้ว ก็ paste ได้เลย เมื่อกลับไปเปิดดูใน Android Studio
ก็เห็นว่าภาพเข้าไปอยู่เหมือนเรานำเข้าผ่าน Android Studio ในข้อ 1.1 นั่นหละ

2. เข้า activity_main.xml จะเห็น Layout ของแอพ มีเพียง TextView ตัวเดียว
เปลี่ยนคำว่า “Hello World!” เป็น “1” เตรียมใช้เก็บตำแหน่งภาพแบบซีรี่
แล้วเข้า Tab:Design ถ้าปัจจุบันอยู่ใน Tab:Text
มองหา Palette, Images, ImageView ลากไปวางไว้บนจอภาพ
เป็นการเพิ่มวัตถุลงไปในจอภาพของเราอีก 1 รายการ
จากนั้นจะมีหน้าต่างมาให้เลือกภาพ ก็คลิ๊กที่ภาพ lp01 แล้วกด OK
เท่านี้แอพของเราก็จะมีภาพมาแสดงผลแล้ว

3. เข้า Tab:Text ของ activity_main.xml แล้วปรับ code ดังนี้
– ลบ app:layout ใน TextView ออกทั้งหมด
– ลบ tools:layout ใน ImageView ออกทั้งหมด
– เพิ่ม android:id=”@+id/textview” ให้กับ TextView
– ย้าย code ImageView ขึ้นมาด้านบน อยู่ก่อน TextView น่าจะเห็นเลข 1 ทับภาพ
เท่านี้ก็จะเห็นภาพอยู่ข้างหลัง และมีตัวอักษร 1 ลอยเหนือภาพที่มุมบนซ้าย

4. ไปเพิ่ม Activity ใน MainActivity.java จะอธิบายเฉพาะที่พิเศษ
ส่วน code ทั้งหมด ดูภาพรวมได้จาก gist ด้านล่าง
สิ่งที่ทำคือ เพิ่ม import และ code ใต้ class และ method ดังนี้

4.1 การแสดงข้อความเหนือภาพ เมื่อคลิ๊กภาพ เหมือน Notification เลย
แต่ code 5 บรรทัดนี้
บรรทัดแรก แปลงตัวอักษรจาก TextView เป็น Integer แล้ว + 1 จะได้เลขสำหรับเลื่อนภาพ
บรรทัดที่สอง มีภาพเตรียมไว้ 5 ภาพคือ lp01 ถึง lp05 คุมว่า ถึงเลข 5 ก็วนมาที่ 1 อีก
บรรทัดที่สาม เตรียม List ไว้ทำงานคู่กับ TextUtils.join เพื่อต่อ String
บรรทัดที่สี่ ทำการต่อ String จะได้ข้อความที่ครบถ้วน
บรรทัดสุดท้าย ใช้คลาส Toast แสดงข้อความแบบ Notification ว่าตอนนี้ถึงภาพไหน

int getV = Integer.valueOf(mTextView.getText().toString()) + 1;
if(getV > 5) { getV = 1; }
String[] List1 ={“เลื่อนไปภาพต่อไป ภาพที่ “,String.valueOf(getV)};
String s = TextUtils.join(” “, List1);
Toast.makeText(MainActivity.this, s , Toast.LENGTH_LONG).show();

4.2 การเปลี่ยนภาพใน ImageView
บรรทัดแรก เตรียมข้อความใน List
บรรทัดที่สอง นำสิ่งที่อยู่ใน List มาต่อกันด้วย TextUtils.join เป็นชื่อภาพ
บรรทัดที่เหลือ นำชื่อภาพที่เป็น String ส่งให้กับ

String[] List2 ={“lp0”,String.valueOf(getV)};
s = TextUtils.join(“”, List2);
Resources res = getResources();
String mDrawableName = s;
int resID = res.getIdentifier(mDrawableName , “drawable”, getPackageName());
mImageView.setImageResource(resID);

4.3 สุดท้าย หลังแสดง Notification และเปลี่ยนภาพแล้ว ก็ต้องส่งเลขตัวใหม่ที่ +1 เข้า TextView มี method setText มาช่วย ตามตัวอย่างข้างล่างนี้เลย

mTextView.setText(String.valueOf(getV));

 

https://gist.github.com/thaiall/180de945ef049198d260a08ae20a47cf

</process>

<website_guide>
+ http://www.android-examples.com/set-onclicklistener-on-imageview-in-android-example/
+ http://www.akexorcist.com/2016/07/best-practice-for-android-drawable-resource-management.html
+ https://developer.android.com/guide/practices/screens_support.html
+ http://www.thaiabc.com/lovelampang/nw/index.php
+ https://www.javatpoint.com/java-string-join
+ https://developer.android.com/reference/android/text/TextUtils.html
</website_guide>

หมายเหตุ 
ถ้าสนใจติดตามเนื้อหาในบล็อกนี้ สามารถ subscribe ด้วย email ที่อยู่ข้างขวา หรือ click here

#AndroidStudio ตอนที่ 4 ทำหน้าเพจที่มีข้อมูลมากจนต้อง scroll ลงไป และการเพิ่ม icon ประจำแอพ

ตอนที่ 4 ทำหน้าเพจที่มีข้อมูลมากจนต้อง scroll ลงไป และการเพิ่ม icon ประจำแอพ

<introduction>
แอพพลิเคชั่น มักจะหมายถึง โปรแกรมที่ทำงานบน Smartphone เดี๋ยวนี้ใคร ๆ ก็ใช้แทน Desktop กันเยอะ แล้วแอพก็ยังโด่งดังมาจากกระแสนิยมโหลดโปรแกรมใน App Store ของ Apple และ Google ก็ทำ Play Store สิ่งที่อยู่ในแอพ เป็นอะไรก็ได้ แต่สิ่งหนึ่งที่ไม่อาจมองข้ามคือข้อมูล ให้อ่าน ให้ศึกษา และเรียนรู้ อาทิ บทความ ประวัติความเป็นมา เรื่องเล่า และนิยายพื้นบ้าน เป็นต้น

ถ้าเรื่องราวที่ต้องการใส่ในแอพ คือ ชุดตัวอักษรที่มีจำนวนมาก การใส่ตัวอักษรจำนวนมากในหนึ่งหน้า แล้วเราก็เปิดอ่านอย่างต่อเนื่อง เลื่อนลงไปผ่าน Scroll Bar นั่นเป็นโจทย์ที่น่าสนใจ สำหรับการอธิบายขั้นตอนการสร้างแอพครั้งนี้ แล้วผมก็มีบทความที่จะทดสอบกับบทเรียนนี้ คือ ไอทีในชีวิตประจำวัน เรื่องที่่ 611 และปิดบทความ How to ครั้งนี้ด้วยขั้นตอนการติดตั้ง icon ประจำแอพ หากใคร install app ใน smart phone ก็จะมี icon สวย ๆ ปรากฎไม่ใช้สัญลักษณ์ android ที่เค้าทำเป็นค่า Default มาให้
</introduction>

<process>
กระบวนการในการพัฒนา APP

เพื่อแสดงข้อมูลเป็น Text หรือ String ที่มีขนาดยาวเกิน 1 หน้าที่จะแสดงผลได้
จึงต้องใช้ความสามารถในการทำ ScrollView

มีขั้นตอน ดังนี้

1. เริ่มต้นก็สร้าง Project แบบ Empty Activity แล้วจากการสำรวจว่า TextView อยู่ตรงไหน เพราะเป็นพระเอกของตอนนี้ โดยเข้าไปใน app, res, layout, activity_main.xml  พบว่า สามารถเข้าดูได้ 2 แบบคือ Tab:Design หรือ Tab:Text ถ้าเลือกดูแบบ Text ก็เหมือนดู Source code ที่เป็น XML จะพบกับ Tag ที่ชื่อ TextView มี Properties คือ android:text=”Hello World!” ซึ่งคำยอดนิยมนี้ สามารถเปลี่ยนได้ตามต้องการ

2. สังเกตก็จะพบส่วนของ properties หรือ คุณสมบัติ ของ TextView จะเห็นได้หากเราคลิ๊กคำว่า TextView ใน Component Tree จะพบ Properties Windows ทางด้านขวา
และมองเห็นคุณสมบัติของ layout_width กับ layout_height ที่เดิมมีค่าเป็น wrap_content ซึ่งหมายความว่า จะเปลี่ยนแปลงตำแหน่งตามเนื้อ content และมีตำแหน่งกลางจอภาพโดยอัตโนมัติ หากเปลี่ยนคุณสมบัติใน layout ทั้ง 2 เป็น match_parent
ก็จะทำให้อ้างอิงกับหน้าต่าง และ TextView ขยายเต็มจอภาพ และคำว่า Hello World! ก็จะไปอยู่ที่มุมบนซ้าย

นี่เป็นคุณสมบัติเติมขณะที่ไม่ได้แก้ไขใด ๆ
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Hello World!”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintLeft_toLeftOf=”parent”
app:layout_constraintRight_toRightOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />

แล้วถ้าสร้าง TextView ด้วยการใช้เครื่องมือ แบบ Drag and Drop ก็จะได้ Code ดังนี้

<TextView
android:id=”@+id/textView”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”TextView” />

3. ขั้นต่อไป ก็ไปหาเนื้อหา (Content) มาวางในช่อง text เพื่อให้มีข้อมูลแสดงผลมากกว่าแค่ Hello World! ผมหาบทความที่มีข้อมูลขนาด 2313 ตัวอักษร แล้ว paste ลงไป เรียงต่อกันเป็นพรืด เมื่อทดสอบแสดงผลใน Device emulator : Nexus_5X_API_19 พบว่าไม่แสดงภาษาไทย หายไปหมด เหลือแต่ภาษาอังกฤษ 

การพัฒนา App ในเครื่อง สามารถส่งงานไปแสดงผลในอุปกรณ์ได้หลายวิธี แต่การเลือก Device emulator อาจไม่สวยงาม และช้ากับเครื่องที่แรมน้อย จึงเปลี่ยนไป Run บน Smartphone ก็จะได้ผลงานเหมือนจริงที่ไม่ใช่การจำลองอุปกรณ์

4. การทำให้แสดงผลแบบมี Scroll Bar ให้เลือนขึ้นลง ดูข้อมูลที่เกิน 1 หน้าได้ ต้องใช้ ScrollView ร่วมกับ TextView มีตัวอย่างตาม Code ด้านล่าง และทำงานใน Smart Phone ได้อย่างถูกต้อง

https://gist.github.com/thaiall/2ebf07ea7a56c033b660bf6abb1a70c1

5. พบว่า IDE ของ Android Studio  แนะนำว่าให้ใช้ @String แทนการวางข้อมูลจำนวนมากใน TextView โดยตรง จึงเข้า app, res, values, strings.xml แล้วเพิ่ม Tag นี้

<string name=”data1“>
ข้อมูลที่นี่จำนวน 2313 ตัวอักษร เป็นบทความที่ย้ายไปเขียนไว้ด้านล่าง
</string>

แล้วไปแก้ไขแฟ้ม activity_main.xml ให้อ้างอิงข้อมูลจาก data1 ตาม code ด้านล่างนี้

<TextView
android:id=”@+id/textView1″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:scrollbars=”vertical”
android:text=”@string/data1” />

6. การกำหนดภาพ icon ประจำ Project ถ้าไม่ชอบภาพตัว Android ที่เค้ามีมาให้ ก็เข้าไปกด right click ที่ app, res แล้วเลือก New, image asset เลือก image เลือก file ภาพจากในคอมพิวเตอร์ แล้วกด next, finish หลังจากนั้น ภาพที่อัพโหลดเข้าไปจะไปปรากฎใน app, res, mipmap, ic_launcher.png จำนวน 5 ภาพ ต่อไปถ้าส่ง APK เข้า Smartphone ก็จะได้เห็นภาพสวย ๆ ชวนให้ Click กัน

7. เขียนโปรแกรมเสร็จแล้วก็ต้องทดสอบ RUN แฟ้มที่ต้องการนำไปใช้
คือ app-debug.apk  มักอยู่ใน C:\Users\[user name]
\AndroidStudioProjects\[project name]\app\build\outputs\apk\

การทดสอบก็มักใช้ Device Emulator เพราะง่ายที่สุด แต่เครื่องคอมพิวเตอร์ที่ใช้มี RAM 4GB ลง Win10 OEM ที่โหลด Windows Defender ตลอดเวลาอีก เมื่อสั่งเปิด Emulator ก็จะทำให้เครื่องช้าลงอย่างเห็นได้ชัด และผลการจำลองก็ไม่สวยงาม จึงเลือกที่จะส่งแฟ้ม app-debug.apk ไปประมวลผล นอกเครื่องคอมพิวเตอร์ แล้วผมเลือกส่งออกไปทาง Smart Phone ผ่านการเชื่อมต่อของ APP : WiFi ADB และทำผ่าน DOS ก็สะดวก จะส่งผ่าน Android Studio ก็ทำได้ ซึ่งการ Connect ทำครั้งเดียว ส่วนการ Install จะทำหลังจากการ Build ในแต่ละครั้ง

</process>

<website_guide>
https://guides.codepath.com/android/Working-with-the-ScrollView
http://www.viralandroid.com/2015/10/how-to-make-scrollable-textview-in-android.html
+ http://blog.teamgrowth.net/index.php/android/how-to-make-the-textview-in-android-scrollable
+ http://www.thaiall.com/itinlife
+ https://play.google.com/store/apps/details?id=com.thaiall.itarticle
</website_guide>

 

ไอทีในชีวิตประจำวัน #611 เมื่อเพื่อนขอใช้โทรศัพท์เข้าสื่อสังคม ()

อาจมีสักครั้งที่เพื่อนสนิท หัวหน้า กิ๊ก หรือแฟนขอยืมสมาร์ทโฟนของเราเข้าสื่อสังคม ในกรณีที่โทรศัพท์ของผู้ที่เข้ามายืมมีปัญหาและมีเหตุผลจำเป็น คำตอบโดยปกติคือ ไม่ให้ยืม แต่ถ้าคำตอบต้องเป็นให้ยืม แล้วต้องทำอย่างไร บางครั้งอาจต้องมีการทำงานกลุ่ม ต้องการภาพ หรือคลิ๊ปที่มีคุณสมบัติเหมือนกันจากสมาร์ทโฟนเครื่องเดียวกันเพื่ออัพโหลด หรือในกลุ่มมีเพียงของเราที่เชื่อมต่อเครือข่าย 3G หรือ WiFi ได้ หรือในกลุ่มพร้อมใจกันไม่ได้พกอุปกรณ์ติดตัวไปด้วย นั่นมีเหตุผลมากมายที่ต้องให้ยืม แล้วผู้พัฒนาแอพพลิเคชั่นเข้าใจเงื่อนไขเหล่านี้ จึงพัฒนาโปรแกรมบราวเซอร์ให้สามารถเปลี่ยนเป็นโหมดส่วนตัวที่ไม่บันทึกข้อมูลขณะใช้งาน และหายไปเมื่อเลิกใช้ได้ ซึ่งรองรับการให้เพื่อนยืมสมาร์ทโฟนเพื่อเข้าสื่อสังคมได้

ในสมาร์ทโฟนมักมีโปรแกรมบราวเซอร์ (Browser) อยู่หลายค่าย อาทิ Chrome, Firefox, Opera, Baidu หรือ Dolphin ผู้ใช้บางท่านอาจลงผลิตภัณฑ์หลายทุกค่าย ซึ่งมักมีคุณสมบัติในการเข้าโหมดส่วนตัวทุกค่าย แต่ละค่ายก็จะมีชื่อเรียกโหมด และวิธีการใช้งานที่แตกต่างกันไป แต่โหมดส่วนตัวหรือโหมดปลอดภัยจะมีคุณสมบัติเหมือนกัน คือ จะไม่เก็บรหัสผู้ใช้และรหัสผ่านขณะใช้งานในโหมดปลอดภัยนี้ ไม่บันทึกการใช้งานให้ติดตามได้ใน History และไม่เก็บข้อมูลคุกกี้ไว้ในเครื่องเมื่อเลิกใช้ และทำงานแยกออกจากโหมดใช้งานปกติ ซึ่งผู้ใช้สามารถล๊อกอินเข้าระบบสื่อสังคม อาทิ เฟสบุ๊ค หรือทวิตเตอร์ด้วยบัญชีผู้ใช้ที่แตกต่างกันระหว่างโหมดปกติ และโหมดปลอดภัยคนละชื่อ หากมี 3 โปรแกรมบราวเซอร์ และสลับโหมดก็จะสามารถให้เพื่อนไม่ต่ำกว่า 6 คนเข้าสู่ระบบของสื่อสังคมด้วยบัญชีที่แตกต่างกันได้ แต่คงจะวุ่นวายอยู่ไม่น้อยกับการใช้สมาร์ทโฟนเครื่องเดียวโดยผู้ใช้ 6 คน

การเข้าโหมดลับของโปรแกรม Chrome เรียก New incognito tab แล้วยังเปิดได้หลาย Tab และมี Notification ที่สั่งปิดได้ทุกแท็บ ส่วนโปรแกรม Firefox เข้าผ่าน Private tab หรือ Tools, New Guest Session ก็จะมีคุณสมบัติที่แตกต่างกัน ส่วนโปรแกรม Opera เข้าผ่าน Tab ที่ชื่อ Private คล้ายกับของ Firefox การใช้งานในโหมดลับนี้จะป้องกันการเห็นข้อมูลเฉพาะในตัวเครื่องเท่านั้น การเชื่อมต่อแล้วส่งข้อมูลออกไปภายนอกยังคงเปิดเผย และมีการเก็บข้อมูลการจราจร (Traffic Log) เช่นเดิม ผ่านผู้ให้บริการเครือข่าย ข้อความที่ส่งไปในสื่อสังคมก็ไม่ลับ หากข้อความนั้นมีคุณสมบัติเป็นสาธารณะ ดังนั้นการส่งข้อความใดก็ควรอยู่ในวิจารณญาณว่าจะไม่ก่อความเดือดร้อนให้ตนเอง หรือผู้อื่นไม่ทางใดก็ทางหนึ่ง เพื่อความปลอดภัยของตนเองเป็นที่ตั้ง เพราะความลับไม่มีในโลก และอย่างน้อยความลับที่ว่าลับนักนั้นตนเองก็รู้

หมายเหตุ 
ถ้าสนใจติดตามเนื้อหาในบล็อกนี้ สามารถ subscribe ด้วย email ที่อยู่ข้างขวา หรือ click here