본문 바로가기
프로젝트/Roulette Wheel View

[Roulette] 룰렛을 그리고 회전시키기 (2) - 텍스트 쓰기, 애니메이션 적용

by JhDroid 2021. 2. 23.
728x90
 

JhDroid/android-roulette-wheel-view

Android draw roulette view. Contribute to JhDroid/android-roulette-wheel-view development by creating an account on GitHub.

github.com

 

룰렛 내부에 텍스트 쓰기

  • 이전 글에서 완성한 룰렛 내부에 텍스트를 쓰는 방법을 알아보겠습니다.

 

1. 텍스트를 위한 Paint와 데이터 리스트 추가

  • 기본적으로 텍스트 색상은 검은색이고 설정으로 중앙 정렬을 추가하겠습니다.
class Roulette @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val strokePaint = Paint()
    private val fillPaint = Paint()
    private val textPaint = Paint()

    private var rouletteSize = 8
    private var rouletteDataList = listOf()

    init {
        strokePaint.apply {
            color = Color.BLACK
            style = Paint.Style.STROKE
            strokeWidth = 15f
            isAntiAlias = true
        }

        fillPaint.apply {
            style = Paint.Style.FILL
            isAntiAlias = true
        }

        textPaint.apply {
            color = Color.BLACK
            textSize = 60f
            textAlign = Paint.Align.CENTER
        }
    }
    ...
}
  • 코드 설명
    • textPaint
      • 텍스트를 위한 Paint 변수 입니다.
      • textAlign = Pain.Align.CENTER : 텍스트 중앙 정렬을 위한 설정입니다.
    • rouletteDataList
      • 룰렛 내부에 들어갈 텍스트 데이터 리스트입니다.
      • 룰렛 크기와 상관없이 0~n개 까지의 데이터가 들어갈 수 있지만 순서대로 8개 까지만 표시됩니다.

 

2. 내부 원을 그리면서 텍스트도 함께 그려주기

  • 텍스트는 룰렛 크기만큼 그려줘야하기 때문에 룰렛 내부를 그리면서 함께 그리도록 추가했습니다.
    • 만약 룰렛 데이터 리스트의 크기가 i (0~룰렛 사이즈) 보다 작다면 "empty"라는 텍스트를 채워주도록 했습니다.
private fun drawRoulette(canvas: Canvas?, rectF: RectF) {
    canvas?.drawArc(rectF, 0f, 360f, true, strokePaint)

    if (rouletteSize in 2..8) {
        val sweepAngle = 360f / rouletteSize.toFloat()
        val centerX = (rectF.left + rectF.right) / 2
        val centerY = (rectF.top + rectF.bottom) / 2
        val radius = (rectF.right - rectF.left) / 2 * 0.5
        val colors = listOf("#fe4a49", "#2ab7ca", "#fed766", "#e6e6ea", "#f6abb6", "#005b96", "#7bc043", "#f37735")

        for (i in 0 until rouletteSize) {
            fillPaint.color = Color.parseColor(colors[i])

            val startAngle = if (i == 0) 0f else sweepAngle * i
            canvas?.drawArc(rectF, startAngle, sweepAngle, true, fillPaint)

            val medianAngle = (startAngle + sweepAngle / 2f) * Math.PI / 180f
            val x = (centerX + (radius * cos(medianAngle))).toFloat()
            val y = (centerY + (radius * sin(medianAngle))).toFloat() + Roulette.DEFAULT_PADDING

            val text = if (i > rouletteDataList.size - 1) "empty" else rouletteDataList[i]
            canvas?.drawText(text, x, y, textPaint)
        }
    } else throw RuntimeException("size out of roulette")
}
  • 코드 설명
    • centerX / centryY
      • 룰렛의 중심 좌표를 담는 변수입니다.
    • radius
      • 룰렛의 반지름을 담는 변수입니다.
      • 반지름에 곱해주는 값(0.5)을 변경하면 텍스트를 좀더 넓게 그릴 수 있습니다.
    • medianAngle
      • 룰렛 내부의 중앙 각도를 담는 변수입니다.
    • x / y
      • 텍스트를 그려줄 좌표를 담는 변수입니다.
      • y 값에 padding을 더해주는 이유는 왜그런지 모르겠는데 텍스트가 약간 위로 붙어서 나와서 더해주고 있습니다.(원인불명..ㅠㅠ)
    • val text
      • 텍스트 데이터 리스트에 있는 텍스트만 표시하고 빈 부분은 "empty"라는 임의의 값으로 대체합니다.
    • drawText(텍스트, x 좌표, y 좌표, Paint())
      • 텍스트를 그려주는 함수입니다.
      • Paint()에서 textAlign을 설정해주지 않으면 텍스트 위치가 이상하게 나올겁니다.
        • 왜냐하면 텍스트는 x값을 중심으로 하단 그림처럼 그리게됩니다.

 

3. 텍스트까지 추가한 결과

결과

 

룰렛 회전 애니메이션 적용

  • 아직 룰렛이 완전하지는 않지만 애니메이션을 적용해보겠습니다.

1. 회전 함수 생성

  • Roulette 클래스에 회전 함수를 추가합니다.
fun rotateRoulette(toDegrees: Float, duration: Long) {
    val rotateAnim = RotateAnimation(
        0f, toDegrees,
        Animation.RELATIVE_TO_SELF, 0.5f,
        Animation.RELATIVE_TO_SELF, 0.5f
    )
    rotateAnim.duration = duration
    rotateAnim.fillAfter = true

    startAnimation(rotateAnim)
}
  • 코드 설명
    • rotateAnim(시작 각도, 회전 각도, x축 타입, x축 값, y축 타입, y축 값)
      • 시작 각도를 0이 아닌 다른 값으로 설정하면 애니메이션 시작 시 룰렛이 설정한 각도만큼 돌아간 상태에서 시작됩니다.
      • 회전 각도는 룰렛이 어느만큼 회전하는지를 설정합니다.
        • 0도를 기준으로 360도는 한바퀴입니다.
      • duration은 애니메이션 지속 시간으로 회전 각도와 애니메이션 시간에 따라 회전 속도가 달라집니다.
        • 예로 회전 각도가 360도 이고 duration이 1000(1초)일 때 애니메이션을 실행하면 1초동안 360도 회전합니다.
      • fillAfter를 true로 설정하면 애니메이션이 종료된 상태를 유지합니다.

 

2. 테스트를 위한 레이아웃 설정

  • 테스트를 위해 메인 레이아웃에 룰렛과 회전 버튼을 추가해줍니다.(DataBinding 사용)
<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <variable
            name="activity"
            type="com.jhdroid.roulette.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <com.jhdroid.roulette.Roulette
            android:id="@+id/roulette"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            app:layout_constraintBottom_toTopOf="@+id/rotate_btn"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

        <com.google.android.material.button.MaterialButton
            android:id="@+id/rotate_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="rotate"
            android:onClick="@{() -> activity.rotateRoulette()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  • 제가 사용한 이미지 두 개는 적당한 것으로 변경해서 사용하면 됩니다.

 

3. MainActivity 수정

  • 레이아웃을 연결(DataBindingUtil)해주고 회전 함수를 사용할 함수를 생성합니다.
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.jhdroid.roulette.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.activity = this
    }

    fun rotateRoulette() {
        val toDegrees = (2000..10000).random().toFloat()
        binding.roulette.rotateRoulette(toDegrees, 3000)
    }
}
  • 2000~10000도 중 랜덤한 회전 각도로 3초동안 회전하는 예제 코드입니다.

 

4. 결과

결과

 

 

다음 글은 룰렛의 회전 결과(12시 방향에 오는 데이터)를 리턴해주는 방법입니다.

 

* 글에 틀린 부분이 있으면 댓글 부탁드립니다 :D

728x90