自定义View实现加载巨图

本文介绍如何使用BitmapRegionDecoder避免Android加载大图时出现OOM问题。通过自定义ViewGroup和监听滑动事件,实现了分块加载图片,减少了卡顿,并探讨了布局复用的可能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android 自定义View实现加载巨图

加载巨图用的是:

BitmapRegionDecoder

这个类的实现就是通过Rect 获取图片中的某一块,这样就防止图片过大直接导致OOM。

我先用自定义ImageView实现,然后发现每滑动一次时,重新setImage一次,这样会有些卡顿的感觉。

于是我联想到ViewGroup,通过几个View来加载图片,于是开始行动,不过因为比较忙,所以只是用了偷懒的方式看这个想法能不能实现。下面贴代码:

/**
 * Project Name: Demo0808
 * Describe: 加载超大图片
 * Author: laicl
 * Create Time: 2016/8/11 0011 上午 11:16
 */
public class LargeImageView extends ScrollView {
	
	//ScrollView 的子控件
    private HorizontalScrollView parent;
    
    //HorizontalScrollView 的子控件
    private LinearLayout mLinear;
    
    //BitmapRegionDecoder 获取图片的
    private Rect mRect = new Rect();
    
    private BitmapRegionDecoder mDecoder;
    private Context mContext;
    private int width;
    private int height;

    public LargeImageView(Context context) {
        this(context, null);
    }

    public LargeImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LargeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        //添加一个横向的ScrollView
        parent = new HorizontalScrollView(context);
        addView(parent);
        mLinear = new LinearLayout(context);
        mLinear.setOrientation(LinearLayout.VERTICAL);
        //横向的ScrollView中添加一个垂直的LinearLayout
        parent.addView(mLinear);
        WindowManager wm = (WindowManager) mContext
                .getSystemService(Context.WINDOW_SERVICE);
        width = wm.getDefaultDisplay().getWidth();
        height = wm.getDefaultDisplay().getHeight();
        parent.setHorizontalScrollBarEnabled(false);
        setVerticalScrollBarEnabled(false);
    }
	
	/**
     * 设置图片
     * @param res 资源文件
     */
    public void setImageView(int res){
        initData(res);
    }

    private void initData(int res){
        try {
            InputStream is = mContext.getResources().openRawResource(res);
            mDecoder = BitmapRegionDecoder.newInstance(is, true);
        }catch (Exception e){
            Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT);
            e.printStackTrace();
            return;
        }
        int imageWidth = mDecoder.getWidth();
        int imgHeight = mDecoder.getHeight();
        //如果图片的宽度为0
        if(imageWidth == 0){
            return;
        }
        //如果图片的高度为0
        if(imgHeight == 0){
            return;
        }
        //判断xml中该控件设置的大小
        if(getMeasuredHeight() == 0){
            if(getMeasuredWidth() == 0){
                setMeasuredDimension(width,height);
            }else{
                setMeasuredDimension(getMeasuredWidth(),height);
            }
        }else{
            if(getMeasuredWidth() == 0){
                setMeasuredDimension(width,getMeasuredHeight());
            }
        }
        int wLength = 0;
        int hLength = 0;
        //计算宽度需要的布局数量,根据控件宽度来计算 
        if(imageWidth % getMeasuredWidth() == 0){
            wLength = imageWidth/getMeasuredWidth();
        }else{
            wLength = imageWidth/getMeasuredWidth() + 1;
        }
        //计算高度需要的布局数量,根据控件高度来计算 
        if(imgHeight % getMeasuredHeight() == 0){
            hLength = imgHeight/getMeasuredHeight();
        }else{
            hLength = imgHeight/getMeasuredHeight() + 1;
        }
        //通过计算数量进行布局添加
        for(int i = 0;i<hLength;i++){
            LinearLayout linear = new LinearLayout(mContext);
            linear.setOrientation(LinearLayout.HORIZONTAL);
            for(int j = 0;j<wLength;j++){
                ImageView image = new ImageView(mContext);
                Bitmap bitmap = getBitmap(0 + getMeasuredHeight() * i, 0 + getMeasuredWidth() * j, getMeasuredWidth(), getMeasuredHeight());
                image.setImageBitmap(bitmap);
                RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(bitmap.getWidth(),bitmap.getHeight());
                image.setLayoutParams(layoutParams);
                linear.addView(image);
            }
            mLinear.addView(linear);
        }
    }
	
	//根据传进来的数值获取bitmap
    private Bitmap getBitmap(int top,int left,int w,int h){
        if(left < 0){
            left = 0;
        }
        mRect.left = left;
        mRect.top = top;
        //限制不能超过图片宽度
        if(mRect.left + w > mDecoder.getWidth()){
            mRect.right = mDecoder.getWidth();
        }else{
            mRect.right = mRect.left + w;
        }
        //限制不能超过图片高度
        if(mRect.top + h > mDecoder.getHeight()){
            mRect.bottom = mDecoder.getHeight();
        }else{
            mRect.bottom = mRect.top + h;
        }
        Bitmap bitmap = mDecoder.decodeRegion(mRect, null);
        return bitmap;
    }
}

运行后,发现可行。不过这是偷懒写的,这样可能会因为布局过多出现问题,可以自己集成LinearLayout或者ViewGroup写一下,然后监听滑动事件,通过布局复用,来控制布局数量。

好了,到此为止,效果图就不贴了。等后续我会去尝试一下布局复用,后续再更新。祝大家开心每一天!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值