Android性能优化——加载长图或者高清大图


下面我们来手撸一个加载长图的控件

使用BitmapRegionDecoder

BitmapRegionDecoder可用于从图像解码矩形区域。BitmapRegionDecoder在原始图像较大且只需要部分图像时特别有用。

要创建BitmapRegionDecoder,请调用NewInstance(…)。给定BitmapRegionDecoder,用户可以重复调用decodeRegion()以获取指定区域的解码位图。

具体代码

  
private BitmapRegionDecoder mDecoder;
...
//创建一个区域解码器                                                            
try {                                                                  
    mDecoder = BitmapRegionDecoder.newInstance(is, false);             
} catch (IOException e) {                                              
    e.printStackTrace();                                               
}                                                                      
...
//解码指定的区域                                           
bitmap = mDecoder.decodeRegion(mRect, mOptions);    
      

开启复用

  • Bitmap一定要是可变的,即inmutable设置一定为ture;
  • Android4.4以下的平台,需要保证inBitmap和即将要得到decode的Bitmap的尺寸规格一致;
  • Android4.4及其以上的平台,只需要满足inBitmap的尺寸大于要decode得到的Bitmap的尺寸规格即可;

开启bitmap的复用需要使用

 options.inBitmap

而使用这个需要的开启

   //将inMutable设置true,inBitmap生效的条件之一
    options.inMutable = true;

具体代码如下

 private BitmapFactory.Options mOptions;    
 private int mImageWidth;    
 private int mImageHeight;   
 ...
     
 mOptions.inJustDecodeBounds = true;                         
 BitmapFactory.decodeStream(is, null, mOptions);             
 mImageWidth = mOptions.outWidth;                            
 mImageHeight = mOptions.outHeight;                          
 //开启复用                                                      
 mOptions.inMutable = true;                                  
 //设置格式成RGB_565                                              
 mOptions.inPreferredConfig = Bitmap.Config.RGB_565;         
 mOptions.inJustDecodeBounds = false;                        

计算显示的大小

重写onMeasure方法

 private Rect mRect;     
 private int mViewWidth;       
 private int mViewHeight;      
 private float mScale; 
 ... 
@Override                                                                              
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {                
     super.onMeasure(widthMeasureSpec, heightMeasureSpec);                              
     //获取测量的view的大小                                                                 
     mViewWidth = getMeasuredWidth();                                                   
     mViewHeight = getMeasuredHeight(); 
     
     //确定要加载的图片的区域                                                                 
     mRect.left = 0;                                                                    
     mRect.top = 0;                                                                     
     mRect.right = mImageWidth;
     
     //获取一个缩放因子                                                                     
     mScale = mViewWidth / (float) mImageWidth;                                         
     //高度就根据缩放比进行获取                                                               
     mRect.bottom = (int) (mViewHeight / mScale);                                       
                                                                                        
 }                                                                                      

显示到画布上

重写onDraw方法

 @Override                                                      
 protected void onDraw(Canvas canvas) {                         
     super.onDraw(canvas);                                      
     //如果解码器拿不到,表示没有设置过要显示的图片                                   
     if (null == mDecoder) {                                    
         return;                                                
     }                                                          
     //复用上一张bitmap                                              
     mOptions.inBitmap = bitmap;                                
     //解码指定的区域                                                  
     bitmap = mDecoder.decodeRegion(mRect, mOptions);           
     //把得到的矩阵大小的内存进行缩放  得到view的大小                               
     Matrix matrix = new Matrix();                              
     matrix.setScale(mScale, mScale);                           
     //画出来                                                      
     canvas.drawBitmap(bitmap, matrix, null);                   
                                  
 }                                                              

手势滑动

  1. 设置onTouch交给GestureDetector

    private GestureDetector mGestureDetector;
    private Scroller mScroller;
    
    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);
            //设置onTouchListener
            setOnTouchListener(this);
            //滑动帮助
            mScroller = new Scroller(context);
        }
    
     @Override
        public boolean onTouch(View v, MotionEvent event) {
            //交给手势处理
            return mGestureDetector.onTouchEvent(event);
        }
    
    
  2. 处理滑动和快速滑动事件

    
        /**
         * 手按下的回调
         */
        @Override
        public boolean onDown(MotionEvent e) {
            //如果移动还没有停止,强制停止
            if (!mScroller.isFinished()) {
                mScroller.forceFinished(true);
            }
            //继续接收后续事件
            return true;
        }
    
    
        /**
         * @param e1        接下
         * @param e2        移动
         * @param distanceX 左右移动时的距离
         * @param distanceY 上下移动时的距离
         * @return
         */
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            //上下移动的时候,需要改变显示区域   改mRect
            mRect.offset(0, (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);
            }
            invalidate();
            return false;
        }
    
        /**
         * 处理惯性问题
         *
         * @param e1
         * @param e2
         * @param velocityX 每秒移动的x点
         * @param velocityY
         * @return
         */
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            //做计算
            mScroller.fling(0, mRect.top,
                    0, (int) -velocityY,
                    0, 0,
                    0, mImageHeight - (int) (mViewHeight / mScale));
            return false;
        }
        
        @Override
        public void computeScroll() {
            if (mScroller.isFinished()) {
                return;
            }
            //true 表示当前滑动还没有结束
            if (mScroller.computeScrollOffset()) {
                mRect.top = mScroller.getCurrY();
                mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
                invalidate();
            }
        }
    

如果想要增加更多的手势,我们下期再见

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ddssingsong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值