美文网首页
加载长图

加载长图

作者: 追寻米K | 来源:发表于2019-03-06 19:25 被阅读0次

    显示一个长图片不能一次全部加载,不然很容易OOM,需要一点一点的加载并显示。
    BitmapRegionDecoder类可以只显示一个矩形区域的图像,而不是完整的图片。

            mDecoder = BitmapRegionDecoder.newInstance(is, false);
            mBitmap = mDecoder.decodeRegion(mRect, mOptions);
    

    mRect就是一个Rect对象,这样我们只能看见一个大图片的一个矩形区域。
    我们这里图片显示的区域为整个屏幕,图片长度大于屏幕高度,宽度有可能比屏幕宽,也有可能比屏幕窄,所以对图片需要做缩放。

            //使用矩阵对图片进行缩放
            Matrix matrix = new Matrix();
            matrix.setScale(mScale, mScale);
    

    这样我们还只能显示一部分图像,需要根据手势的滑动动态调整Rect的top和bottom来控制显示的区域,

            //手势
            mGestureDetector = new GestureDetector(context, this);
    

    这些还不够,手势滑动完我们需要有一个惯性的滑动,在惯性滑动的时候也要图片跟着显示,这里使用Scroller来计算惯性滑动的距离

            mScroller = new Scroller(context);
    

    完整的代码:
    自定义View

    public class BigView extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {
    
        private Rect mRect;
        private BitmapFactory.Options mOptions;
        private int mImageHeight;
        private int mImageWidth;
        private BitmapRegionDecoder mDecoder;
        private int mViewWidth;
        private int mViewHeight;
        private float mScale;
        private Bitmap mBitmap;
        private GestureDetector mGestureDetector;
        private Scroller mScroller;
    
        public BigView(Context context) {
            this(context, null, 0);
        }
    
        public BigView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            //指定图片加载的区域
            mRect = new Rect();
            //解码图片的配置
            mOptions = new BitmapFactory.Options();
            //手势
            mGestureDetector = new GestureDetector(context, this);
            setOnTouchListener(this);
            mScroller = new Scroller(context);
        }
    
        /**
         * 输入一张图片的输入流
         *
         * @param is
         */
        public void setImage(InputStream is) {
            if (is == null) {
                return;
            }
            //先读取原图宽高,然后缩放保证展示的图片跟View的宽度一致
            mOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, mOptions);
            mImageWidth = mOptions.outWidth;
            mImageHeight = mOptions.outHeight;
            //内存复用(长图是一节一节的加载,所以可以使用复用)
            mOptions.inMutable = true;
            mOptions.inJustDecodeBounds = false;
            try {
                //BitmapRegionDecoder会持有is这个对象
                //true 表示内存中is和BitmapRegionDecoder持有的那份为同一个is对象
                //false 表示BitmapRegionDecoder会复制一份is对象,这样在is流关闭了之后
                //并不会影响到BitmapRegionDecoder的相关操作
                mDecoder = BitmapRegionDecoder.newInstance(is, false);
            } catch (IOException e) {
                e.printStackTrace();
            }
            requestLayout();
        }
    
        /**
         * 测量View的大小
         *
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (mDecoder == null) {
                return;
            }
    
            mViewWidth = getMeasuredWidth();
            mViewHeight = getMeasuredHeight();
            //获得缩放因子
            mScale = mViewWidth / (float) mImageWidth;
            mRect.top = 0;
            mRect.left = 0;
            mRect.right = mViewWidth;
            mRect.bottom = (int) (mViewHeight / mScale);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (mDecoder == null) {
                return;
            }
            //复用上一个bitmap
            mOptions.inBitmap = mBitmap;
            //解码制定区域
            mBitmap = mDecoder.decodeRegion(mRect, mOptions);
            //使用矩阵对图片进行缩放
            Matrix matrix = new Matrix();
            matrix.setScale(mScale, mScale);
    
            //画图
            canvas.drawBitmap(mBitmap, matrix, null);
        }
    
        @Override
        public boolean onDown(MotionEvent e) {
            //如果滑动还没有停止 强制停止滑动
            if (!mScroller.isFinished()) {
                mScroller.forceFinished(true);
            }
            //继续接受后续事件
            return true;
        }
    
        @Override
        public void onShowPress(MotionEvent e) {
    
        }
    
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return false;
        }
    
        /**
         * 手指不离开屏幕滑动
         *
         * @param e1        手指按下事件  获取开始的坐标
         * @param e2        当前手势事件 获取当前坐标
         * @param distanceX X轴方向移动的距离
         * @param distanceY Y轴方向移动的距离
         * @return
         */
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            //改变图片加载的区域(图片跟随手指的滑动)
            mRect.offset(0, (int) distanceY);
            //bottom大于图片的高度或者top小于0
            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);
            }
            invalidate();
            return false;
        }
    
        @Override
        public void onLongPress(MotionEvent e) {
    
        }
    
        /**
         * 手指离开屏幕 惯性滑动
         *
         * @param e1        手指按下事件  获取开始的坐标
         * @param e2        当前手势事件 获取当前坐标
         * @param velocityX 速度 每秒X轴方向移动的像素
         * @param velocityY 速度 每秒Y轴方向移动的像素
         * @return
         */
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            /**
             *  startX 滑动开始的X坐标
             *  startY 滑动开始的Y坐标
             *  velocityX X方向初始速度 像素/秒
             * velocityY Y方法
             * minX X方向的最小值
             * maxX
             * minY Y方向的最小值
             * maxY
             */
            //作用是计算惯性滑动的距离
            mScroller.fling(0, mRect.top, 0, (int) -velocityY, 0,
                    0, 0, mImageHeight - (int) (mViewHeight / mScale));
            return false;
        }
    
        /**
         * 获取计算结果并重绘
         */
        @Override
        public void computeScroll() {
            super.computeScroll();
            if (mScroller.isFinished()){
                return;
            }
            //true 表示当前动画未结束
            if (mScroller.computeScrollOffset()){
                //设置了maxY 所以滑动距离不会超出
                mRect.top = mScroller.getCurrY();
                mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
                invalidate();
            }
    
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
    
            return mGestureDetector.onTouchEvent(event);
        }
    }
    

    MainActivity:

    public class MainActivity extends AppCompatActivity {
        private BigView mBigView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mBigView = findViewById(R.id.bigView);
            InputStream is = null;
            try {
                is = getAssets().open("big.png");
                mBigView.setImage(is);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if (is !=null){
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    
    }
    

    相关文章

      网友评论

          本文标题:加载长图

          本文链接:https://www.haomeiwen.com/subject/kmaayqtx.html