【Android/Kotlin】円を拡大するアニメーションの実装方法

## 基本画面  
<img width="248" alt=" 2019-12-03 12.20.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275576/08681dee-6589-37ac-d228-b496759cac61.png">

- FrameLayoutをmatch/matchで設定しています。
- 円のカスタムビューをvisible=GONEで設定しています。
- 星のImageViewを2種類おいています。
- 星をクリックすると、円が拡大描画されていきます。

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/back_color"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center">

        <com.kiyota.scaleanimationapp.CircleCustomView
            android:id="@+id/main_circle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone"></com.kiyota.scaleanimationapp.CircleCustomView>

        <ImageView
            android:id="@+id/star_pink"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:src="@drawable/ic_star_pink"></ImageView>

        <ImageView
            android:id="@+id/star_blue"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:layout_marginTop="100dp"
            android:src="@drawable/ic_star"></ImageView>
    </FrameLayout>

</androidx.appcompat.widget.LinearLayoutCompat>
```

```CircleCustomView.kt
class CircleCustomView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    /**
     * サークルの色
     */
    private val paint = Paint()
    /**
     * サークルの座標位置
     */
    private val rect = RectF()
    /**
     * サークルの円周の長さ
     */
    private var angle = 360F
    /**
     * 中心座標(x)
     */
    private var x: Int = 0
    /**
     * 中心座標(y)
     */
    private var y: Int = 0
    /**
     * 初回フラグ
     */
    var isInit = true

    /**
     * コンパニオンオブジェクト
     */
    companion object {
        /**
         * サークルの開始位置
         */
        private const val angleTarget = 0F
    }

    /**
     * コンストラクタ
     */
    init {
        // サークルの幅
        this.paint.isAntiAlias = true
        this.paint.style = Paint.Style.FILL_AND_STROKE
        // サークルの色
        this.paint.color = (context.resources.getColor(R.color.star_color))
    }

    /**
     * ビュー描画メソッド
     *
     * @param canvas 描画領域
     */
    @SuppressLint("CanvasSize")
    public override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 初回は中心スタート
        if (isInit) {
            // サークル開始地点(中央揃え)
            this.x = canvas.width / 2
            this.y = canvas.height / 2
            isInit = false
        }
        // サークルの背景色(透明)
        canvas.drawColor(this.context.resources.getColor(R.color.color_back_circle))
        // サークルの半径()大きさ
        var radius = 40
        val left = this.x - radius
        val top = this.y - radius
        val right = this.x + radius
        val bottom = this.y + radius
        // サークルの領域設定
        this.rect.set(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
        // サークルの描画
        canvas.drawArc(this.rect, angleTarget, this.angle, false, this.paint)
    }

    /**
     * 中心座標変更メソッド
     */
    fun changeCenter(x: Int, y: Int) {
        // サークルの中心座標
        this.x = x
        this.y = y
    }

    /**
     * サークル色変更メソッド
     *
     * @param index 押されたボタンの番号
     */
    fun changeColor(index: Int) {
        when (index) {
            0 -> {
                this.paint.color = context.resources.getColor(R.color.colorAccent)
            }
            1 -> {
                this.paint.color = context.resources.getColor(R.color.star_color)
            }
            else -> {
                this.paint.color = context.resources.getColor(R.color.colorPrimary)
            }
        }
    }
}
```
 
## 画面の中心で円を拡大する場合(ピンク)  
 - 中心座標(540 , 792)
 - pivot(0.5f , 0.5f)
 <img width="963" alt=" 2019-12-03 12.00.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275576/b5745050-251d-3436-6e6d-6a9aa02e9b1a.png">

```MainActivity.kt
package com.kiyota.scaleanimationapp

class MainActivity : AppCompatActivity(), Animation.AnimationListener {

    private var index = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // クリックされたらアニメーションをスタートする
        star_pink.setOnClickListener {
            index = 0
            // 端末(Nexus5x)の中心座標が(540,792)という意味です
            startScalingXml(540, 792, index)
        }
        star_blue.setOnClickListener {
            index = 1
            startScalingXml(540, 792, index)
        }
    }

    /**
     * サークル拡大アニメーションメソッド
     *
     * @param x x座標
     * @param y y座標
     * @param index クリックされたビューの順番
     */
    private fun startScalingXml(x: Int, y: Int, index: Int) {
        main_circle.changeCenter(x, y)
        main_circle.changeColor(index)
        var scaleAnimation = ScaleAnimation(
            1.0f, 30.0f, 1.0f, 30.0f,
            Animation.RELATIVE_TO_SELF,
            0.5f, Animation.RELATIVE_TO_SELF, 0.5f
        )
        scaleAnimation.duration = 20000
        scaleAnimation.repeatCount = 0
        scaleAnimation.fillAfter = true
        main_circle.visibility = VISIBLE
        scaleAnimation.setAnimationListener(this)
        main_circle.startAnimation(scaleAnimation)
    }

    override fun onAnimationRepeat(p0: Animation?) {
    }

    /**
     * アニメーション終了時処理メソッド
     *
     * @param p0 アニメーション
     */
    override fun onAnimationEnd(p0: Animation?) {
        setColorByIndex(index)
        main_circle.visibility = GONE
    }

    override fun onAnimationStart(p0: Animation?) {
    }

    /**
     * 背景色切り替えメソッド
     *
     * @param index クリックされたビューの順番
     */
    private fun setColorByIndex(index: Int) {
        when (index) {
            0 -> {
                back_color.setBackgroundColor(this.resources.getColor(R.color.colorAccent))
            }
            1 -> {
                back_color.setBackgroundColor(this.resources.getColor(R.color.star_color))
            }
            else -> {
                back_color.setBackgroundColor(this.resources.getColor(R.color.colorPrimary))
            }
        }
    }
}

```

## 画面の左側で円を拡大する場合(水色) :失敗  
 - 中心座標(270 , 792)
 - pivot(0.5f , 0.5f)  
 
<img width="489" alt=" 2019-12-03 12.04.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275576/f9f54886-61aa-0edf-9f71-86e100b079d6.png">

## 画面の左側で円を拡大する場合(水色) :成功  
 - 中心座標(270 , 792)
 - pivot(0.25f , 0.5f)
 ※x軸の値を半分にしてあげればOK
<img width="960" alt=" 2019-12-03 12.08.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275576/0efa3291-dbf1-8558-87ed-7ffef03cd31f.png">