Этот ответ основан на решении, представленном в https://developer.android.com/topic/performance/graphics/load-bitmap.html (без использования внешних библиотек), с некоторыми изменениями, сделанными мной, чтобы сделать его функциональность лучше и практичнее.
Некоторые замечания об этом решении:
Предполагается, что вы хотите сохранить соотношение сторон . Другими словами:
finalWidth / finalHeight == sourceBitmap.getWidth() / sourceBitmap.getWidth()
(независимо от вопросов о кастинге и округлении)
Предполагается, что у вас есть два значения (maxWidth
& maxHeight
), которые вы хотите любое из размеры вашего окончательного растрового изображения не превышают его соответствующего значения . Другими словами:
finalWidth <= maxWidth && finalHeight <= maxHeight
Итак, minRatio
было положено в основу расчетов (см. Реализацию). UNLIKE базовое решение, которое поместило maxRatio
в качестве основы для расчетов в фактическом . Кроме того, расчет inSampleSize
был намного лучше (более логично, кратко и эффективно).
Предполагается, что вы хотите, чтобы (по крайней мере) одно из окончательных измерений имело точно значение соответствующего ему maxValue (каждое было возможно , учитывая вышеизложенные предположения) . Другими словами:
finalWidth == maxWidth || finalHeight == maxHeight
Последний дополнительный шаг по сравнению с базовым решением (Bitmap.createScaledBitmap(...)
) - это ограничение " точно ". Очень важное замечание: Вы не должны делать этот шаг вначале (например, принятый ответ ) из-за значительного потребления памяти в случае больших изображений!
Это для декодирования file
. Вы можете изменить его как базовое решение для декодирования resource
(или всего, что поддерживает BitmapFactory
).
Реализация:
public static Bitmap decodeSampledBitmap(String pathName, int maxWidth, int maxHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
final float wRatio_inv = (float) options.outWidth / maxWidth,
hRatio_inv = (float) options.outHeight / maxHeight; // Working with inverse ratios is more comfortable
final int finalW, finalH, minRatio_inv /* = max{Ratio_inv} */;
if (wRatio_inv > hRatio_inv) {
minRatio_inv = (int) wRatio_inv;
finalW = maxWidth;
finalH = Math.round(options.outHeight / wRatio_inv);
} else {
minRatio_inv = (int) hRatio_inv;
finalH = maxHeight;
finalW = Math.round(options.outWidth / hRatio_inv);
}
options.inSampleSize = pow2Ceil(minRatio_inv); // pow2Ceil: A utility function that comes later
options.inJustDecodeBounds = false; // Decode bitmap with inSampleSize set
return Bitmap.createScaledBitmap(BitmapFactory.decodeFile(pathName, options),
finalW, finalH, true);
}
/**
* @return the largest power of 2 that is smaller than or equal to number.
* WARNING: return {0b1000000...000} for ZERO input.
*/
public static int pow2Ceil(int number) {
return 1 << -(Integer.numberOfLeadingZeros(number) + 1); // is equivalent to:
// return Integer.rotateRight(1, Integer.numberOfLeadingZeros(number) + 1);
}
Пример использования, если у вас есть imageView
с определенным значением для layout_width
(match_parent
или явным значением) и неопределенным значением для layout_height
(wrap_content
), а вместо этого определенное значение для maxHeight
:
imageView.setImageBitmap(decodeSampledBitmap(filePath,
imageView.getWidth(), imageView.getMaxHeight()));