Android超长图片展示

安卓中图片一般有两种加载方式: ImageView和图片框架。普通的固定资源图片用ImageView就行;网络图片或者列表中的图片,UIL、Fresco、Picasso等框架也能解决下载、缓存管理、滑动优化这些基本问题。除了一些特殊情况,比如超长图片的完整展示,实际情况就是一些做成图片形式的长图文:比如宽度1000、高度10000的长图。

安卓底层的显示是由OpenGL ES支持的,渲染的图片有4096*4096的大小限制,因此ImageView的任何一边不能超过 4096 个 像素。当上面这张长图放进ImageView中,是显示不出来的,log里会警告:

W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (1000x10000, max=4096x4096)

用图片框架也一样,因为后者也是对ImageView的包装。

那么,只能使用BitmapRegionDecoder将图片切分了(BitmapRegionDecoder基本用法这里不再赘述),将长图切分成一条条,每条的高度控制在4096以内,分别放进一个ImageView中,这样暂时能显示了。不过还是有问题,这几个ImageView得放进一个能滑动的控件中才能正常显示。如果放进ScrollView中,这些图片轻松占几十M内存,小内存手机很容易OOM;如果放进ListView/RecyclerView中,内存是能在滑动时及时回收了,这样代码就复杂一些了,如果这张图外面还有个列表什么的,就更麻烦了。再考虑到图片可能来自网络地址,显示前又得加上下载、本地缓存等处理。

有没有一个办法能解决以上所有问题,能加载超长图片、不会OOM、不需要下载、代码简单、轻松嵌入到页面中?当然是有的,就是使用原生控件WebView加载,其实想到就简单了。

WebView本质是一个Chromium内核的浏览器,用来加载网页,既然能展示网页H5,展示一张图片也就不在话下。

使用方法如下:
在layout中放一个WebView控件,将图片地址imageUrl用html包裹起来,再使用webView.loadData()方法加载图片。


String imageUrl = "http://att.bbs.duowan.com/forum/201405/22/154840fovfma9ayappk4fp.jpg";
String html = "" +
                "" +
                "     " +
                "+imageUrl+"\" width=\""+screenWidthDp+"\"/>" +
                "" +
                "";

WebView webView = (WebView) findViewById(R.id.web_view);
webView.loadData(html, "text/html", "utf-8");

上面用String来表示html,便于在代码中动态修改,比较灵活。也可以将html写好放到assets中读取,那样可读性好一些。

html的img标签中除了动态设置src图片地址外,还动态设置了width图片宽度,这里的宽度screenWidthDp传的是屏幕的宽度的dp值,需要获取屏幕宽度px值,转换成dp传进去。之所以这么做,是为了合理缩放图片以适应屏幕宽度。也有其他适应屏幕的方法,比如:

WebSettings settings = webView.getSettings(); 
settings.setUseWideViewPort(true); 
settings.setLoadWithOverviewMode(true); 

但是测试中发现这种方法在某些机型上会随机失效,比如小米3。因此在html中动态设置是最稳妥的方式。

图片经过缩放完美展示了,但有时会有这样的需求——获取图片的原始宽度,以计算图片的缩放倍数。图片是直接在WebView中以网页的形式缩放加载的,WebView并没有提供原始宽度的获取方法,那么只能在网页里想办法了。html中图片img的naturalWidth属性代表原始宽度,要把这个html中的数值传到java里,就需要使用JS接口回调了,方法也简单,完整代码如下

    @SuppressLint("JavascriptInterface")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String imageUrl = "http://att.bbs.duowan.com/forum/201405/22/154840fovfma9ayappk4fp.jpg";

        int screenWidthDp =  px2dp(this, getScreenWidth(this));

        String html = "" +
                "" +
                "     " +
                "+imageUrl+"\" width=\""+screenWidthDp+"\"/>" +
                "" +
                "" +
                "";

        WebView webView = (WebView) findViewById(R.id.web_view);

        webView.setWebViewClient(new WebViewClient(){
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:getsize()");
            }
        });

        webView.getSettings().setJavaScriptEnabled(true);
        webView.addJavascriptInterface(new Object() {
            @JavascriptInterface
            public void getNaturalSize(int imgWidth, int imgHeight) {
                Log.i("image size","width: "+imgWidth+" height: "+imgHeight);
            }
        },"stub");

        webView.loadData(html, "text/html", "utf-8");
    }

    // 获取屏幕宽度
    public static int getScreenWidth(Context context) {
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        return dm.widthPixels;
    }

    // 将px转换成dp值
    public static int px2dp(Context context, int px) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (px / scale + 0.5f);
    }

首先在html中添加一个JS方法getsize(),在方法里获取图片原始宽度img.naturalWidth;

然后使用webView.setWebViewClient监听页面加载完成,完成后使用view.loadUrl(“javascript:getsize()”)调用JS的getsize()方法;

接着使用webView.addJavascriptInterface,添加接口stub和接口方法getNaturalSize()供JS调用;

最后在JS的getsize()方法中调用Java中getNaturalSize()方法,并通过方法的参数将原始宽度传到Java中。

一句话总结就是Java调用JS方法,在JS方法里又调用Java方法,数值都是通过方法的参数传递。这就是Java跟html/JS的交互过程。

你可能感兴趣的:(Android超长图片展示)