Skip to content

Instantly share code, notes, and snippets.

@afreakyelf
Created February 11, 2021 11:17
Show Gist options
  • Save afreakyelf/427b4bc7564d8ad1e90d0c7bdc7c59cb to your computer and use it in GitHub Desktop.
Save afreakyelf/427b4bc7564d8ad1e90d0c7bdc7c59cb to your computer and use it in GitHub Desktop.
PinchZoomRecyclerView for android
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener
import androidx.recyclerview.widget.RecyclerView
/**
* Created by Rajat on 11,July,2020
*/
class PinchZoomRecyclerView : RecyclerView {
private var mActivePointerId = INVALID_POINTER_ID
private var mScaleDetector: ScaleGestureDetector? = null
private var mScaleFactor = 1f
private var maxWidth = 0.0f
private var maxHeight = 0.0f
private var mLastTouchX = 0f
private var mLastTouchY = 0f
private var mPosX = 0f
private var mPosY = 0f
private var width = 0f
private var height = 0f
constructor(context: Context?) : super(context!!) {
if (!isInEditMode) mScaleDetector =
ScaleGestureDetector(getContext(), ScaleListener())
}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {
if (!isInEditMode) mScaleDetector =
ScaleGestureDetector(getContext(), ScaleListener())
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context!!,
attrs,
defStyleAttr
) {
if (!isInEditMode) mScaleDetector =
ScaleGestureDetector(getContext(), ScaleListener())
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
width = MeasureSpec.getSize(widthMeasureSpec).toFloat()
height = MeasureSpec.getSize(heightMeasureSpec).toFloat()
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
try {
return super.onInterceptTouchEvent(ev)
} catch (ex: IllegalArgumentException) {
ex.printStackTrace()
}
return false
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
super.onTouchEvent(ev)
val action = ev.action
mScaleDetector!!.onTouchEvent(ev)
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val x = ev.x
val y = ev.y
mLastTouchX = x
mLastTouchY = y
mActivePointerId = ev.getPointerId(0)
}
MotionEvent.ACTION_MOVE -> {
val pointerIndex = (action and MotionEvent.ACTION_POINTER_INDEX_MASK
shr MotionEvent.ACTION_POINTER_INDEX_SHIFT)
val x = ev.getX(pointerIndex)
val y = ev.getY(pointerIndex)
val dx = x - mLastTouchX
val dy = y - mLastTouchY
mPosX += dx
mPosY += dy
if (mPosX > 0.0f) mPosX = 0.0f
else if (mPosX < maxWidth) mPosX = maxWidth
if (mPosY > 0.0f) mPosY = 0.0f
else if (mPosY < maxHeight) mPosY = maxHeight
mLastTouchX = x
mLastTouchY = y
invalidate()
}
MotionEvent.ACTION_UP -> {
mActivePointerId = INVALID_POINTER_ID
}
MotionEvent.ACTION_CANCEL -> {
mActivePointerId = INVALID_POINTER_ID
}
MotionEvent.ACTION_POINTER_UP -> {
val pointerIndex =
action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
val pointerId = ev.getPointerId(pointerIndex)
if (pointerId == mActivePointerId) {
val newPointerIndex = if (pointerIndex == 0) 1 else 0
mLastTouchX = ev.getX(newPointerIndex)
mLastTouchY = ev.getY(newPointerIndex)
mActivePointerId = ev.getPointerId(newPointerIndex)
}
}
}
return true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.translate(mPosX, mPosY)
canvas.scale(mScaleFactor, mScaleFactor)
canvas.restore()
}
override fun dispatchDraw(canvas: Canvas) {
canvas.save()
if (mScaleFactor == 1.0f) {
mPosX = 0.0f
mPosY = 0.0f
}
canvas.translate(mPosX, mPosY)
canvas.scale(mScaleFactor, mScaleFactor)
super.dispatchDraw(canvas)
canvas.restore()
invalidate()
}
private inner class ScaleListener : SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
val scale = detector.scaleFactor
mScaleFactor = 1.0f.coerceAtLeast((mScaleFactor * scale).coerceAtMost(3.0f))
if (mScaleFactor < 3f) {
val centerX = detector.focusX
val centerY = detector.focusY
var diffX = centerX - mPosX
var diffY = centerY - mPosY
diffX = diffX * detector.scaleFactor - diffX
diffY = diffY * detector.scaleFactor - diffY
mPosX -= diffX
mPosY -= diffY
}
maxWidth = width - width * mScaleFactor
maxHeight = height - height * mScaleFactor
invalidate()
return true
}
}
companion object {
private const val INVALID_POINTER_ID = -1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment