15 July 2015

วิธีการเรียกใช้งาน Resource โดยกำหนดชื่อไฟล์จาก String

Updated on


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

        สำหรับบทความนี้จะเป็นการดึง Resource หรือไฟล์ที่อยู่ในโฟลเดอร์ res มาใช้งาน โดยใช้ชื่อไฟล์ที่เป็น String เป็นตัวกำหนดว่าจะดึงไฟล์ไหน

การทำงานเบื้องต้นของ Resource บนแอนดรอยด์

        ผู้ที่หลงเข้ามาอ่านทุกคนย่อมรู้อยู่แล้วว่าเวลาจะเรียก Resource ใดๆในโฟลเดอร์ res จะต้องเรียกผ่าน ID ของไฟล์นั้นๆ


        เมื่อนักพัฒนาเพิ่มไฟล์เข้ามาใน Resource ไฟล์นั้นๆก็จะถูกสร้าง Resource ID เฉพาะตัวขึ้นมาแล้วเก็บไว้ในไฟล์ที่ชื่อว่า R.java เพื่อให้นักพัฒนาสามารถเรียกใช้งานในโค๊ดโดยไม่ต้องกำหนด Path ของไฟล์โดยตรง แต่จะเรียกผ่าน R.drawable.ic_launcher แทน ซึ่งในความเป็นจริงก็คือ Integer ที่เก็บค่าตัวเลขไว้ ตัวอย่างเช่น 0x7f040019 (ค่าตัวเลขนี้ระบบ Generate ขึ้นมาให้ตอนที่ Build Project)

        ที่ต้องทำแบบนี้ก็เพื่อที่ว่าตัวแอนดรอยด์จะได้จัดการกับ Resource Configuration ได้ง่ายขึ้น แยกโฟลเดอร์เป็น mdpi, hdpi และ xhdpi ได้ (หรืออื่นๆ) โดยที่นักพัฒนาไม่ต้องไปพิมพ์ชื่อโฟลเดอร์ย่อยๆพวกนี้เอง ที่ต้องทำก็แค่เรียกไปที่ R.drawable.ic_launcher เท่านั้น ที่เหลือระบบจะจัดการให้ว่าต้องไปเรียกที่โฟลเดอร์ drawable-mdpi, drawable-hdpi หรือว่า drawable-xhdpi

Resource ID ไม่ได้ถูกกำหนดตายตัว

        เนื่องจาก ID นั้นถูก Generate จากระบบ และเมื่อมีการแก้ไขไฟล์ในนั้นไปเรื่อยๆก็จะทำให้ Resource ID ของไฟล์นั้นๆเปลี่ยนแปลงได้

        นั่นก็หมายความว่านักพัฒนาไม่สามารถใช้ Resource ID เพื่ออ้างอิงไฟล์นั้นๆได้เสมอไป เพราะวันหนึ่งที่นักพัฒนาแก้ไขโปรเจคนั้นๆอาจจะทำให้ Resource ID เปลี่ยนไปได้ แล้วเกิดปัญหาข้อมูลที่เคยเก็บไว้ไม่สามารถใช้ได้ เพราะหาไฟล์นั้นๆไม่เจอ

แล้วจะใช้วิธีไหนดีล่ะ?

         จริงๆมีอีกหลายวิธีที่ทำให้นักพัฒนาไม่ต้องไปเก็บค่า Resource ID โดยตรง และบทความนี้ก็เป็นหนึ่งในวิธีที่ว่า โดยจะใช้การกำหนดจากชื่อไฟล์ที่อยู่ใน Resource นั่นแหละ เพื่อใช้ในการดึงไฟล์นั้นๆมาใช้งาน

เอา String มาดึงไฟล์จาก Resource ได้ด้วยหรอ?

        ได้นะเออ แต่ว่าเป็นวิธีการทางอ้อมมากกว่า เพราะเมื่อนักพัฒนารู้ว่าไฟล์ใน Resource ถูกระบุตำแหน่งด้วย Resource ID ที่เป็นชุดตัวเลข Integer ที่ระบบ Generate ขึ้นมา ดังนั้นก็ต้องเอาชื่อไฟล์ที่เป็น String นี่แหละ ไปหามาว่าไฟล์ที่ชื่อนี้มี Resource ID เป็นอะไร แล้วค่อยเอา Resource ID ที่ได้ไปดึงไฟล์อีกที

         ข้อควรจำ กำหนดแค่ชื่อไฟล์ ไม่ต้องใส่นามสกุลไฟล์นะจ๊ะ


        ซึ่งหัวใจหลักคือตอนที่เอาชื่อไฟล์ที่เป็น String ไปหาค่า Resource ID ส่วนตอนเอา Resource ID ไปดึง Drawable มาใช้งานไม่ใช่เรื่องยากอะไร เพราะปกติเวลาเรียกใช้ R.drawable.ic_launcher ก็คือการใส่ Resource ID อยู่แล้ว

        และการหา Resource ID ด้วย String จะใช้คำสั่งดังนี้

public static int getResourceByFilename(Context context, String filename) {
    return context.getResources().getIdentifier(filename, "drawable", context.getPackageName());
}

        Parameter ที่ต้องใช้มีด้วยกันสองตัวคือ Context เพื่อเข้าถึง Resource และ String ที่เป็นชื่อไฟล์ที่ต้องการหา Resource ID

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int resId = getResourceByFilename(this, "ic_launcher");
    }

    public static int getResourceByFilename(Context context, String filename) {
        return context.getResources().getIdentifier(filename, "drawable", context.getPackageName());
    }
}

        อ้าว ได้ Resource ID มาแล้ว ก็เอาไปใช้งานได้เลย

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int resId = getResourceByFilename(this, "ic_launcher");
        ImageView ivAppLogo = (ImageView) findViewById(R.id.iv_app_logo);
        ivAppLogo.setImageResource(resId);
    }

    public static int getResourceByFilename(Context context, String filename) {
        return context.getResources().getIdentifier(filename, "drawable", context.getPackageName());
    }
}

        เพียงเท่านี้ก็สามารถดึง ic_launcher มาแสดงใน ImageView ได้แล้ว~

        และถ้าสังเกตดีๆจะเห็นว่าสามารถเปลี่ยน Resource ที่ต้องการได้ด้วยนะ เช่น Mipmap Resource เป็นต้น เพราะในคำสั่ง getIdentifier มีการกำหนดชื่อ Resource อยู่ด้วย

public static int getResourceByFilename(Context context, String filename) {
    return context.getResources().getIdentifier(filename, "mipmap", context.getPackageName());
}

        เท่านี้ก็ดึง Resource ID จาก Mipmap Resource ได้แล้ว

        ถ้าเขียนให้ครอบคลุมกว่านี้หน่อยก็จะได้ประมาณนี้

public static int getResourceByFilename(Context context, String resourceType, String filename) {
    return context.getResources().getIdentifier(filename, resourceType, context.getPackageName());
}

         แล้วเวลาเรียกใช้งานก็กำหนด Resource ที่ต้องการด้วย

int resId = getResourceByFilename(this, "drawable", "ic_launcher");

        เพียงเท่านี้ก็เอาไปใช้งานได้หลากหลายแล้ว ไม่ว่าจะเป็น Layout Resource, String Resource หรือ Color Resource ก็ตาม