Android how to create runtime thumbnail

后端 未结 9 983
别那么骄傲
别那么骄傲 2020-11-27 09:59

I have a large sized image. At runtime, I want to read the image from storage and scale it so that its weight and size gets reduced and I can use it as a thumbnail. When a u

9条回答
  •  悲&欢浪女
    2020-11-27 10:51

    This answer is based on the solution presented in https://developer.android.com/topic/performance/graphics/load-bitmap.html (without using of external libraries) with some changes by me to make its functionality better and more practical.

    Some notes about this solution:

    1. It is assumed that you want to keep the aspect ratio. In other words:

      finalWidth / finalHeight == sourceBitmap.getWidth() / sourceBitmap.getWidth() (Regardless of casting and rounding issues)

    2. It is assumed that you have two values (maxWidth & maxHeight) that you want any of the dimensions of your final bitmap doesn't exceed its corresponding value. In other words:

      finalWidth <= maxWidth && finalHeight <= maxHeight

      So minRatio has been placed as the basis of calculations (See the implementation). UNLIKE the basic solution that has placed maxRatio as the basis of calculations in actual. Also, the calculation of inSampleSize has been so much better (more logic, brief and efficient).

    3. It is assumed that you want to (at least) one of the final dimensions has exactly the value of its corresponding maxValue (each one was possible, by considering the above assumptions). In other words:

      finalWidth == maxWidth || finalHeight == maxHeight

      The final additional step in compare to the basic solution (Bitmap.createScaledBitmap(...)) is for this "exactly" constraint. The very important note is you shouldn't take this step at first (like the accepted answer), because of its significant consumption of memory in case of huge images!

    4. It is for decoding a file. You can change it like the basic solution to decode a resource (or everything that BitmapFactory supports).

    The implementation:

    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);
    }
    

    Sample Usage, in case of you have an imageView with a determined value for layout_width (match_parent or a explicit value) and a indeterminate value for layout_height (wrap_content) and instead a determined value for maxHeight:

    imageView.setImageBitmap(decodeSampledBitmap(filePath, 
            imageView.getWidth(), imageView.getMaxHeight()));
    

提交回复
热议问题