Skip to content

Instantly share code, notes, and snippets.

@gchumillas
Last active November 11, 2020 16:12
Show Gist options
  • Save gchumillas/1027794c476f7f4eeddc6bfa53a17e84 to your computer and use it in GitHub Desktop.
Save gchumillas/1027794c476f7f4eeddc6bfa53a17e84 to your computer and use it in GitHub Desktop.
Rotate, Scale and Move in Android (Kotlin)
package com.example.myapplication
import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Matrix
import android.graphics.PointF
import android.os.Bundle
import android.view.ScaleGestureDetector
import androidx.core.view.doOnLayout
import com.almeros.android.multitouch.MoveGestureDetector
import com.almeros.android.multitouch.RotateGestureDetector
import com.example.myapplication.databinding.ActivityMainBinding
@SuppressLint("ClickableViewAccessibility")
class MainActivity : Activity() {
private lateinit var binding: ActivityMainBinding
private lateinit var imageMatrix: Matrix
private var translate = PointF(0f, 0f)
private var rotate = 0f
private var scale = 1f
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val imageView = binding.imageView
imageView.doOnLayout {
var moveGestureDetector = MoveGestureDetector(this, MoveGestureHandler())
var rotateGestureDetector = RotateGestureDetector(this, RotateGestureHandler())
val scaleGestureDetector = ScaleGestureDetector(this, ScaleGestureHandler())
val center = PointF(
imageView.measuredWidth.toFloat() / 2f,
imageView.measuredHeight.toFloat() / 2f
)
// Centers the image
imageMatrix = Matrix().apply {
setTranslate(
center.x - imageView.drawable.intrinsicWidth / 2f,
center.y - imageView.drawable.intrinsicHeight / 2f
)
}
imageView.imageMatrix = imageMatrix
imageView.setOnTouchListener { _, event ->
moveGestureDetector.onTouchEvent(event)
rotateGestureDetector.onTouchEvent(event)
scaleGestureDetector.onTouchEvent(event)
imageView.imageMatrix = Matrix().apply {
set(imageMatrix)
postTranslate(-center.x, -center.y)
postRotate(rotate)
postScale(scale, scale)
postTranslate(center.x + translate.x, center.y + translate.y)
}
true
}
}
}
inner class MoveGestureHandler : MoveGestureDetector.SimpleOnMoveGestureListener() {
override fun onMove(detector: MoveGestureDetector): Boolean {
translate = detector.focusDelta
return super.onMove(detector)
}
override fun onMoveEnd(detector: MoveGestureDetector) {
imageMatrix = Matrix(binding.imageView.imageMatrix)
translate = PointF(0f, 0f)
super.onMoveEnd(detector)
}
}
inner class RotateGestureHandler : RotateGestureDetector.SimpleOnRotateGestureListener() {
override fun onRotate(detector: RotateGestureDetector): Boolean {
rotate = -detector.rotationDegreesDelta
return super.onRotate(detector)
}
override fun onRotateEnd(detector: RotateGestureDetector?) {
imageMatrix = Matrix(binding.imageView.imageMatrix)
rotate = 0f
super.onRotateEnd(detector)
}
}
inner class ScaleGestureHandler : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scale = detector.scaleFactor
return super.onScale(detector)
}
override fun onScaleEnd(detector: ScaleGestureDetector?) {
imageMatrix = Matrix(binding.imageView.imageMatrix)
scale = 1f
super.onScaleEnd(detector)
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="@string/image_editor"
android:scaleType="matrix"
android:src="@mipmap/ic_launcher" />
</LinearLayout>
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
viewBinding {
enabled = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.github.Almeros:android-gesture-detectors:v1.0.1'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment