Compose를 사용하기 시작하면서 이전에 커스터마이징이 필요했던 뷰가 수십, 수백 줄의 코드로 레이아웃 속성을 커스터마이징해야 하고, View를 상속해야 하고, Paint를 사용해야 한다는 것을 알게 되었습니다. 하나의 방법으로 20줄을 직접 사용할 수 있습니다.
아래 그림과 같이 사용자 지정 호 진행률 표시줄을 구현하려면:
Kotlin을 사용하여 구현
초기에는 이렇게 작은 데모
완료 코드를 작성했습니다: https://github.com/yaoxiawen/CircleProgressBar
사용자 정의 레이아웃 속성
attrs.xml에서 필수 속성 정의(만들지 않은 경우)
<!--圆环形进度条-->
<declare-styleable name="CircleProgressBar">
<attr name="ringMax" format="integer" />
<attr name="progress" format="integer" />
<attr name="startAngle" format="float" />
<attr name="endAngle" format="float" />
<attr name="reverse" format="boolean" />
<attr name="roundCap" format="boolean" />
<attr name="ringWidth" format="dimension" />
<attr name="ringColor" format="color" />
<attr name="ringBackgroundColor" format="color" />
</declare-styleable>
사용자 지정 보기
보기를 상속할 CircleProgressBar 만들기
class CircleProgressBar @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)
AttributeSet을 통해 속성 구문 분석
생성자에서 AttributeSet 매개변수를 통해 레이아웃에 전달된 속성 값을 구문 분석하고 모든 속성에는 기본값이 있어야 합니다.
val ta = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar)
max = ta.getInt(R.styleable.CircleProgressBar_ringMax, DEFAULT_MAX)
if (max <= 0) {
max = DEFAULT_MAX
}
startAngle = ta.getFloat(R.styleable.CircleProgressBar_startAngle, DEFAULT_START_ANGLE)
endAngle = ta.getFloat(R.styleable.CircleProgressBar_endAngle, DEFAULT_END_ANGLE)
reverse = ta.getBoolean(R.styleable.CircleProgressBar_reverse, DEFAULT_REVERSE)
roundCap = ta.getBoolean(R.styleable.CircleProgressBar_roundCap, DEFAULT_ROUND_CAP)
progress = ta.getInt(R.styleable.CircleProgressBar_progress, DEFAULT_PROGRESS)
ringWidth =
ta.getDimension(R.styleable.CircleProgressBar_ringWidth, DEFAULT_RING_WIDTH)
ringColor = ta.getColor(R.styleable.CircleProgressBar_ringColor, DEFAULT_RING_COLOR)
ringBackgroungColor = ta.getColor(
R.styleable.CircleProgressBar_ringBackgroundColor,
DEFAULT_RING_BACKGROUND_COLOR
)
ta.recycle()
페인트 초기화
사용자 지정 보기는 그리기 위해 캔버스를 사용하며 자연스럽게 그림판이 필요합니다.
mArcPaint = Paint()
mArcPaint.isAntiAlias = true
onDraw에서 호 그리기
캔버스의 drawArc를 사용하여 호를 그리고 Paint에서 strokeCap을 Paint.Cap.ROUND로 설정하여 둥근 캡 효과를 그립니다.
with(mArcPaint) {
color = ringBackgroungColor
style = Paint.Style.STROKE
strokeWidth = ringWidth
if (roundCap) {
strokeCap = Paint.Cap.ROUND
}
}
//绘制圆环背景
canvas.drawArc(mRectF, startAngle, endAngle - startAngle, false, mArcPaint)
with(mArcPaint) {
color = ringColor
if (roundCap) {
strokeCap = Paint.Cap.ROUND
}
}
var sweepAngle = progress.toFloat() / max * (endAngle - startAngle)
if (reverse) {
sweepAngle = -sweepAngle //逆时针滚动
}
canvas.drawArc(mRectF, startAngle, sweepAngle, false, mArcPaint)
에서 사용되는 xml 레이아웃
<com.example.circleprogressbar.CircleProgressBar
android:layout_width="200dp"
android:layout_height="200dp"
app:progress="30"
app:startAngle="-225"
app:endAngle="45"
app:ringWidth="10dp" />
Compose를 사용하여 구현
일부 유사한 API의 경우 Canvas의 drawArc를 사용하여 호를 그리고 cap = StrokeCap.Round를 설정하여 둥근 캡 효과를 얻습니다.
@Composable
fun CircleProgress(
modifier: Modifier = Modifier,
progress: Int,
startAngle: Float,
endAngle: Float,
progressBgColor: Color,
progressColor: Color,
) {
Canvas(
modifier = modifier,
onDraw = {
drawArc(
color = progressBgColor,
startAngle = startAngle,
sweepAngle = endAngle - startAngle,
useCenter = false,
style = Stroke(size.width / 11, cap = StrokeCap.Round)
)
drawArc(
color = progressColor,
startAngle = startAngle,
sweepAngle = (endAngle - startAngle) * progress / 100,
useCenter = false,
style = Stroke(size.width / 11, cap = StrokeCap.Round)
)
}
)
}
메소드를 호출하여 일부 해당 매개변수를 전달하고 매개변수를 기본값으로 직접 설정할 수도 있습니다.
CircleProgress(
modifier = Modifier.padding(5.dp).size(66.dp),
startAngle = -225f,
endAngle = 45f,
progress = 30,
progressBgColor = Color.LightGray,
progressColor = Color.Blue
)