长图加载的自定义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?) {
}
}
|