ImageView的子类无法加载图片
在图片预览时,偶现图片无法现实,在查看程序的时候发现Bitmap是实际存在的,但是在ImageView中缺绘制不出来,这个问题困然了我很久,查看代码也查不出原因,再加上是偶现的,查原因时费了不少时间。
先来看看异常:
10-14 09:53:34.902 9003-10023/? W/OpenGLRenderer﹕ Bitmap too large to be uploaded into a texture (1024x9297, max=8192x8192)
10-14 09:53:34.902 9003-10023/? W/OpenGLRenderer﹕ Shape too large to be rendered into a texture (940x8510, max=8192x8192)
10-14 09:53:34.917 9003-10023/? W/OpenGLRenderer﹕ Bitmap too large to be uploaded into a texture (1024x9297, max=8192x8192)
10-14 09:53:34.919 9003-10023/? W/OpenGLRenderer﹕ Bitmap too large to be uploaded into a texture (1024x9297, max=8192x8192)
10-14 09:53:34.919 9003-10023/? W/OpenGLRenderer﹕ Bitmap too large to be uploaded into a texture (1024x9297, max=8192x8192)
其实是因为图片过长的原因,图片可以绘制的最大高度为8192,而我的图片高度为9297,在Canvas绘制时,OpenGL无法绘制出来,所以导致在预览长图时无法显示出来。
修改方式:
1.在异步加载图片时,判断图片的宽高,如果高度超出了底层OpenGL绘制的高度,那就把图片缩小。代码如下:
1 public void loadImage(final MediaItem mediaItem) { 2 if (mBitmap == null && !mDestoryDecodeThread && !mDestory) { 3 mHardWareDecodeFuture = ThreadPool.getInstance().submit(new ThreadPool.Job<Bitmap>() { 4 @Override 5 public Bitmap run(ThreadPool.JobContext jc) { 6 boolean rotated = ((mediaItem.getRotation() / 90) & 1) == 1; 7 int photoWidth = rotated ? mediaItem.getHeight() : mediaItem.getWidth(); 8 int photoHeight = rotated ? mediaItem.getWidth() : mediaItem.getHeight(); 9 final BitmapFactory.Options options = new BitmapFactory.Options(); 10 jc.setCancelListener(new ThreadPool.CancelListener() { 11 @Override 12 public void onCancel() { 13 options.requestCancelDecode(); 14 } 15 }); 16 InputStream is = null; 17 try { 18 Bitmap bitmap = null; 19 Uri uri = mediaItem.getContentUri(); 20 int sampleSize = 1; 21 if (photoHeight > mHeight || photoWidth > mWidth) { 22 final int heightRatio = (int) Math.floor((float) photoHeight / (float) mHeight); 23 final int widthRatio = (int) Math.floor((float) photoWidth / (float) mWidth); 24 sampleSize = heightRatio > widthRatio ? heightRatio : widthRatio; 25 } 26 options.inSampleSize = sampleSize; 27 if (bitmap != null) { 28 options.inBitmap = bitmap; 29 } 30 31 if (uri.toString().startsWith("file://")) { 32 bitmap = BitmapFactory.decodeFile(uri.toString().substring("file://".length(), uri.toString().length()), options); 33 } else { 34 is = getContext().getContentResolver().openInputStream(uri); 35 bitmap = BitmapFactory.decodeStream(is, null, options); 36 } 37 38 /*重点缩小图片大小的代码*/ 39 int maxHeight = EglUtil.getMaxTextureSize(getContext()); 40 if (bitmap.getHeight() > maxHeight) { 41 int w = bitmap.getWidth(); 42 int h = bitmap.getHeight(); 43 while (h > maxHeight) { 44 w = w / 2; 45 h = h / 2; 46 } 47 Bitmap b = Bitmap.createScaledBitmap(bitmap, w, h, true); 48 bitmap.recycle(); 49 bitmap = b; 50 } 51 /**/ 52 53 if (jc.isCancelled() || mDestory || mDestoryDecodeThread) { 54 WeakReference<Bitmap> ref = new WeakReference<Bitmap>(bitmap); 55 return null; 56 } 57 return bitmap; 58 } catch (Exception ex) { 59 } finally { 60 MediaItem.closeSilently(is); 61 } 62 return null; 63 } 64 }, new FutureListener<Bitmap>() { 65 @Override 66 public void onFutureDone(final Future<Bitmap> future) { 67 if (future != null && future.get() != null) { 68 post(new Runnable() { 69 @Override 70 public void run() { 71 mBitmap = future.get(); 72 if (!mStopDrawBigBmp && !mDestoryDecodeThread && !mDestory) { 73 mScreenNailLoaded = true; 74 initBitmapRect(); 75 invalidate(); 76 } else { 77 if (mBitmap != null && !mBitmap.isRecycled()) { 78 mBitmap.recycle(); 79 } 80 } 81 82 83 } 84 }); 85 } 86 } 87 }); 88 } 89 }
2.判断图片是否超过绘制的最高高度。没一台手机,可绘制的最大高度是有可能不一样的,在Android5.0以上的版本中可以获取绘制的最大高度值,代码如下:
1 public static int getMaxTextureSize(Context context){ 2 if(maxTextureSize != -1){ 3 return maxTextureSize; 4 } 5 maxTextureSize = 0; 6 int[] maxSize = new int[1]; 7 try { 8 ConfigurationInfo configurationInfo = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getDeviceConfigurationInfo(); 9 int glesVersion = configurationInfo.reqGlEsVersion; 10 if(Build.VERSION.SDK_INT >= 21) { 11 //configureEGLContext 12 EGLDisplay mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 13 if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 14 throw new IllegalStateException("No EGL14 display"); 15 } 16 int[] version = new int[2]; 17 if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) { 18 throw new IllegalStateException("Cannot initialize EGL14"); 19 } 20 int[] attribList = { 21 EGL14.EGL_RED_SIZE, 8, 22 EGL14.EGL_GREEN_SIZE, 8, 23 EGL14.EGL_BLUE_SIZE, 8, 24 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, 25 //EGL_RECORDABLE_ANDROID, 1, 26 EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT, 27 EGL14.EGL_NONE 28 }; 29 EGLConfig[] configs = new EGLConfig[1]; 30 int[] numConfigs = new int[1]; 31 EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0, 32 configs.length, numConfigs, /*offset*/ 0); 33 if (EGL14.eglGetError() != EGL14.EGL_SUCCESS) { 34 throw new IllegalStateException("eglCreateContext RGB888+recordable ES2" + ": EGL error: 0x" + Integer.toHexString(EGL14.eglGetError())); 35 } 36 int[] attrib_list = { 37 EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 38 EGL14.EGL_NONE 39 }; 40 EGLContext mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, 41 attrib_list, /*offset*/ 0); 42 if (EGL14.eglGetError() != EGL14.EGL_SUCCESS) { 43 throw new IllegalStateException("eglCreateContext" + ": EGL error: 0x" + Integer.toHexString(EGL14.eglGetError())); 44 } 45 if (mEGLContext == EGL14.EGL_NO_CONTEXT) { 46 throw new IllegalStateException("No EGLContext could be made"); 47 } 48 int[] surfaceAttribs = { 49 EGL14.EGL_WIDTH, 64, 50 EGL14.EGL_HEIGHT, 64, 51 EGL14.EGL_NONE 52 }; 53 EGLSurface surface = EGL14.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs, 0); 54 EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext); 55 //getMaxTextureSize 56 if(glesVersion >= 0x20000) { 57 GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0); 58 }else if(glesVersion >= 0x10000) { 59 GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0); 60 } 61 //releaseEGLContext 62 EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); 63 EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); 64 EGL14.eglReleaseThread(); 65 EGL14.eglTerminate(mEGLDisplay); 66 }else { 67 if(glesVersion >= 0x20000) { 68 GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0); 69 }else if(glesVersion >= 0x10000) { 70 GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0); 71 } 72 } 73 } catch (IllegalStateException e) { 74 e.printStackTrace(); 75 } 76 maxTextureSize = maxSize[0] > 0 ? maxSize[0] : DEFAULT_MAX_BITMAP_DIMENSION; 77 return maxTextureSize; 78 }
注意:以上获取最大高度值是在android5.0以上的版本才行。android5.0以下的版本使用:
private static final int DEFAULT_MAX_BITMAP_DIMENSION = 8196;
这样就能保证不管图片有多长,都能显示出来了。