问题:用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,作用于直接显示图片,非常方便;