diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0abecd057..41d01e6a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -51,6 +51,9 @@ android:name=".ui.activities.HelpActivity" android:label="@string/settings" /> + + + mScaleDetector.onTouchEvent(event) + bitmapMatrix.getValues(m) + val x = m[Matrix.MTRANS_X] + val y = m[Matrix.MTRANS_Y] + val curr = PointF(event.x, event.y) + when (event.action) { + MotionEvent.ACTION_DOWN -> { + last[event.x] = event.y + start.set(last) + mode = DRAG + } + + MotionEvent.ACTION_POINTER_DOWN -> { + last[event.x] = event.y + start.set(last) + mode = ZOOM + } + + MotionEvent.ACTION_MOVE -> //if the mode is ZOOM or + //if the mode is DRAG and already zoomed + if (mode == ZOOM || mode == DRAG && saveScale > minScale) { + var deltaX = curr.x - last.x // x difference + var deltaY = curr.y - last.y // y difference + val scaleWidth = (origWidth * saveScale).roundToInt() + .toFloat() // width after applying current scale + val scaleHeight = (origHeight * saveScale).roundToInt() + .toFloat() // height after applying current scale + //if scaleWidth is smaller than the views width + //in other words if the image width fits in the view + //limit left and right movement + if (scaleWidth < width) { + deltaX = 0f + if (y + deltaY > 0) deltaY = -y else if (y + deltaY < -bottom) deltaY = + -(y + bottom) + } else if (scaleHeight < height) { + deltaY = 0f + if (x + deltaX > 0) deltaX = -x else if (x + deltaX < -right) deltaX = + -(x + right) + } else { + if (x + deltaX > 0) deltaX = -x else if (x + deltaX < -right) deltaX = + -(x + right) + if (y + deltaY > 0) deltaY = -y else if (y + deltaY < -bottom) deltaY = + -(y + bottom) + } + //move the image with the matrix + bitmapMatrix.postTranslate(deltaX, deltaY) + //set the last touch location to the current + last[curr.x] = curr.y + } + + MotionEvent.ACTION_UP -> { + mode = NONE + val xDiff = abs(curr.x - start.x).toInt() + val yDiff = abs(curr.y - start.y).toInt() + if (xDiff < CLICK && yDiff < CLICK) performClick() + } + + MotionEvent.ACTION_POINTER_UP -> mode = NONE + } + imageMatrix = bitmapMatrix + invalidate() + true + } + } + + override fun setImageBitmap(bm: Bitmap) { + super.setImageBitmap(bm) + bmWidth = bm.width.toFloat() + bmHeight = bm.height.toFloat() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + width = MeasureSpec.getSize(widthMeasureSpec).toFloat() + height = MeasureSpec.getSize(heightMeasureSpec).toFloat() + // Fit to screen. + val scaleX = width / bmWidth + val scaleY = height / bmHeight + val scale = scaleX.coerceAtMost(scaleY) + bitmapMatrix.setScale(scale, scale) + imageMatrix = bitmapMatrix + saveScale = 1f + + // Center the image + redundantYSpace = height - scale * bmHeight + redundantXSpace = width - scale * bmWidth + redundantYSpace /= 2f + redundantXSpace /= 2f + bitmapMatrix.postTranslate(redundantXSpace, redundantYSpace) + origWidth = width - 2 * redundantXSpace + origHeight = height - 2 * redundantYSpace + right = width * saveScale - width - 2 * redundantXSpace * saveScale + bottom = height * saveScale - height - 2 * redundantYSpace * saveScale + imageMatrix = bitmapMatrix + } + + private inner class ScaleListener : SimpleOnScaleGestureListener() { + override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { + mode = ZOOM + return true + } + + override fun onScale(detector: ScaleGestureDetector): Boolean { + var mScaleFactor = detector.scaleFactor + val origScale = saveScale + saveScale *= mScaleFactor + if (saveScale > maxScale) { + saveScale = maxScale + mScaleFactor = maxScale / origScale + } else if (saveScale < minScale) { + saveScale = minScale + mScaleFactor = minScale / origScale + } + right = width * saveScale - width - 2 * redundantXSpace * saveScale + bottom = height * saveScale - height - 2 * redundantYSpace * saveScale + if (origWidth * saveScale <= width || origHeight * saveScale <= height) { + bitmapMatrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2) + if (mScaleFactor < 1) { + bitmapMatrix.getValues(m) + val x = m[Matrix.MTRANS_X] + val y = m[Matrix.MTRANS_Y] + + if ((origWidth * saveScale).roundToInt() < width) { + if (y < -bottom) bitmapMatrix.postTranslate( + 0f, + -(y + bottom) + ) else if (y > 0) bitmapMatrix.postTranslate(0f, -y) + } else { + if (x < -right) bitmapMatrix.postTranslate( + -(x + right), + 0f + ) else if (x > 0) bitmapMatrix.postTranslate(-x, 0f) + } + } + } else { + bitmapMatrix.postScale(mScaleFactor, mScaleFactor, detector.focusX, detector.focusY) + bitmapMatrix.getValues(m) + val x = m[Matrix.MTRANS_X] + val y = m[Matrix.MTRANS_Y] + + if (mScaleFactor < 1) { + if (x < -right) bitmapMatrix.postTranslate( + -(x + right), + 0f + ) else if (x > 0) bitmapMatrix.postTranslate(-x, 0f) + if (y < -bottom) bitmapMatrix.postTranslate( + 0f, + -(y + bottom) + ) else if (y > 0) bitmapMatrix.postTranslate(0f, -y) + } + } + return true + } + } + + companion object { + const val NONE = 0 + const val DRAG = 1 + const val ZOOM = 2 + const val CLICK = 3 + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_zoomable_image.xml b/app/src/main/res/layout/activity_zoomable_image.xml new file mode 100644 index 000000000..ee826c44f --- /dev/null +++ b/app/src/main/res/layout/activity_zoomable_image.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file