Kotlinでピンチイン／アウト操作を行う

#はじめに
Kotlinでピンチイン／アウトつまり拡大縮小のサンプルが世の中に無いので作成します。
ついでにスクロール操作も作成します。
#準備
CustomViewというViewクラスを継承するクラスを作成します。

```sample.kt
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    var paint: Paint = Paint()
    override fun onDraw(canvas: Canvas) {
        canvas.drawColor(Color.argb(255, 0, 127, 0))
    }
}
```
背景を緑っぽくして塗りつぶしています。
これを配置しましょう。

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <com.example.[アプリの名称].CustomView
            android:id="@+id/customView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
```

アプリの名称部分はcom.exampleと入力しているときの補完で自動的に入力されるかと思います。
これをデザインビューで見るとすでに緑で表示されています。
すでに裏ではコンパイルされているのでしょうか？

#描画
onDraw内に追加して絵を描きます。
拡大縮小がわかりやすいように線と丸を書きます。

```draw.kt
    override fun onDraw(canvas: Canvas) {
        canvas.drawColor(Color.argb(255, 0, 127, 0))
        var x0 : Float= (canvas.width / 2F)
        var y0 :Float = (canvas.height /2F)
        paint.setStrokeWidth(15F)
        paint.setColor(Color.argb(255, 0, 255, 120))
        canvas.drawLine(x0, y0 - 100, x0 + 100, y0 + 100, paint)
        canvas.drawLine(x0 - 100, y0 + 100, x0 + 100, y0 + 100, paint)
        canvas.drawLine(x0 - 100, y0 + 100, x0, y0 - 100, paint)
        paint.setColor(Color.argb(255, 255, 255, 68))
        paint.setStrokeWidth(30F)
        paint.setAntiAlias(true)
        paint.setStyle(Paint.Style.STROKE)
        canvas.drawCircle(x0, y0, 10F, paint)
    }
```

線を3本引いて作る三角形と丸を書いています。
x0とy0には画面横幅、縦幅の半分つまり中心の位置が入っています。

ではピンチイン／アウトとスクロールを追加します。
ついでにさっきのサンプルが**Kotlinらしくない**と警告が出ていたのも修正します。

```CustomView.kt
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    var paint: Paint = Paint()
    var x1 : Float = 0F
    var y1 : Float = 0F
    var x0 : Float = 0F
    var y0 : Float = 0F
    var scale : Float = 1F
    override fun onDraw(canvas: Canvas) {
        canvas.drawColor(Color.argb(255, 0, 127, 0))
        x0  = (width / 2F)
        y0  = (height /2F)
        paint.strokeWidth = 15F
        paint.color = Color.argb(255, 0, 255, 120)
        canvas.drawLine(xx(0F),yy(-100F),xx(100F),yy(100F), paint)
        canvas.drawLine(xx(-100F),yy(100F), xx(100F), yy(100F), paint)
        canvas.drawLine(xx(-100F),yy(100F),xx(0F),yy(-100F), paint)
        paint.color = Color.argb(255, 255, 255, 68)
        paint.strokeWidth = 30F
        paint.isAntiAlias = true
        paint.style = Paint.Style.STROKE
        canvas.drawCircle(xx(0F),yy(0F) , 10F, paint)
    }
    fun xx(x : Float) : Float{
        return x0 + x * scale + x1
    }
    fun yy(y : Float) : Float{
        return y0 + y * scale + y1
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(ev: MotionEvent): Boolean {
        mScaleDetector.onTouchEvent(ev)
        mGestureDetector.onTouchEvent(ev)
        return true
    }

    private val scaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {

        override fun onScale(detector: ScaleGestureDetector): Boolean {
            scale *= detector.scaleFactor
            invalidate()
            return true
        }
    }

    private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
        override fun onScroll(
                e1: MotionEvent,
                e2: MotionEvent,
                distanceX: Float,
                distanceY: Float
        ): Boolean {
            x1 -= distanceX
            y1 -= distanceY
            invalidate()
            return true
        }
    }
    private val mScaleDetector = ScaleGestureDetector(context, scaleListener)
    private val mGestureDetector = GestureDetector(context,gestureListener)
}
```
x1,y1にはスクロール要求分を反映、scaleには拡大縮小率要求を反映。
xx()とyy()で座標変換しています。

これを実行するとピンチイン／アウトやスクロールが出来るようになります。
コード補完で自動的に入力される部分を除くとほとんど何も書いてませんがそれでもちゃんと動作するようです。
ちなみに**invalidate()**は再描画です。

MainActivityからCustomView内のメソッドを呼びたい場合は

```MainActivity.kt
    val customView = activity?.findViewById<customView>(R.id.customView)
    customView?.[メソッド名]
```
のように呼び出します。

#最後に
よく使われそうなピンチイン／アウトやスクロールですが意外とKotlinでのやり方が書かれていないので参考にして頂ければと思います。

こちらのサイトを参考にしています。
[ImageViewをカスタマイズする【前編 ピンチイン・ピンチアウト】](https://tech.pjin.jp/blog/2015/11/24/androidimageview%e3%82%92%e3%82%ab%e3%82%b9%e3%82%bf%e3%83%9e%e3%82%a4%e3%82%ba%e3%81%99%e3%82%8b%e3%80%90%e5%89%8d%e7%b7%a8-%e3%83%94%e3%83%b3%e3%83%81%e3%82%a4%e3%83%b3%e3%83%bb%e3%83%94%e3%83%b3/)