美文网首页小技巧
通过BitmapRegionDecoder实现高清长图的加载

通过BitmapRegionDecoder实现高清长图的加载

作者: JustCode | 来源:发表于2017-07-28 22:10 被阅读53次

前言

在商城这类项目中,肯定是有在商品详情页显示长图的需求,当然用h5那就另说了。如果直接使用BitmapFactory.decodeXXX,然后往ImageView上面setImageBitmap,那么程序也就直接抛给你个OOM。通过一波百度 or google,找到了BitmapRegionDecoder这个类,并且实现了此功能。

正题

BitmapRegionDecoder处理图像的原理是:给定一个长方形范围(Rect),然后通过decodeRegion方法来显示此范围。因此要显示长图片,那就需要将长图分割成多个Rect,然后依次有序得显示。因此BitmapRegionDecoder实际上是起到图像分割的作用,因此类似截图拼图的功能也是可以利用它来进行实现的。

  • 实现思路:商品的详情页,宽度上固定不变的,也就是屏幕的宽度。为了可以让图像的x轴方向完整并且清晰得显示在整个屏幕上,除了需要美工的支持之外,我们还是需要就图像进行一定的压缩的,毕竟android的分辨率有这么多种。因此第一步就是先压缩图片,然后通过BitmapRegionDecoder将图片在y轴上进行分割,最后通过一个RecyclerView来显示这个图像列表。还可以添加图像的缓存功能,不过暂时还没有实现这个功能。

  • 直接上代码:

    public class RecycleView4BigPicActivity extends AppCompatActivity {
           private static final String TAG = "RecycleView4BigPicActiv";
           private RecyclerView mRecyclerView;
           private ImageAdapter mImageAdapter;
           private SparseArray<Bitmap> mBitmapList = new SparseArray<>();
          @Override
          protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                 setContentView(R.layout.activity_recycle_view_big_pic);
    
                mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
                LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
                mRecyclerView.setLayoutManager(linearLayoutManager);
                mRecyclerView.setHasFixedSize(true); // 图片高度是固定的,通过此属性来提高性能
                mRecyclerView.setAdapter(mImageAdapter = new ImageAdapter());
    
                loadPic();
          }
         // 加载图片
         private void loadPic() {
                 InputStream is = null;
                 String picName = "bigpic.png";
                 BitmapRegionDecoder regionDecoder;
                 try {
                         is = getAssets().open(picName);
                         regionDecoder = BitmapRegionDecoder.newInstance(is, false);
    
                         // 获取图片的真是宽高
                        final int width = regionDecoder.getWidth();
                        final int height = regionDecoder.getHeight();
    
                        BitmapFactory.Options options = new BitmapFactory.Options();
                        options.inSampleSize = calculateInSampleSize(width);
    
                        // 期望item的高度值
                        // 这边只考虑了长图,也就是高度比宽度大很多
                        final int desiredItemHeight = width;
    
                        // 余数
                        final int remainder = height % desiredItemHeight;
    
                        // 切割后图片数量,也就是item的数量
                        final int bmpCount = height / desiredItemHeight;
    
                        for (int i = 0; i < bmpCount; i++) {
                                  Rect rect;
    
                                 // 如果总长度能够整除期望item高度,那么每块bitmap的高度相同 = desiredItemHeight
                                 // 如果两者不能整除,那么最后一块bitmap的高度 = desiredItemHeight + remainder(余数)
    
                                if (i == bmpCount - 1) {
                                     rect = new Rect(0, desiredItemHeight * i, width, desiredItemHeight * (i + 1) + remainder);
                                } else {
                                     rect = new Rect(0, desiredItemHeight * i, width, desiredItemHeight * (i + 1));
                                }
    
                               Bitmap bitmap = regionDecoder.decodeRegion(rect, options);
                               mBitmapList.append(i, bitmap);
                     }
                     mImageAdapter.setData(mBitmapList);
                    } catch (IOException e) {
                          e.printStackTrace();
                    } finally {
                           if (is != null) {
                                 try {
                                       is.close();
                                     } catch (IOException e) {
                                         e.printStackTrace();
                                    }
                          }
                   }
           }
           // 计算采样率
           private int calculateInSampleSize(int actualWidth) {
                   // 获取手机屏幕的分辨率
                   DisplayMetrics displayMetrics = new DisplayMetrics();
                   getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
                   final int screenWidth = displayMetrics.widthPixels;
    
                   // 图片在手机上是全屏显示的,因此宽度是固定的,根据屏幕宽度跟图片实际宽度来计算采样率(inSampleSize)
                   final double ratio = actualWidth / screenWidth;
    
                   return (int) (ratio + 1);
           }
    
           @Override
           protected void onDestroy() {
                     super.onDestroy();
                     int size = 0;
                     if (mBitmapList != null && (size = mBitmapList.size()) > 0) {
                             for (int i = 0; i < size; i++) {
                                      Bitmap b = mBitmapList.get(i);
                                      if (b != null) {
                                              // recycle之后,虚拟机gc的时候就会回收这部分内容
                                              b.recycle();
                                      }
                              }
                     }
            }
    }
    

BitmapRegionDecoder是通过newInstance()方法进行创建的,这边使用的是:

itmapRegionDecoder newInstance(InputStream is, boolean isShareable)

  • is:很好理解,也就是图片输入流
  • isShareable:比较难以理解 —— 如果这是true,那么BitmapRegionDecoder可能会保持一个浅复制输入。如果是false,然后BitmapRegionDecoder将显式地创建一个副本输入数据,并保留。 即使是true,仍然可能是深复制输入数据。 如果图像被逐行编码,true可能会降低解码速度。

还有一点需要注意的是,BitmapRegionDecoder最大只能显示4096 * 4096 尺寸的图像Rect,如果超过此大小将不会显示。

相关文章

网友评论

    本文标题:通过BitmapRegionDecoder实现高清长图的加载

    本文链接:https://www.haomeiwen.com/subject/xtjflxtx.html