1. 程式人生 > >android 超長圖顯示的幾種方法

android 超長圖顯示的幾種方法

問題:用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.引用第三方庫:

工具類SubsamplingScaleImageView

總結:

2方法在imageview的基礎上進行顯示,可應用於顯示的控制元件必須為ImageView的場景;
3,4的方法原理都是一樣的,都是繼承View,作用於直接顯示圖片,非常方便;