Skip to content

Latest commit

ย 

History

History
156 lines (123 loc) ยท 6.83 KB

summary.md

File metadata and controls

156 lines (123 loc) ยท 6.83 KB

CustomView

CustomView๋ž€?

์•ˆ๋“œ๋กœ์ด๋“œ ๊ธฐ๋ณธ ์œ„์ ฏ ์œ„์ ฏ ๋˜๋Š” ๋ ˆ์ด์•„์›ƒ์ด ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ง€์•Š๋Š”๋‹ค๋ฉด View ์„œ๋ธŒํด๋ž˜์Šค๋ฅผ ์ง์ ‘๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
๊ธฐ์กด ์œ„์ ฏ ๋˜๋Š” ๋ ˆ์ด์•„์›ƒ์„ ์•ฝ๊ฐ„๋งŒ ์กฐ์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์œ„์ ฏ ๋˜๋Š” ๋ ˆ์ด์•„์›ƒ์„ ์„œ๋ธŒํด๋ž˜์Šค๋กœ ๋งŒ๋“ค๊ณ  ๊ทธ ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

CustomView ์ƒ์„ฑํ•˜๊ธฐ

  1. View or Layout์„ ํ™•์žฅํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค
  2. XML์—์„œ ์†์„ฑ๊ณผ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ ๋‹ค
  3. ์Šˆํผํด๋ž˜์Šค์˜ ์ผ๋ถ€ ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•œ๋‹ค
    • onDraw()
    • onMeasure()
    • onLayout()
    • onSizeChanged()
    • ํ•„์š”ํ•œ ๋‹ค๋ฅธ on.. ๋ฉ”์†Œ๋“œ ์žฌ์ •์˜
  4. ์ƒˆ๋กœ ์ž‘์„ฑํ•œ ํ™•์žฅ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค

CustomView๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ViewLifeCycle ์•Œ์•„๋ณด๊ธฐ

View๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ParentView์— addView๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด onAttachToWindow() ๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ , ํฌ๊ธฐ๋ฅผ ๊ฒฐ์ •ํ•˜๊ณ , ๋ ˆ์ด์•„์›ƒ์„ ๊ทธ๋ฆฌ๊ณ , Canvas์œ„์— ๊ทธ๋ฆฌ๋Š” onDraw()๊นŒ์ง€ ํ˜ธ์ถœ๋œ๋‹ค.

image

CustomView ์ž‘์„ฑํ•˜๊ธฐ

Constructor

๋ชจ๋“  CustomView๋Š” ์ƒ์„ฑ์ž์—์„œ ์ถœ๋ฐœํ•œ๋‹ค.
์ƒ์„ฑ์ž์—์„œ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , default๊ฐ’ ๋“ฑ์„ ์„ค์ •ํ•œ๋‹ค.
View๋Š” ์ดˆ๊ธฐ ์„ค์ •์„ ์‰ฝ๊ฒŒ ์„ธํŒ…ํ•˜๊ธฐ์œ„ํ•ด AttributeSet์ด๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง€์›ํ•œ๋‹ค.
attrs.xmlํŒŒ์ผ(res/valeus/attrs.xml)์„ ๋งŒ๋“ค์–ด ์ด๊ฒƒ์„ ๋ถ€๋ฆ„์œผ๋กœ์„œ ๋ทฐ์˜ ์„ค์ •๊ฐ’์„ ์‰ฝ๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

attrs.xml ํŒŒ์ผ์— ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฆฌ์†Œ์Šค ์ž‘์„ฑ
image
CustomView์— ์†์„ฑ์„ ์ „๋‹ฌํ•˜๋ฉด ์ƒ์„ฑ์ž๋กœ ์ „๋‹ฌํ•œ๋‹ค
image
์ƒ์„ฑ์ž์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•œ ์†์„ฑ์„ ๋ถˆ๋Ÿฌ์˜จ๋‹ค

 constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0){
        val strokeWidth = context.obtainStyledAttributes(attrs, R.styleable.NewAttr)
            .getDimensionPixelSize(
                R.styleable.NewAttr_strokeWidth,
                context.resources.getDimensionPixelSize(R.dimen._2dp)
            )

        val color = context.obtainStyledAttributes(attrs, R.styleable.NewAttr)
            .getColor(R.styleable.NewAttr_strokeColor, Color.WHITE)
    }

onAttachToWindow()

Parent View๊ฐ€ addView(childView)๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋‚˜์„œ ํ˜ธ์ถœ

onMeasure() - ๋ทฐ์˜ ํฌ๊ธฐ ์ธก์ •

View๋Š” layout์•ˆ์—์„œ ๊ฐ๊ฐ ์ž์‹ ์˜ width๋‚˜ height์„ ๊ฐ€์ง„๋‹ค.
์ž์‹ ์˜ width, height๋ฅผ widthMeasureSpec(๋ถ€๋ชจ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ •ํ•œ ๊ฐ€๋กœ), heightMeasureSpec(๋ถ€๋ชจ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ •ํ•œ ์„ธ๋กœ)๋ผ ํ•œ๋‹ค.

  • measure(widthMeasureSpec: Int, heightMeasureSpec: Int) ํ˜ธ์ถœ
    • ๋ถ€๋ชจ๋…ธ๋“œ์—์„œ ์ž์‹๋…ธ๋“œ๋ฅผ ๊ฒฝ์œ ํ•˜๋ฉฐ ์‹คํ–‰๋˜๋ฉฐ, View์˜ ํฌ๊ธฐ๋ฅผ ์•Œ์•„๋‚ด๊ธฐ์œ„ํ•ด ํ˜ธ์ถœ
    • ์‹ค์ œ View์˜ ํฌ๊ธฐ๋ฅผ ์ธก์ •ํ•˜๋Š”๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ, ์‹ค์ œํฌ๊ธฐ๋Š” onMeasure(int,int)์—์„œ ์ธก์ •ํ•œ๋‹ค
  • onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ View์˜ ํฌ๊ธฐ๋ฅผ ์•Œ์•„๋‚ธ๋‹ค

onMeasure ํ•จ์ˆ˜ ์•ˆ์—์„œ

  • View๊ฐ€ ์›ํ•˜๋Š” ์‚ฌ์ด์ฆˆ๋ฅผ ๊ณ„์‚ฐ
  • MeasureSpec ์— ๋”ฐ๋ผ mode ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค
  • MeasureSpec์˜ mode๋ฅผ ์ฒดํฌํ•˜์—ฌ ๋ทฐ์˜ ํฌ๊ธฐ๋ฅผ ์ ์šฉ
 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)

        val width = when (widthMode) {
            MeasureSpec.EXACTLY -> widthSize
            MeasureSpec.AT_MOST -> (paddingLeft + paddingRight + suggestedMinimumWidth).coerceAtMost(widthSize)
            else -> widthMeasureSpec
        }

        val height = when (heightMode) {
            MeasureSpec.EXACTLY -> heightSize
            MeasureSpec.AT_MOST -> (paddingTop + paddingBottom + suggestedMinimumHeight).coerceAtMost(heightSize)
            else -> heightMeasureSpec
        }

        setMeasuredDimension(width, height)
    }

MeasureSpec.AT_MOST : wrap_content ์— ๋งคํ•‘๋˜๋ฉฐ ๋ทฐ ๋‚ด๋ถ€์˜ ํฌ๊ธฐ์— ๋”ฐ๋ผ ํฌ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค
MeasureSpec.EXACTLY : fill_parent, match_parent ๋กœ ์™ธ๋ถ€์—์„œ ๋ฏธ๋ฆฌ ํฌ๊ธฐ๊ฐ€ ์ง€์ •๋œ๋‹ค
MeasureSpec.UNSPECIFIED : Mode ๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ. ์†Œ์Šค์ƒ์—์„œ ์‚ฌ์ด์ฆˆ๋ฅผ ์ง์ ‘ ๋„ฃ์—ˆ์„ ๋•Œ ์‚ฌ์šฉ

onLayout() - ๋ทฐ์˜ ์œ„์น˜์™€ ํฌ๊ธฐ๋ฅผ ํ• ๋‹น

  • layout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) ํ˜ธ์ถœ
    • ๋ถ€๋ชจ์—์„œ ์ž์‹๋…ธ๋“œ๋ฅผ ๊ฒฝ์œ ํ• ๋•Œ ์‹คํ–‰๋˜๋ฉฐ, View์™€ Child View๋“ค์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ํ• ๋‹นํ•œ๋‹ค
    • measure(int,int)์— ์˜ํ•ด ๊ฐ ๋ทฐ์— ์ €์žฅ๋œ ํฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ„์น˜๋ฅผ ์ง€์ •ํ•œ๋‹ค
    • CustomView๊ฐ€ ParentView์ผ๋•Œ ์ฃผ๋กœ ์“ฐ์ธ๋‹ค.
    • ๋„˜์–ด์˜ค๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋„˜์–ด์˜จ๋‹ค (์ฃผ์˜)

onDraw() - ๋ทฐ ๊ทธ๋ฆฌ๊ธฐ

์‹ค์ œ๋กœ ๋ทฐ๋ฅผ ๊ทธ๋ฆฌ๋Š” ๋‹จ๊ณ„์ด๋‹ค.

onDraw() ํ•จ์ˆ˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์›ํ•˜๋Š”๋Œ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” Canvas๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
onDraw() ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๊ณ , Canvas์— ๊ทธ๋ฆฌ๊ณ ์‹ถ์€ ์• ์šฉ์„ ๊ทธ๋ฆฌ๋ฉด ๋œ๋‹ค.

Scroll ๋˜๋Š” Swipe๋ฅผ ํ• ๋•Œ onDraw()๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋Š”๋ฐ onDraw()ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ ๋น„์šฉ์ด ํฌ๋‹ˆ, ํ•œ๋ฒˆ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ์žฌํ™œ์šฉํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š”๊ฒƒ์ด ์ข‹๋‹ค.

 override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        val width = measuredWidth + 0.0f
        val height = measuredHeight + 0.0f

        val circle = Paint()
        circle.color = this.lineColor
        circle.strokeWidth = 10f
        circle.isAntiAlias = false
        circle.style = Paint.Style.STROKE

        canvas?.drawArc(
            RectF(
                10f, 10f, width - 10f, height - 10f
            ), -90f,
            (this.curValue + 0.0f) / (this.maxValue + 0.0f) * 360, false, circle
        )

        val textp = Paint()
        textp.color = Color.BLACK
        textp.textSize = 30f
        textp.textAlign = Paint.Align.CENTER


        if (System.currentTimeMillis() / 1000 % 2 == 0L) {
            canvas?.drawText(
                "${this.curValue} / ${this.maxValue}",
                (width / 2),
                (height / 2),
                textp
            )
        }
    }

invalidate()

View์˜ text ๋˜๋Š” color๋ณ€๊ฒฝ ๋“ฑ ๋‹จ์ˆœํžˆ View๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆด๋•Œ๋‚˜, touch interaction๋“ฑ์ด ๋ฐœ์ƒํ•  ๋•Œ onDraw()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ View๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆฐ๋‹ค.

requestLayout()

measure()๋ถ€ํ„ฐ ํ˜ธ์ถœํ•˜์—ฌ ๋‹ค์‹œ View๋ฅผ ๊ทธ๋ฆฐ๋‹ค.
๋ทฐ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„๋•Œ, ๊ทธ๊ฒƒ์„ ๋‹ค์‹œ ์ธก์ •ํ•ด์•ผ๋  ๋•Œ ํ˜ธ์ถœํ•œ๋‹ค.