android给每个应用程序分配的内存是16MB,从网上下了个图片



高清大图,大小是500多kb,加载在listview的imageview里,layout_width和layout_height设置为60p。启动程序直接omm错误退出。

       要想在listview里加载这样的图片,只能进行图片压缩了。但是怎么压缩呢?

andriod提供了一些方法如下:
压缩图片质量:   
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos);   
其中的quality为0~100, 可以压缩图片质量, 不过对于大图必须对图片resize   

这个是等比例缩放:
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);

这个是截取图片某部分:
bitmap = Bitmap.createBitmap(bitmap, x, y, width, height);

但是这些方法在压缩图片之前先要把图片加载到内存中,所以不符合要求。


网上的常用的方法如下:

options为产生图片的编码设置

options.inJustDecodeBounds = true时,调用BitmapFactory.decodeResource()方法,将不会将图片加载到内存里去,却可以得到图片的信息(保存在options里)

这时我们如果我们希望图片宽100,高100,图片实际宽1000,实际高1000,那么我们设置options.inSampleSize = 1000/100(图片的缩放比例),设置options.inJustDecodeBounds = false();(下次调用BitmapFactory.decodeResource()将占用内存)

再次调用BitmapFactory.decodeResource(),将会产生按你设置的比例进行压缩的bitmap


*/
public class BitmapCompressTools {
    public static Bitmap decodeSampleBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight){
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res,resId,options);
        options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res,resId,options);
    }

    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
         final int height = options.outHeight;
         final int width = options.outWidth;
        int inSampleSize = 1;

        if(height > reqHeight|| width > reqWidth){
           final int halfheight =  height /2;
            final int halfwidth = width / 2;
            while(halfheight/inSampleSize >reqHeight && halfwidth/inSampleSize > reqWidth){
                inSampleSize *= 2;
            }
        }
        return  inSampleSize;
    }


}





滑动时不加载图片:

在网上看的一段程序,看不懂,却运行的通。

在adapter的getview()(第一次显示,页面刷新,或者当adapter对应的数据源变化时候会主动调用,notifyDataSetChanged等方法主动刷新界面时被调用,也就是当listview需要显示item的时候就会调用getview())里的代码:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    View v;
    if (convertView == null) {
        v = getLayoutInflater().inflate(R.layout.list_item, null);
        v.setTag(v);
    } else {
        v = (View) convertView.getTag();
        ImageView iv = (ImageView) v.findViewById(R.id.img);
        iv.setImageResource(R.drawable.ic_launcher);
    }
    ImageView iv = (ImageView) v.findViewById(R.id.img);
    // 给图片控件设置上对应的位置编号
    iv.setTag(position);
    if (!isInit) {
        iv.setImageResource(R.drawable.lable);
    }

    return v;
}

刚进来时它会调用if(convertView == null)初始化v,但v.setTag(v)有什么用?   下面isinit初始值是false,所以imageview会初始化为R.drawable.lable

当滑动listview时: 在下面滑动监听器中改变isInit的值,以上getView()将运行covertview!=null的分支,设置iv为R.drawable.ic_launcher,由于isInit为true,不会运行iv.setImageResource(R.drawable.lable)

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        isInit = true;
        switch (scrollState) {
            case OnScrollListener.SCROLL_STATE_IDLE:// 滑动停止
                for (; start_index < end_index; start_index++) {
                    ImageView img = (ImageView) list.findViewWithTag(start_index);
                    bitmap = BitmapCompressTools.decodeSampleBitmapFromResource(getResources(),R.drawable.da,60,60);
                    img.setImageBitmap(bitmap);
                }
                break;

            default:
                break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub
        // 设置当前屏幕显示的起始index和结束index
        start_index = firstVisibleItem;
        end_index = firstVisibleItem + visibleItemCount;

    }
});

当滑动停止时,会运行以上代码加载压缩后的图片

我的两个问题是:convertView没给它赋过值,为什么会进入convertview!=null的语句,以及v.setTag(v)有什么用?