android 超長圖顯示的幾種方法
阿新 • • 發佈:2018-12-11
問題:用ImageView控制元件載入長圖的時候會遇到這樣的一個問題,同一張長圖在有些機型可以正常顯示,但是在部分機型確顯示不了。
原因:當APP開啟硬體加速的時候,GPU對於openglRender 渲染有一個限制值,超過了這個限制值,就無法渲染,不同的手機會有不同的限制值;
j針對這一問題,統計了一下幾種解決方法:
1.關閉硬體加速:
在清單檔案AndroidManifest.xml中設定:
<application android:hardwareAccelerated="false" >
或者在對應的View中設定:
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
注:這樣的確解決了圖片載入問題,但你會在app執行的時候,發現app變得十分卡頓,影響體驗,不推薦使用。
2.通過獲取手機openglRender 的限制值,判斷超過這個限制的話,對圖片進行壓縮:
<1.獲取限制值
/** * 獲取手機顯示最長圖片的限制值 * @return */ public static int getOpenglRenderLimitValue() { int maxsize; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { maxsize = getOpenglRenderLimitEqualAboveLollipop(); } else { maxsize = getOpenglRenderLimitBelowLollipop(); } return maxsize == 0 ? 4096 : maxsize; } private static int getOpenglRenderLimitBelowLollipop() { int[] maxSize = new int[1]; GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0); return maxSize[0]; } private static int getOpenglRenderLimitEqualAboveLollipop() { EGL10 egl = (EGL10) EGLContext.getEGL(); EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] vers = new int[2]; egl.eglInitialize(dpy, vers); int[] configAttr = { EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER, EGL10.EGL_LEVEL, 0, EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] numConfig = new int[1]; egl.eglChooseConfig(dpy, configAttr, configs, 1, numConfig); if (numConfig[0] == 0) {// TROUBLE! No config found. } EGLConfig config = configs[0]; int[] surfAttr = { EGL10.EGL_WIDTH, 64, EGL10.EGL_HEIGHT, 64, EGL10.EGL_NONE }; EGLSurface surf = egl.eglCreatePbufferSurface(dpy, config, surfAttr); final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;// missing in EGL10 int[] ctxAttrib = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL10.EGL_NONE }; EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, ctxAttrib); egl.eglMakeCurrent(dpy, surf, surf, ctx); int[] maxSize = new int[1]; GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0); egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl.eglDestroySurface(dpy, surf); egl.eglDestroyContext(dpy, ctx); egl.eglTerminate(dpy); return maxSize[0]; }
<2.通過Glide載入:
Glide.with(context).load(path).into(new SimpleTarget<GlideDrawable>() { @Override public void onLoadStarted(Drawable placeholder) { super.onLoadStarted(placeholder); } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); } @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) { dismissLoading(); //獲取圖片的寬高 int height = resource.getIntrinsicHeight(); int width = resource.getIntrinsicWidth(); //與限制值進行判斷,重新設定寬高 if (height > getOpenglRenderLimitValue()) { width = width * getOpenglRenderLimitValue() / height; height = getOpenglRenderLimitValue(); //圖片轉bitmap Bitmap bitmap = ImageUtil.drawable2Bitmap(resource); //根據新的寬高比進行圖片壓縮 Bitmap result = ImageUtil.mixCompress(context,bitmap, null,height,width); if (result != null) { view.setImageBitmap(result); } }else { //小於限制值,則直接顯示 view.setImageDrawable(resource); } } });
<3.Drawable轉Bitmao
// Drawable轉換成Bitmap
public static Bitmap drawable2Bitmap(Drawable drawable) {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
}
<4.圖片壓縮
/**
* 混合終極方法(尺寸、質量、JNI壓縮)
*
* @param image bitmap物件
* @param filePath 要儲存的指定目錄
* @Description: 通過JNI圖片壓縮把Bitmap儲存到指定目錄
*/
public static Bitmap mixCompress(Context context,Bitmap image, String filePath,int Height,int Width ) {
// 最大圖片大小 1000KB
int maxSize = 1000;
// 獲取尺寸壓縮倍數
int ratio = getRatioSize(context,Width, Height);
// 壓縮Bitmap到對應尺寸
Bitmap result = Bitmap.createBitmap(Width / ratio, Height / ratio, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, Width/ ratio, Height / ratio);
canvas.drawBitmap(image, null, rect, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 質量壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中
int quality = 100;
result.compress(Bitmap.CompressFormat.JPEG, quality, baos);
// 迴圈判斷如果壓縮後圖片是否大於最大值,大於繼續壓縮
while (baos.toByteArray().length / 1024 > maxSize) {
// 重置baos即清空baos
baos.reset();
// 每次都減少10
quality -= 10;
// 這裡壓縮options%,把壓縮後的資料存放到baos中
result.compress(Bitmap.CompressFormat.JPEG, quality, baos);
}
return result;
}
/**
* 計算縮放比
*
* @param bitWidth 當前圖片寬度
* @param bitHeight 當前圖片高度
* @return
* @Description:函式描述
*/
public static int getRatioSize(Context context,int bitWidth, int bitHeight) {
// 圖片最大解析度
int imageHeight =ImageViewerUtil.getOpenglRenderLimitValue();
// 縮放比
int ratio = 1;
// 縮放比,由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
if (bitWidth > bitHeight && bitWidth > imageHeight) {
// 如果圖片寬度比高度大,以寬度為基準
ratio = bitWidth / imageHeight;
} else if (bitWidth < bitHeight && bitHeight > imageHeight) {
// 如果圖片高度比寬度大,以高度為基準
ratio = bitHeight / imageHeight;
}
// 最小比率為1
if (ratio <= 0)
ratio = 1;
return ratio;
}
通過以上步驟,就可以正常顯示了;
3.通過自定義View:
參考鴻洋大神的Android 高清載入巨圖方案 拒絕壓縮圖片
4.引用第三方庫:
總結:
2方法在imageview的基礎上進行顯示,可應用於顯示的控制元件必須為ImageView的場景;
3,4的方法原理都是一樣的,都是繼承View,作用於直接顯示圖片,非常方便;