长图加载的自定义view。
总结:1.利用Options先得到图片的宽高。
2.通过计算view的宽高得到 缩放因子。
3.利用缩放因子,计算要加载的图片Rect。
4.利用 BitmapRegionDecoder 结合Rect 进行制定区域解码图片。
5.通过 Options.inMutable = true 和Options.inBitmap = bitmap 重复利用Bitmap内存
6.利用缩放因子进行Bitmap矩阵的绘制,达到完全显示图片的效果
package com.example.bigview import android.content.Context import android.graphics.* import android.util.AttributeSet import android.view.GestureDetector import android.view.MotionEvent import android.view.View import android.widget.Scroller import java.io.InputStream /** * 长图加载自定义 view */ class VerticalLoadBigView : View, GestureDetector.OnGestureListener { lateinit var bitmap: Bitmap private var mViewHeight: Int = 0 var mViewWidth: Int = 0 var mRect: Rect //要加载的图片的矩形区域 var mOptions: BitmapFactory.Options //需要复用的 var mGestureDetector: GestureDetector //手势识别 var mImageWidth = 0 var mImageHeight = 0 lateinit var mDecoder: BitmapRegionDecoder var mScroller: Scroller //滑动帮助类 var mScale: Float = 0.0f constructor(context: Context) : this(context, null) constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0) constructor(context: Context, attributeSet: AttributeSet?, defS: Int) : super( context, attributeSet, defS ) { mRect = Rect() mOptions = BitmapFactory.Options() mGestureDetector = GestureDetector(context, this) mScroller = Scroller(context) } /** * 设置图片进来 */ fun setImage(inputStream: InputStream) { mOptions.inJustDecodeBounds = true BitmapFactory.decodeStream(inputStream, null, mOptions) //得到图片的宽高 mImageWidth = mOptions.outWidth mImageHeight = mOptions.outHeight mOptions.inMutable = true //开启重用bitmap 必须与mOptions.bitmap = bitmap一起使用 //设置格式 mOptions.outConfig = Bitmap.Config.RGB_565 mOptions.inJustDecodeBounds = false //创建一个区域解码器 mDecoder = BitmapRegionDecoder.newInstance(inputStream, false) requestLayout() } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) //得到View的宽高 mViewWidth = measuredWidth mViewHeight = measuredHeight //获得缩放因子 if (mImageHeight > mImageWidth) { mScale = (mViewWidth / mImageWidth).toFloat() } else { mScale = (mViewHeight / mImageHeight).toFloat() } //确定要加载的图片的矩形区域 mRect.left = 0 mRect.top = 0 mRect.right = mImageWidth mRect.bottom = (mImageHeight / mScale).toInt() } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) //解码图片的指定区域 bitmap = mDecoder.decodeRegion(mRect, mOptions) //复用上一张bitmap的内存区域 mOptions.inBitmap = bitmap //创建一个缩放比 val matrix = Matrix() matrix.setScale(mScale, mScale) canvas?.drawBitmap(bitmap, matrix, null) } override fun onTouchEvent(event: MotionEvent?): Boolean { return mGestureDetector.onTouchEvent(event) } override fun onShowPress(e: MotionEvent?) { } override fun onSingleTapUp(e: MotionEvent?): Boolean { return false } override fun onDown(e: MotionEvent?): Boolean { if (!mScroller.isFinished) { //如果移动还没有停止,强制停止 mScroller.forceFinished(true) } return true } /** * 处理惯性事件 * velocaityX 计算像素点每秒移动的速度 得到的是一个速度值 * velocaityY */ override fun onFling( e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float ): Boolean { //做计算 mScroller.fling( 0, mRect.top, 0, (-velocityY).toInt(), 0, 0, 0, mImageHeight - (mViewHeight / mScale).toInt() ) return false } override fun computeScroll() { if (mScroller.isFinished) { return } //true 表示当前滑动还没有结束 就去计算 mRect的上下值 if (mScroller.computeScrollOffset()) { mRect.top = mScroller.currY mRect.bottom = mRect.top + (mViewHeight / mScale).toInt() invalidate() } } /** * 处理滑动事件 * e1 按下的 event * e2 移动的event * distanceX 左右移动的距离 * distanceY 上下移动的距离 */ override fun onScroll( e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float ): Boolean { //设置偏移的距离 mRect.offset(0, distanceY.toInt()) //处理移动时已经移到了两个顶端的问题 if (mRect.bottom > mImageHeight) { mRect.bottom = mImageHeight mRect.top = mImageHeight - (mViewHeight / mScale).toInt() } if (mRect.top < 0) { mRect.top = 0 mRect.bottom = (mViewHeight / mScale).toInt() } invalidate() return false; } override fun onLongPress(e: MotionEvent?) { } }