如何打造一个大图加载控件

130 阅读1分钟

核心思想

1.Bitmap分区块加载 2.内存复用

构造方法

初始化一些成员变量

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

public MBigView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs,0);
}

public MBigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    // 图片绘制的区域
    mRect = new Rect();
    //Bitmap Options
    mOptions = new BitmapFactory.Options();
    //手势检测器
    mGestureDetector = new GestureDetector(context,this);
    //滚动器
    mScroller = new Scroller(context);
    //缩放手势检测器
    mScaleGestureDetector = new ScaleGestureDetector(context,new ScaleGesture());
    //监听触摸事件
    setOnTouchListener(this);
}

设置图片方法setImage(InputStream),里面做了

1.获取图片宽高 2.开启复用 3.设置图片质量 4.创建区域解码器

public void setImage(InputStream is){

    // 不加载进内存的情况下获取图片的宽和高;
    mOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is,null,mOptions);
    mImageWidth = mOptions.outWidth;
    mImageHeight = mOptions.outHeight;
    mOptions.inJustDecodeBounds = false;

    // 开启复用
    mOptions.inMutable = true;
    // 设置图片格式
    mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
    // 图片由像素点组成;像素点由什么组成;argb 透明通道 红色 绿色 蓝色;
    // 创建区域解码器
    try {
        mDecoder = BitmapRegionDecoder.newInstance(is,false);
    } catch (IOException e) {
        e.printStackTrace();
    }
    requestLayout();
}

onMeasure方法中,确定图片的加载区域,定义缩放因子

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mViewWidth = getMeasuredWidth();
    mViewHeight = getMeasuredHeight();

    // 确定图片的加载区域;
    mRect.left = 0;
    mRect.top = 0;
    mRect.right = Math.min(mImageWidth,mViewWidth);
    Log.e("xxxxxxx",mImageWidth+"  "+mViewWidth);
    mRect.bottom = Math.min(mImageHeight,mViewHeight);

    // 再定义一个缩放因子
    originalScale = mViewWidth/(float)mImageWidth;
    mScale = originalScale;
}

onDraw方法,复用内存,使用区域解码器解码

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if(mDecoder == null){return;}
    // 复用内存
    mOptions.inBitmap = mBitmap;
    //区域解码器,解码区域
    mBitmap = mDecoder.decodeRegion(mRect,mOptions);

    Matrix matrix = new Matrix();
    // 缩放矩阵
    tempScale = mViewWidth/(float)mRect.width();
    Log.e("TAG","left: " + mRect.left + " right: " + mRect.right + "top: " + mRect.top + "bottom: " + mRect.bottom + "tempScale: " + tempScale);
    matrix.setScale(tempScale,tempScale);
    //绘制bitmap
    canvas.drawBitmap(mBitmap,matrix,null);
}

双击放大,改变缩放值大小

@Override
public boolean onDoubleTap(MotionEvent e) {
    // 双击放大图片;
    if(mScale < originalScale*2){
        mScale = originalScale*2;
    }else{
        mScale = originalScale;
    }

    Log.e("xxxxx mScale ","mScale   = "+mScale+"   originalScale = "+originalScale);
    mRect.right = mRect.left+(int)(mViewWidth/mScale);
    mRect.bottom = mRect.top+(int)(mViewHeight/mScale);


    // 移动时,处理到达顶部和底部的情况
    if(mRect.bottom > mImageHeight){
        mRect.bottom = mImageHeight;
        mRect.top = mImageHeight-(int)(mViewHeight/mScale);
    }
    if(mRect.top < 0){
        mRect.top = 0;
        mRect.bottom = (int)(mViewHeight/mScale);
    }
    if(mRect.right > mImageWidth){
        mRect.right = mImageWidth;
        mRect.left = mImageWidth-(int)(mViewWidth/mScale);
    }
    if(mRect.left < 0){
        mRect.left = 0;
        mRect.right = (int)(mViewWidth/mScale);
    }
    Log.e("xxxxx Rect ","mRect.Left   = "+mRect.left+
            "   mRect.right = "+mRect.right+
            "   mRect.top = "+mRect.top+
            "   mRect.bottom = "+mRect.bottom);
    invalidate();
    return false;
}

滚动时注意处理边界条件


@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    //处理区域偏移
    mRect.offset((int)distanceX,(int)distanceY);
    //处理边界条件
    if(mRect.bottom > mImageHeight){
        mRect.bottom = mImageHeight;
        mRect.top = mImageHeight-(int)(mViewHeight/mScale);
    }
    if(mRect.top < 0){
        mRect.top = 0;
        mRect.bottom = (int)(mViewHeight/mScale);
    }
    if(mRect.right > mImageWidth){
        mRect.right = mImageWidth;
        mRect.left = mImageWidth-(int)(mViewWidth/mScale);
    }
    if(mRect.left < 0){
        mRect.left = 0;
        mRect.right = (int)(mViewWidth/mScale);
    }
    invalidate();
    return false;
}