หมายเหตุ: มีไลบรารีหลายแห่งที่ตามมา แนวทางปฏิบัติที่ดีที่สุดในการโหลดรูปภาพ คุณใช้ไลบรารีเหล่านี้ในแอปเพื่อทำสิ่งต่อไปนี้ได้ โหลดรูปภาพในลักษณะที่เหมาะสมที่สุด เราขอแนะนำให้ แบบเลื่อนผ่าน ซึ่งจะโหลดและแสดงภาพอย่างรวดเร็วและราบรื่นที่สุดเท่าที่จะเป็นไปได้ ไลบรารีการโหลดรูปภาพยอดนิยมอื่นๆ ได้แก่ Picasso จาก Square, Cil จาก Instacart และ เฟรสโก จาก Facebook ไลบรารีเหล่านี้ช่วยลดความซับซ้อนของงานที่ซับซ้อนส่วนใหญ่ที่เกี่ยวข้อง กับบิตแมปและรูปภาพประเภทอื่นๆ บน Android
มีรูปภาพได้หลายขนาดและรูปทรง ในหลายกรณี มักมีปริมาณมากกว่าที่มักต้องใช้ อินเทอร์เฟซผู้ใช้ของแอปพลิเคชัน (UI) ตัวอย่างเช่น แอปพลิเคชันแกลเลอรีของระบบจะแสดงภาพที่ถ่าย โดยใช้กล้องของอุปกรณ์ Android ซึ่งโดยทั่วไปจะมีความละเอียดสูงกว่าหน้าจอมาก ความหนาแน่นของอุปกรณ์
เนื่องจากคุณทำงานกับหน่วยความจำที่จำกัด คุณควรโหลดแค่ความละเอียดที่ต่ำลงเท่านั้น ในหน่วยความจำ เวอร์ชันความละเอียดต่ำกว่าควรตรงกับขนาดของคอมโพเนนต์ UI ที่ แสดงสิ่งนั้น รูปภาพที่มีความละเอียดสูงกว่าจะไม่มีประโยชน์ใดๆ แต่ยังคงถ่าย สร้างหน่วยความจำที่มีค่า และทำให้มีค่าใช้จ่ายในการปฏิบัติงานเพิ่มขึ้นเนื่องจากเพิ่มขึ้นอย่างต่อเนื่อง การปรับขนาด
บทเรียนนี้จะแนะนำวิธีถอดรหัสบิตแมปขนาดใหญ่โดยไม่มีค่าใช้จ่ายเกินต่อแอปพลิเคชัน ขีดจำกัดหน่วยความจำโดยการโหลดเวอร์ชันตัวอย่างย่อยที่เล็กลงในหน่วยความจำ
อ่านมิติข้อมูลและประเภทบิตแมป
คลาส BitmapFactory
มีวิธีถอดรหัสหลายวิธี (decodeByteArray()
, decodeFile()
, decodeResource()
ฯลฯ) สำหรับการสร้าง Bitmap
จากแหล่งที่มาต่างๆ เลือก
วิธีการถอดรหัสที่เหมาะสมที่สุดตามแหล่งข้อมูลของรูปภาพ วิธีการเหล่านี้จะพยายาม
จัดสรรหน่วยความจำสำหรับบิตแมปที่สร้างขึ้น จึงอาจทำให้เกิด OutOfMemory
ได้อย่างง่ายดาย
ข้อยกเว้น วิธีการถอดรหัสแต่ละประเภทมีลายเซ็นเพิ่มเติมที่ช่วยให้คุณระบุการถอดรหัสได้
ตัวเลือกผ่านชั้นเรียน BitmapFactory.Options
การตั้งค่าพร็อพเพอร์ตี้ inJustDecodeBounds
เป็น true
ขณะถอดรหัส
หลีกเลี่ยงการจัดสรรหน่วยความจำ โดยแสดงผล null
สำหรับออบเจ็กต์บิตแมป แต่ตั้งค่า outWidth
, outHeight
และ outMimeType
เทคนิคนี้จะช่วยให้คุณอ่าน
ขนาดและประเภทของข้อมูลรูปภาพก่อนการสร้าง (และการจัดสรรหน่วยความจำ)
บิตแมป
Kotlin
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } BitmapFactory.decodeResource(resources, R.id.myimage, options) val imageHeight: Int = options.outHeight val imageWidth: Int = options.outWidth val imageType: String = options.outMimeType
Java
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
เพื่อหลีกเลี่ยงข้อยกเว้น java.lang.OutOfMemory
โปรดตรวจสอบขนาดของบิตแมปก่อน
ถอดรหัสไฟล์ เว้นแต่คุณจะไว้ใจให้แหล่งที่มาให้ข้อมูลภาพที่มีขนาดตามที่คาดหมายได้
ที่สามารถพอดีกับหน่วยความจำที่มีอยู่ได้
โหลดเวอร์ชันที่ลดขนาดลงในหน่วยความจำ
เมื่อทราบขนาดรูปภาพแล้ว คุณสามารถนําไปใช้ตัดสินใจได้ว่าควรใช้รูปภาพขนาดเต็มหรือไม่ โหลดลงในหน่วยความจำหรือหากควรโหลดเวอร์ชันตัวอย่างย่อยแทน ปัจจัยบางส่วนที่จะมีดังนี้ พิจารณา
- การใช้งานหน่วยความจำโดยประมาณเมื่อโหลดรูปภาพขนาดเต็มในหน่วยความจำ
- จำนวนหน่วยความจำที่คุณยินดีจ่ายเพื่อโหลดรูปภาพนี้เมื่อพิจารณาจากหน่วยความจำอื่นๆ ข้อกำหนดของการสมัคร
- ขนาดคอมโพเนนต์
ImageView
หรือคอมโพเนนต์ UI เป้าหมายที่รูปภาพ จะมีการโหลดเข้าไป - ขนาดและความหนาแน่นของหน้าจออุปกรณ์ปัจจุบัน
ตัวอย่างเช่น การโหลดรูปภาพขนาด 1024x768 พิกเซลลงในหน่วยความจำก็ไม่คุ้มถ้าสุดท้ายแล้วภาพนั้นจะ
ที่แสดงในภาพขนาดย่อ 128x96 พิกเซลใน ImageView
หากต้องการบอกให้ตัวถอดรหัสสุ่มตัวอย่างรูปภาพย่อย ให้โหลดเวอร์ชันขนาดเล็กลงในหน่วยความจำ ตั้งค่า inSampleSize
เป็น true
ในออบเจ็กต์ BitmapFactory.Options
ตัวอย่างเช่น รูปภาพที่มีความละเอียด 2048x1536
ถูกถอดรหัสด้วย inSampleSize
ของ 4 จะสร้าง
ขนาดประมาณ 512x384 การโหลดนี้ลงในหน่วยความจำจะใช้หน่วยความจำเต็ม 0.75 MB แทนที่จะเป็น 12MB
รูปภาพ (สมมติว่ามีการกำหนดค่าบิตแมปของ ARGB_8888
) นี่คือ
วิธีคำนวณค่าขนาดตัวอย่างที่ยกกำลัง 2 ตามความกว้างเป้าหมายและ
ส่วนสูง:
Kotlin
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { // Raw height and width of image val (height: Int, width: Int) = options.run { outHeight to outWidth } var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val halfHeight: Int = height / 2 val halfWidth: Int = width / 2 // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { inSampleSize *= 2 } } return inSampleSize }
Java
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
หมายเหตุ: ระบบจะคํานวณเลข 2 ค่าเนื่องจากตัวถอดรหัสใช้
ค่าสุดท้ายโดยการปัดเศษลงยกกำลัง 2 ที่ใกล้เคียงที่สุดตามเอกสารประกอบ inSampleSize
หากต้องการใช้วิธีนี้ ให้ถอดรหัสด้วยการตั้งค่า inJustDecodeBounds
เป็น true
ก่อน แล้วจึงส่งต่อตัวเลือก
จากนั้นถอดรหัสอีกครั้งโดยใช้ค่า inSampleSize
ใหม่และตั้งค่า inJustDecodeBounds
เป็น false
Kotlin
fun decodeSampledBitmapFromResource( res: Resources, resId: Int, reqWidth: Int, reqHeight: Int ): Bitmap { // First decode with inJustDecodeBounds=true to check dimensions return BitmapFactory.Options().run { inJustDecodeBounds = true BitmapFactory.decodeResource(res, resId, this) // Calculate inSampleSize inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight) // Decode bitmap with inSampleSize set inJustDecodeBounds = false BitmapFactory.decodeResource(res, resId, this) } }
Java
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
วิธีนี้ช่วยให้ง่ายต่อการโหลดบิตแมปขนาดใหญ่ที่กำหนดเองลงใน ImageView
ซึ่งแสดงภาพขนาดย่อขนาด 100x100 พิกเซล ดังที่แสดงในตัวอย่างต่อไปนี้
รหัส:
Kotlin
imageView.setImageBitmap( decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100) )
Java
imageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
คุณสามารถทำตามขั้นตอนที่คล้ายกันนี้เพื่อถอดรหัสบิตแมปจากแหล่งที่มาอื่นๆ โดยแทนที่
เมธอด BitmapFactory.decode*
ที่เหมาะสมได้ตามต้องการ