Android長圖加載 - 利用Bitmap Options

class BigView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), GestureDetector.OnGestureListener, View.OnTouchListener {
    //要加載的圖片範圍
    private val mRect by lazy {
        Rect()
    }
    //Bitmap配置類
    private val mOptions by lazy {
        BitmapFactory.Options().apply {
            //開啓內存複用
            inMutable = true
            //設置圖片格式爲565
            inPreferredConfig = Bitmap.Config.RGB_565
        }
    }
    //手勢監聽
    private val mGestureDetector by lazy {
        GestureDetector(context, this)
    }
    //Scroller,利用這個類進行滑動
    private val mScroller by lazy {
        Scroller(context)
    }
    //矩陣,處理圖片的縮放
    private val mMatrix by lazy {
        Matrix()
    }
    //圖片寬高
    private var mImageWidth: Int = 0
    private var mImageHeight: Int = 0
    //展示圖片的View的寬高
    private var mViewWidth: Int = 0
    private var mViewHeight: Int = 0
    //縮放因子
    private var mScale: Float = 0f
    //圖片解碼器
    private var mDecoder: BitmapRegionDecoder? = null
    //複用的Bitmap
    private var mBitmap: Bitmap? = null

    init {
        setOnTouchListener(this)
    }

    //設置圖片
    fun setImage(inputStream: InputStream) {
        //設置inJustDecodeBounds,讓其不加載進內存也能獲得圖片相關信息
        mOptions.inJustDecodeBounds = true
        BitmapFactory.decodeStream(inputStream, null, mOptions)
        //獲取圖片寬高
        mImageWidth = mOptions.outWidth
        mImageHeight = mOptions.outHeight
        mOptions.inJustDecodeBounds = false

        //獲取圖片解碼器
        try {
            mDecoder = BitmapRegionDecoder.newInstance(inputStream, false)
        } catch (e: IOException) {
        }

        requestLayout()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //獲取View視圖的寬高
        mViewWidth = measuredWidth
        mViewHeight = measuredHeight

        //計算圖片的縮放係數
        mScale = mViewWidth.div(mImageWidth.toFloat())

        //賦值給圖片要加載區域的Rect,要加載進來的高度需要根據縮放係數計算
        //縮放之後肯定能夠展示更多的高度,所以要算出縮放前需要加載的圖片高度
        mRect.set(0, 0, mImageWidth, mViewHeight.div(mScale).toInt())
    }

    //繪製圖片
    override fun onDraw(canvas: Canvas?) {
        if (mDecoder == null) {
            return
        }

        //設置複用的Bitmap
        mOptions.inBitmap = mBitmap
        //解碼器對圖片加載的區域進行解碼
        mBitmap = mDecoder!!.decodeRegion(mRect, mOptions)
        //設置矩陣
        mMatrix.setScale(mScale, mScale)
        //在展示的區域內繪製這個圖片,區域之外的圖片不會繪製
        mBitmap?.let {
            canvas?.drawBitmap(it, mMatrix, null)
        }
    }

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        //交給手勢識別去處理
        mGestureDetector.onTouchEvent(event)
        return true
    }

    override fun onDown(e: MotionEvent?): Boolean {
        //手指按下的時候,讓滑動停止
        if (!mScroller.isFinished) {
            mScroller.forceFinished(true)
        }
        return true
    }

    override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
        //將矩形偏移
        mRect.offset(0, distanceY.toInt())

        //處理滑動到頂部和底部的情況
        if (mRect.top < 0) {
            mRect.top = 0
            mRect.bottom = mViewHeight.div(mScale).toInt()
        }

        if (mRect.bottom > mImageHeight) {
            mRect.bottom = mImageHeight
            mRect.top = mImageHeight.minus(mViewHeight.div(mScale)).toInt()
        }

        invalidate()
        return true
    }

    override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
        mScroller.fling(0, mRect.top, 0, -velocityY.toInt(), 0, 0, 0, mImageHeight.minus(mViewHeight.div(mScale)).toInt())
        return true
    }

    override fun computeScroll() {
        if (mScroller.isFinished) {
            return
        }

        if (mScroller.computeScrollOffset()) {
            mRect.top = mScroller.currY
            mRect.bottom = mRect.top.plus(mViewHeight.div(mScale).toInt())
            invalidate()
        }
    }

    override fun onShowPress(e: MotionEvent?) {
    }

    override fun onSingleTapUp(e: MotionEvent?): Boolean = true

    override fun onLongPress(e: MotionEvent?) {
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章