1.为什么要二次采样
OK,那么首先我要 解决的一个问题就是为什么我们要二次采样?
不知道大家在开发App的过程中有没有遇到过类似于图片墙这样的功能?在做图片墙的时候你有没有遇到过OOM异常呢?遇到了又是怎么解决的?再比如我现在有一张100M大的图片,我想把这张图片用一个ImageView显示出来,那么你的ImageView能够显示出来这张图片吗?上面我们说的这两种情况其实都涉及到图片加载时内存溢出的问题,内存溢出可能发生在加载一张大图的时候,也有可能发生在加载多张普通小图的时候,如果我们不对图片做二次采样,那么OOM就是一把悬在头上的剑,随时可能会掉下。所以一定要对图片进行二次采样。事实上,我在手机上显示一张分辨率特别大的图片和显示一张分辨率小的图片(不要小的太离谱即可),对用户的视觉体验来说,并不会有多大变化,但是对我们手机的内存来说,影响却是非常巨大的。总而言之,二次采样就是为了避免图片加载时的OOM异常。 2.二次采样分别是哪两次?每次采样的目的是什么
既然是二次采样,那当然要分为两步了,下面我们来说说每次采样的主要工作: 1.第一次采样
第一次采样我主要是想要获得图片的压缩比例,假如说我有一张图片是200*200,那么我想把这张图片的缩略图显示在一个50*50的ImageView上,那我的压缩比例应该为4,那么这个4应该怎么样来获得呢?这就是我们第一步的操作了,我先加载图片的边界到内存中,这个加载操作并不会耗费多少内存,加载到内存之后,我就可以获得这张图片的宽高参数,然后根据图片的宽高,再结合控件的宽高计算出缩放比例。 2.第二次采样
在第一次采样的基础上,我来进行二次采样。二次采样的时候,我把第一次采样后算出来的结果作为一个参数传递给第BitmapFactory,这样在加载图片的时候系统就不会将整张图片加载进来了,而是只会加载该图片的一张缩略图进来,这样不仅提高了加载速率,而且也极大的节省了内存,而且对于用户来说,他也不会有视觉上的差异。 3.代码实现
package com.bawei.test; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.constraint.solver.Cache; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ImageView; import java.io.IOException; import it.sephiroth.android.library.picasso.Picasso; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class MainActivity extends AppCompatActivity { private ImageView image1,image2; private String path="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1631218477,898720467&fm=27&gp=0.jpg"; public static final int IMAGE_MAX_HEIGH=200; public static final int IMAGE_MAX_WIDTH=150; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); image1= (ImageView) findViewById(R.id.image1); image2= (ImageView) findViewById(R.id.image2); Picasso.with(this).load(path).into(image1); Two(); } //二次采样 public void Two(){ OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(path).build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { byte[] bytes = response.body().bytes(); Bitmap bitmap = getBitmapByBytes(bytes); image2.setImageBitmap(bitmap); } }); } //二次图片的处理 public static Bitmap getBitmapByBytes(byte[] bytes){ //对于图片的二次采样,主要得到图片的宽,高 int width=0; int height=0; int sampleSize=1;//默认的缩放1 //图片工厂 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds=true;//仅解码图片的宽高,不加载像素 BitmapFactory.decodeByteArray(bytes,0,bytes.length,options); //得到宽高 height=options.outHeight; width=options.outWidth; //图片的实际宽高,根据默认最大大小值,得到图片的缩放比例 while ((height / sampleSize >IMAGE_MAX_HEIGH) || (width / sampleSize >IMAGE_MAX_WIDTH)) { sampleSize *= 2; } //二次采样开始 //二次采样时我需要将图片加载出来显示,不能只加载图片的框架,因此inJustDecodeBounds属性要设置为false //不再只加载图片实际边缘 options.inJustDecodeBounds = false; //并且制定缩放比例 options.inSampleSize = sampleSize; return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); } }•(了解)计算机图形表示原理 图片:宽 高 图片是由每个像素点来组成 像素点:小方块 图片的大小:宽高每个像素点的大小
•(掌握)从SdCard加载图片显示
public void load(View v){ //从sdcard加载mm Bitmap bitmap=BitmapFactory.decodeFile("/mnt/sdcard/**.jpg"); iv.setIMageBitmap(bitmap); }/*获取图片的宽高,这里要设置Options.inJustDecodeBounds=true,这时候decode的bitmap为null, 只是把图片的宽高放在Options里,然后第二步就是设置合适的压缩比例inSampleSize, 这时候获得合适的Bitmap.*/