Foggy day
Android(kotlin) - custom circle progressBar 본문
It is the sample of custom circle progressbar that use customview and valueanimation.
If you want to use circle percent Progress, I recommend this sample.
Additional explanations have been omitted. I left a little explanation in the code. But, It is not English.
If you have any questions, please leave a comment.
Video preview
1. CircleProgress
@SuppressLint("ResourceAsColor")
class CircleProgress : View {
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(
context: Context?,
rectWidth: Float,
rectHeight: Float,
color: Int,
baseColor: Int
) : super(context) {
this.rectWidth = rectWidth
this.rectHeight = rectHeight
this.color = color
this.baseColor = baseColor
}
var rectWidth: Float = 0f
var rectHeight: Float = 0f
var startAngle = 0f
var endAngle = 0f
var baseStartAngle = 0f
var baseEndAngle = 0f
var color = 0
var baseColor = 0
var endPointColor = R.color.brand_blue
var colorInnerPoint = R.color.white
var strokeWidth = 80f
var strokeInnerPointWidth = 40f
var visible: Boolean = false
init {
//View의 배경색 지정
setBackgroundColor(resources.getColor(R.color.white, null))
}
@SuppressLint("ResourceAsColor", "DrawAllocation")
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (!visible) return
val marginValue = strokeWidth / 2
//progress 배경
//left와 top에 strokeWidth/2 값을 준 이유는 stroke의 값 안쪽으로 커지는 것이 아니라
//left와 top을 기준으로 stroke 값 만큼 두께가 양쪽으로 넓어지기 때문에 화면에 짤려 보이기 때문입니다.
val baseRect = RectF(
marginValue,
marginValue,
rectWidth - marginValue,
rectHeight - marginValue
)
val basePaint = Paint()
basePaint.style = Paint.Style.STROKE
basePaint.color = resources.getColor(baseColor, null)
basePaint.strokeCap = Paint.Cap.ROUND
basePaint.strokeWidth = strokeWidth / 2
canvas?.drawArc(baseRect, baseStartAngle, baseEndAngle, false, basePaint)
//progress percent
val rect =
RectF(marginValue, marginValue, rectWidth - marginValue, rectHeight - marginValue)
val paint = Paint()
paint.style = Paint.Style.STROKE
paint.isAntiAlias = true
paint.color = resources.getColor(color, null)
paint.strokeCap = Paint.Cap.ROUND
paint.strokeWidth = strokeWidth
canvas?.drawArc(rect, startAngle, endAngle, false, paint)
//progress의 끝부분에 점 찍기
//큰 원
val radius = rect.width() / 2
val endPointPaint = Paint()
endPointPaint.style = Paint.Style.FILL
endPointPaint.color = resources.getColor(endPointColor, null)
val pointBig: Point =
calculatePointOnArc(rect.centerX(), rect.centerY(), radius, startAngle + endAngle)
canvas?.drawCircle(
pointBig.x.toFloat(),
pointBig.y.toFloat(),
strokeWidth / 2,
endPointPaint
)
//작은 원
endPointPaint.color = resources.getColor(colorInnerPoint, null)
val pointSmall: Point =
calculatePointOnArc(rect.centerX(), rect.centerY(), radius, startAngle + endAngle)
canvas?.drawCircle(
pointSmall.x.toFloat(),
pointSmall.y.toFloat(),
strokeInnerPointWidth / 2 - 10,
endPointPaint
)
}
/**
* 원의 끝 지점의 좌표 구하기
*/
private fun calculatePointOnArc(
centerX: Float,
centerY: Float,
circleRadius: Float,
endAngle: Float
): Point {
val point = Point()
val endAngleRadian: Double = endAngle * (Math.PI / 180)
val pointX: Int = (centerX + circleRadius * cos(endAngleRadian)).roundToInt()
val pointY: Int = (centerY + circleRadius * sin(endAngleRadian)).roundToInt()
Log.d("TAG", "calculatePointOnArc: pointX : $pointX ")
Log.d("TAG", "calculatePointOnArc: pointY : $pointY ")
point.x = pointX
point.y = pointY
return point
}
}
2. CircleProgressAnimation
class CircleProgressAnimation(
private var circleProgress: CircleProgress,
private val startAngle: Int,
private val toAngle: Int,
animDuration: Long
) : Animation() {
init {
duration = animDuration
circleProgress.visible = true
}
//interpolatedTime은 0부터 1까지 증가하는 값
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
super.applyTransformation(interpolatedTime, t)
var angle = (startAngle.toFloat() + ((toAngle - startAngle) *interpolatedTime))
circleProgress.endAngle = angle
circleProgress.requestLayout()
}
}
3. activity_circle_progress_anim.xml
<?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=".animation.progress.circle.CircleProgressAnimActivity">
<FrameLayout
android:id="@+id/progressWrap"
android:layout_marginTop="100dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="100dp"
android:text="실행" />
</LinearLayout>
4. CircleProgressAnimActivity
class CircleProgressAnimActivity : AppCompatActivity() {
lateinit var b: ActivityCircleProgressAnimBinding
var circleWidth = 0f
var circleHeight = 0f
lateinit var progressWrap: FrameLayout
lateinit var animation: Animation
lateinit var circleProgressProgress: CircleProgress
@SuppressLint("WrongViewCast")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
b = ActivityCircleProgressAnimBinding.inflate(layoutInflater)
setContentView(b.root)
b.button.setOnClickListener {
circleProgressProgress.startAnimation(animation)
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
progressWrap = findViewById(R.id.progressWrap)
circleWidth = progressWrap.width.toFloat()
circleHeight = progressWrap.height.toFloat()
Log.d("TAG", "onCreate: 사이즈 circleWidth : $circleWidth")
Log.d("TAG", "onCreate: 사이즈 circleHeight : $circleHeight")
circleProgressProgress = CircleProgress(
this,
circleWidth,
circleHeight,
R.color.brand_purple,
R.color.gray
)
progressWrap.addView(circleProgressProgress)
circleProgressProgress.startAngle = 120f
circleProgressProgress.endAngle = 250f
circleProgressProgress.baseStartAngle = 120f
circleProgressProgress.baseEndAngle = 300f
circleProgressProgress.color = R.color.brand_pink
circleProgressProgress.baseColor = R.color.colorGray500
circleProgressProgress.endPointColor = R.color.brand_green
circleProgressProgress.postInvalidate()
animation = CircleProgressAnimation(
circleProgressProgress,
0,
250,
1000
)
}
}
'Android' 카테고리의 다른 글
Android(Java, kotlin) - Appbar Animation with ViewPager inside Fragment (0) | 2020.12.29 |
---|---|
Android Custom View: Extending The Views (0) | 2020.12.27 |
Android - 최고의 커스텀 뷰 가이드 (0) | 2020.12.27 |
Android(Java) - composite design pattern example (0) | 2020.12.26 |
Android(Kotlin) - FlexibleViewPagerHeight when you want to apply wrapContent to viewPager (0) | 2020.12.26 |