在支持双指的缩放的时候,首先要了解一下Android支持缩放的工具类ScaleGestureDetector,这个缩放是根据这个工具类来实现的。ScaleGestureDetector是用于处理缩放的工具类,用法与GestureDetector类似,都是通过onTouchEvent()关联相应的MotionEvent事件。
OnScaleGestureListener回调方法的介绍:
public interface OnScaleGestureListener {
/**
* 缩放进行中,返回值表示是否下次缩放需要重置,如果返回ture,那么detector就会重置缩放事件,如果返回false,detector会在之前的缩放上继续进行计算
*/
public boolean onScale(ScaleGestureDetector detector);
/**
* 缩放开始,返回值表示是否受理后续的缩放事件
*/
public boolean onScaleBegin(ScaleGestureDetector detector);
/**
* 缩放结束
*/
public void onScaleEnd(ScaleGestureDetector detector);
}
因为这个地方既有GestureDetector又有ScaleGestureDetector,下面在onTouchEvent的方法的处理会有所改变:
override fun onTouchEvent(event: MotionEvent?): Boolean {
var result= scaleGestureDetector.onTouchEvent(event)
if(!scaleGestureDetector.isInProgress){
result = gesture.onTouchEvent(event)
}
return result
}
下面就可以上一下完整的代码了:
class ScaleImageView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int=0) : AppCompatImageView(context,attrs,defStyleAttr),
GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, Runnable,
ScaleGestureDetector.OnScaleGestureListener {
private val scaleFactor = 1.5f
private val picture:Bitmap = BitmapFactory.decodeResource(resources, R.drawable.banner)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var bigScale = 1f
private var smallScale = 1f
private var offsetX = 0f
private var offsetY = 0f
private var currentOffsetY = 0f
private var currentOffsetX = 0f
private var isBig = false
private val gesture:GestureDetectorCompat = GestureDetectorCompat(context,this)
private val overScroller = OverScroller(context)
private var animator:ObjectAnimator? = null
private val scaleGestureDetector = ScaleGestureDetector(context,this)
private var initScale:Float = 0f
var currentScale:Float = 1f
set(value) {
field = value
invalidate()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if(w/width.toFloat() > h/height.toFloat()){
bigScale = width / picture.width.toFloat() * scaleFactor
smallScale = height / picture.height.toFloat()
}else{
smallScale = width / picture.width.toFloat()
bigScale = height / picture.height.toFloat() * scaleFactor
}
offsetX = (width - picture.width) / 2f
offsetY = (height - picture.height) / 2f
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
var result= scaleGestureDetector.onTouchEvent(event)
if(!scaleGestureDetector.isInProgress){
result = gesture.onTouchEvent(event)
}
return result
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.let {
var scaleFaction = (currentScale - smallScale) / (bigScale - smallScale)
it.translate(currentOffsetX * scaleFaction, currentOffsetY * scaleFaction)
it.scale(currentScale, currentScale, width / 2f, height / 2f)
it.drawBitmap(picture,offsetX,offsetY,paint)
}
}
//--------------------------GestureDetector.OnGestureListener---------------------------
override fun onShowPress(e: MotionEvent?) {
}
override fun onSingleTapUp(e: MotionEvent?): Boolean {
return false
}
override fun onDown(e: MotionEvent?): Boolean {
return true
}
override fun onFling(
down: MotionEvent?,
event: MotionEvent?,
velocityX: Float,
velocityY: Float
): Boolean {
if(isBig){
overScroller.fling(currentOffsetX.toInt(),currentOffsetY.toInt(),
velocityX.toInt(),velocityY.toInt(),
-(bigScale * picture.width - width).toInt()/2,
(bigScale * picture.width - width).toInt()/2,
-(bigScale * picture.height - height).toInt()/2,
(bigScale * picture.height - height).toInt()/2,
TypedValue.complexToDimensionPixelSize(50,context.resources.displayMetrics),
TypedValue.complexToDimensionPixelSize(50,context.resources.displayMetrics)
)
postOnAnimation(this)
}
return false
}
override fun run() {
if(overScroller.computeScrollOffset()){
currentOffsetY = overScroller.currY.toFloat()
currentOffsetX = overScroller.currX.toFloat()
invalidate()
postOnAnimation(this)
}
}
override fun onScroll(
down: MotionEvent?,
event: MotionEvent?,
distanceX: Float,
distanceY: Float
): Boolean {
if(isBig) {
currentOffsetX -= distanceX
currentOffsetY -= distanceY
fixOffset()
invalidate()
}
return false
}
private fun fixOffset() {
if (currentOffsetX > (bigScale * picture.width - width) / 2) {
currentOffsetX = (bigScale * picture.width - width) / 2
}
if (currentOffsetX < -(bigScale * picture.width - width) / 2) {
currentOffsetX = -(bigScale * picture.width - width) / 2
}
if (currentOffsetY > (bigScale * picture.height - height) / 2) {
currentOffsetY = (bigScale * picture.height - height) / 2
}
if (currentOffsetY < -((bigScale * picture.height - height)) / 2) {
currentOffsetY = -((bigScale * picture.height - height)) / 2
}
}
override fun onLongPress(e: MotionEvent?) {
}
//----------------------------GestureDetector.OnDoubleTapListener---------------------------------
override fun onDoubleTap(e: MotionEvent?): Boolean {
isBig = !isBig
if(isBig){
e?.let {
currentOffsetX = (it.x - width / 2f) - (e.x - width /2f) * bigScale / smallScale
currentOffsetY = (it.y - height / 2f) - (e.y - height / 2f) * bigScale/ smallScale
}
fixOffset()
getAnimator().start()
}else{
getAnimator().reverse()
}
return false
}
private fun getAnimator():ObjectAnimator {
return if (animator == null) {
animator = ObjectAnimator.ofFloat(this, "currentScale", smallScale, bigScale)
animator!!
} else {
if(isBig) {
animator?.setFloatValues(currentScale, bigScale)
}else{
animator?.setFloatValues(smallScale, bigScale)
}
animator!!
}
}
override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
return false
}
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
return false
}
//----------------------------ScaleGestureDetector.OnScaleGestureListener---------------------------------
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
//这个地方返回true 是为了可以处理后面的缩放事件
initScale = currentScale
return true
}
override fun onScaleEnd(detector: ScaleGestureDetector?) {
}
override fun onScale(detector: ScaleGestureDetector?): Boolean {
currentScale = initScale * detector?.scaleFactor!!
fixScale()
invalidate()
return false
}
private fun fixScale() {
if (currentScale > bigScale) {
currentScale = bigScale
}
if (currentScale < smallScale) {
currentScale = smallScale
}
}
}