Android 加载超大长图及原理

  • Post author:
  • Post category:其他




Android 加载超大长图及原理

我们在开发中偶尔会遇到加载超大长图,类似于微信n多页聊天记录截图加载。但这类图片一般都会很大,几兆、十几兆、甚至几十兆,很容易造成内存溢出,今天笔者给大家提供一个可以满足此类需求的简单的自定义view。具体原理就是图片缩放,只加载屏幕显示区域图片,内存复用。代码很简单,注视也很相信,可以拿来直接用。

public class BigView extends View implements GestureDetector.OnGestureListener,View.OnTouchListener {
    private  Scroller mScroll;
    //手势
    private  GestureDetector mGestureDetector;
    private BitmapFactory.Options mOptions;
    private int mImageHeight;
    private int mImageWidht;
    //手机屏幕宽高
    private int mViewHeight;
    private int mViewWidth;
    //图片缩放因子
    private float mScale;
    private Rect mRect;
    private Bitmap mBitmap;
    //bitmap解码器
    private BitmapRegionDecoder mDecoder;

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

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

    public BigView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mOptions=new  BitmapFactory.Options();//内存复用
        mGestureDetector=new GestureDetector(context,this);
        mScroll =new Scroller(context);
        mRect=new Rect();
        setOnTouchListener(this);
    }
    public void setImage(InputStream inputStream){

        //只将大图的边框加载到内存
        mOptions.inJustDecodeBounds=true;
        //将图片边宽加载到options
        BitmapFactory.decodeStream(inputStream,null,mOptions);
        mImageHeight=mOptions.outHeight;
        mImageWidht=mOptions.outWidth;
        //设置内存可复用
        mOptions.inMutable=true;
        //设置图片编码格式
        mOptions.inPreferredConfig=Bitmap.Config.RGB_565;
        mOptions.inJustDecodeBounds=false;
        try {
            //解码图片
            mDecoder= BitmapRegionDecoder.newInstance(inputStream,false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //requestLayout方法会导致View的onMeasure、onLayout、onDraw方法被调用;
        requestLayout();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mViewHeight=getMeasuredHeight();
        mViewWidth=getMeasuredWidth();
        mScale=mViewWidth/(float)mImageWidht;
        //等比例缩放后图片所占区域
        mRect.top=0;
        mRect.left=0;
        mRect.right= mImageWidht;
        mRect.bottom= (int) (mViewHeight/mScale);

    }

    @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();
        //将图片缩放到手机屏幕内
        matrix.preScale(mScale,mScale);
        canvas.drawBitmap(mBitmap,matrix,null);
    }
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        //将触摸时间分发给手势
        return mGestureDetector.onTouchEvent(motionEvent);
    }
    @Override
    public boolean onDown(MotionEvent motionEvent) {
        //当按下屏幕时,如果正在滑动,则强制停止滑动
        if(!mScroll.isFinished()){
            mScroll.forceFinished(true);
        }
        return true;
    }
    @Override
    public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
        //滑动时rect偏移量计算
        mRect.offset(0, (int) v1);
        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);
        }
        //调用ondraw方法
        invalidate();
        return false;
    }
    @Override
    public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
        mScroll.fling(0,mRect.top,0,(int)-v1,0,0,0,mImageHeight-(int)(mViewHeight/mScale));
        return false;
    }
    //处理滚动结果
    @Override
    public void computeScroll() {
        if(mScroll.isFinished())return;
        if(mScroll.computeScrollOffset()){
            mRect.top=mScroll.getCurrY();
            mRect.bottom=mScroll.getCurrY()+(int)(mViewHeight/mScale);
            //调用ondraw方法
            invalidate();
        }
        super.computeScroll();
    }

    @Override
    public void onShowPress(MotionEvent motionEvent) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent motionEvent) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent motionEvent) {

    }

}

外部只需要简单调用即可使用 bigView.setImage(inputStream);

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BigView bigView=findViewById(R.id.bigView);
        InputStream inputStream= null;
        try {
            inputStream = getAssets().open("big.jpeg");
            bigView.setImage(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}



版权声明:本文为weixin_43843686原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。