12 September 2013

สร้างภาพง่ายๆจาก XML ด้วย Shape Drawable

Updated on


        โดยปกติแล้วเวลาผู้ที่หลงเข้ามาอ่านจะทำแอปพลิเคชันซักตัวขึ้นมาก็ต้องมีการออกแบบรูปร่างหน้าตาอยู่แล้วใช่มั้ยล่ะ แต่ส่วนใหญ่ก็จะออกแบบบางแอปฯที่ต้องมีการนำภาพเข้ามาใช้งานด้วย ซึ่งบางทีก็มักจะมีปัญหาตามมาอย่างเช่นการทำภาพหลายขนาดเพื่อรองรับหน้าจอหลายๆแบบ ทำให้เกิดปัญหา APK มีขนาดบวมเพราะภาพเหล่านี้

        ซึ่งในบทความนี้เจ้าของบล็อกจะมาแนะนำสิ่งที่เรียกว่า Shape Drawable ให้รู้จักกัน เป็นหนึ่งในความสามารถของแอนดรอยด์ที่ทำให้นักพัฒนาสามารถสร้างภาพรูปทรงง่ายๆได้ด้วยการสร้างไฟล์ให้อยู่ในรูป XML

        Shape Drawable นั้นเป็นหนึ่งใน Drawable Resource ตัวไฟล์จะเก็บไว้ในโฟลเดอร์ drawable เหมือนกับไฟล์ภาพเลย แต่ตัว Shape Drawable จะประกอบไปด้วย XML ล้วนๆ ซึ่งเจ้า XML ที่ว่าเนี่ยแหละ จะมาสร้างภาพที่มีรูปทรงแบบง่ายๆ โดยไม่ต้องนั่งทำภาพเองให้วุ่นวาย

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

        ถัดมาก็คือแก้ปัญหาเวลาแสดงภาพบน View ซักตัวที่มีขนาดของ View ไม่แน่นอน เวลาภาพถูกย่อขนาดเฉพาะด้านใดด้านหนึ่ง ถ้าเป็นภาพปกติก็จะเจอปัญหาภาพบิดเบี้ยวไปบ้าง แต่ปัญหานี้จะหมดไปเมื่อใช้ Shape Drawable



        จะเห็นว่าถ้าผู้ที่หลงเข้ามาอ่านใช้ Bitmap Drawable แบบทั่วๆไป เวลามีการเปลี่ยนขนาดของ View ในด้านใดด้านหนึ่งก็จะทำให้ภาพที่แสดงนั้นบิดเบี้ยวเกินไป แต่ในกรณีที่ใช้ Shape Drawable จะไม่ต้องกังวลเรื่องภาพจะบิดเบี้ยว

ลองสร้าง Shape Drawable กันเถอะ

        สำหรับ Shape Drawable นั้นจะสร้างเป็นไฟล์ XML ขึ้นมา โดยเก็บไว้ในโฟลเดอร์ drawable ครับ ไม่ต้องแยกตาม Qualifier แบบ Bitmap Drawable แต่อย่างใด

        วิธีการสร้าง Shape Drawable นั้นก็ไม่ต่างอะไรกับไฟล์ XML ทั่วไปเลย แต่เพื่อให้ง่ายขึ้น ผู้ที่หลงเข้ามาอ่านสามารถคลิกขวาที่โฟลเดอร์ drawable แล้วเลือกไปที่ New > Drawable resources file ได้เลย


        กำหนดชื่อไฟล์ได้ตามใจชอบ ส่วน Root element ให้กำหนดเป็น shape


        เท่านี้ก็จะได้ไฟล์ Shape Drawable แล้ว~


        ให้ลองเพิ่มคำสั่งเข้าไปดังนี้

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

    <solid android:color="#c3ce46" />

</shape>

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <Button
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@drawable/shape_button_green"
        android:text="OK"
        android:textSize="18sp" />

</LinearLayout>

        พอลองเปิดหน้า Preview ขึ้นมาก็จะเห็นปุ่มมีพื้นหลังเป็นวงกลมสีเขียว


        นี่แหละครับ ผลลัพธ์ของการใช้ Shape Drawable ผู้ที่หลงเข้ามาอ่านสามารถลองปรับขนาดของปุ่มตัวนี้ดูได้ ไม่ว่าจะขยายขนาดหรือย่อขนาด ก็ยังได้ภาพพื้นหลังที่คมชัด และถ้าปรับความกว้างและความสูงของปุ่มให้ไม่เท่ากันก็ได้พื้นหลังเป็นวงรีแทน

Shape Drawable มีแบบไหนบ้าง?

        รูปร่างต่างๆที่สามารถสร้างด้วย Shape Drawable ได้ จะมีทั้งหมด 4 แบบด้วยกัน ดังนี้


        ซึ่งทั้ง 4 แบบนั้นจะมีการกำหนดค่าที่แตกต่างกันนิดหน่อย แต่ส่วนใหญ่ก็เหมือนๆกันน่ะแหละ

        • Corners
        • Solid
        • Gradient
        • Padding
        • Size
        • Stroke

Corners

        เป็นการกำหนดความโค้งของมุม Shape โดยปกติแล้วจะอยู่ในแท็กของ Shape ดังนี้

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    
    <corners 
        android:radius="integer"
        android:topLeftRadius="integer"
        android:topRightRadius="integer"
        android:bottomLeftRadius="integer"
        android:bottomRightRadius="integer" />
    
</shape>

        สำหรับค่าที่กำหนดแนะนำให้ใช้เป็นหน่วย dp จะเหมาะกว่า และไม่จำเป็นต้องกำหนดให้ครบทุกอันก็ได้ เฉพาะอันที่ต้องการก็ได้ และถ้ากำหนดเป็น radius แล้วอันที่เหลือไม่ต้องกำหนด เพราะสี่อันที่เหลือคือการกำหนดความโค้งของมุมแบบแยกฝั่ง



        ในกรณีที่กำหนดทั้ง radius และแยกฝั่งด้วย จะมีผลขึ้นอยู่กับ radius เท่านั้น

Gradient

        เป็นการไล่เฉดสีบนพื้นผิวของภาพ

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    
    <gradient
        android:angle="integer"
        android:startColor="color"
        android:centerColor="integer"
        android:endColor="color"
        android:type="type"
        android:gradientRadius="integer" 
        android:centerX="integer"
        android:centerY="integer" />
    
</shape>

        angle คือมุมทิศทางของ Gradient โดยกำหนดเป็นตัวเลข -270 ถึง 270 ได้เลย ดังตัวอย่างนี้


       การกำหนดค่าสีจะมีทั้งหมด 3 ค่าด้วยกัน คือ startColor, centerColor และ endColor ซึ่งก็คือสีที่ต้องการกำหนดใน Gradient เรียงลำดับจากซ้ายไปขวา โดยกำหนดค่าสีเป็นแบบ #RRGGBB หรือ #AARRGGBB


        ต่อไปเป็น type ที่เอาไว้กำหนดรูปแบบของ Gradient โดยจะมีรูปแบบของ Gradient ทั้งหมด 3 รูปแบบด้วยกัน คือ Linear ไล่เฉดแนวยาว Radial ไล่เฉดวงกลมจากจุดศูนย์กลางและ Sweep เป็นการไล่เฉดสีหมุนเป็นวงกลม


        ต่อไปเป็น gradientRadius ที่เอาไว้กำหนดรัศมีของเส้น Gradient โดยกำหนดเป็นตัวเลขได้เลย​ สามารถกำหนดเป็น % ก็ได้ หรือจะกำหนดเป็นหน่วย DP ก็ได้เช่นกัน (ถ้าไม่ใส่ %)


        ต่อไปเป็น centerX และ centerY เป็นการกำหนดจุดเริ่มต้นของ startColor ใส่ค่าเป็น % ก็ได้ 0% ถึง 100% ถ้ามากกว่านั้นก็จะล้นตัววัตถุออกไปแทนหรือจะใส่ 0 ถึง 1 ก็มีค่าเหมือนกับ 0% ถึง 100% เช่นกัน โดยจะใช้ได้กับรูปแบบ Radial และ Sweep เท่านั้น ซึ่งการใช้กับ Radial จะต้องกำหนด gradientRadius ด้วย ส่วน Sweep นั้นไม่จำเป็นต้องกำหนดแต่อย่างใด


Padding

        มันก็คือค่า Padding เหมือนกับตอนจัด Layout นั่นแหละ แต่สามารถกำหนดเตรียมไว้ตั้งแต่ใน Shape Drawable ได้เลย

Size

        กำหนดขนาดของ Shape ที่สร้างขึ้น จะกำหนดหรือไม่ก็ได้ เพราะถ้าไม่กำหนด เวลาใช้งานมันก็จะอิงกับขนาดของ View ตัวนั้นๆ นั่นแหละ

Solid

        เป็นการกำหนดสีของ Shape ซึ่งจะทับซ้อนกับ Gradient ถ้ามี Gradient ด้วยก็จะใช้ Gradient แทนเลย

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    
    <solid android:color="color" />
    
</shape>

        สำหรับ color ก็คือค่าสีในรูปของ #RRGGBB หรือ #AARRGGBB นั่นเอง


Stroke

        เป็นการกำหนดเส้นขอบของ Shape นั้นๆ ไม่มีผลกับ Shape แบบ Line

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    
    <stroke
        android:width="integer"
        android:color="color"
        android:dashWidth="integer"
        android:dashGap="integer" />
    
</shape>

        width คือความหนาของเส้นของ แนะนำให้กำหนดเป็นหน่วย dp ส่วน color คือสีของเส้นขอบ


        ส่วน dashWidht กับ dashGap ขออธิบายคู่กันเลย เพราะใช้คู่กัน

        Dash เป็นการลากเส้นประ โดยที่ dashWidth คือระยะของเส้นขีด และ dashGap เป็นระยะที่จะเว้นว่าง โดยจะสลับกันแบบนี้อัตโนมัติ แนะนำให้กำหนดค่าทั้งสองนี้ในหน่วยของ dp


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

        ทีนี้ขอพูดถึง Shape แบบ Ring หน่อย อันนี้จะพิเศษนิดนึง ตรงที่ในแท็กของ Shape จะต้องกำหนดเพิ่มอีกสามอย่างคือ innerRadius, thickness และ useLevel

        ซึ่ง innerRadius และ thickness ให้กำหนดเป็น dp ส่วน useLevel ให้กำหนดเป็น False (ขี้เกียจอธิบาย)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:innerRadius="60dp"
    android:shape="ring"
    android:thickness="10dp"
    android:useLevel="false">

    <solid android:color="#ff4081" />

</shape>


        ทีนี้ก็ถึงความสนุกล่ะ เมื่อจับคำสั่งต่างๆเหล่านี้มาผสมกับ





สรุป

        จะเห็นว่า Shape Drawable เป็นหนึ่งในความสามารถของแอนดรอยด์ที่จะช่วยให้นักพัฒนาสามารถสร้าง Shape ในรูปแบบง่ายๆเพื่อใช้งานในแอปฯได้ โดยไม่ต้องมานั่งทำภาพเป็นไฟล์ JPG หรือ PNG เสมอไป ซึ่งจุดเด่นหลักๆของ Shape Drawable คือไม่ต้องกลัวภาพแตก เพราะเป็นการวาดภาพด้วย Canvas ซึ่งจะวาดใหม่ทุกครั้งอยู่ไปแสดงผลบน View ต่างๆ และตัวไฟล์ XML ของ Shape Drawable ก็มีขนาดเล็ก จงทำให้ลดขนาดของไฟล์ APK ได้พอสมควรเลยล่ะ

        ดังนั้นทางที่ดีเวลาออกแบบแอปพลิเคชันก็แนะนำว่าควรออกแบบไม่ซับซ้อนมากนัก ควรมีลักษณะ Flat หรือจะอิงตาม Material Design เลยก็ได้ เพื่อที่ว่าจะได้ไม่ต้องเสียเวลาทำภาพให้กับหน้าจอแต่ละแบบ

        บทความเรื่องดีไซน์ทำแล้วสนุกจริงๆ แต่ใช้เวลาทำนานเกินไปหน่อย