์ปค์Šคํ…€๋ทฐ CustomView

์ปค์Šคํ…€๋ทฐ๋ž€

์•ˆ๋“œ๋กœ์ด๋“œ Android์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์œ„์ ฏ๋“ค(TextView, Button, EditText, ListView ๋“ฑ)์ด ์•„๋‹ˆ๋ผ ๋‚ด ํ•„์š”์— ๋งž์ถฐ์„œ ๋งŒ๋“  View๋ฅผ ๋งํ•œ๋‹ค.

์ปค์Šคํ…€๋ทฐ๋ฅผ ๋งŒ๋“œ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•

  1. ์•ˆ๋“œ๋กœ์ด๋“œ ์ œ๊ณต UI๋ฅผ ํ™•์žฅํ•œ๋‹ค.
class TaggableTextView extends TextView {
 ...
}
  1. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ View ๋˜๋Š” ViewGroup์—์„œ๋ถ€ํ„ฐ ํ™•์žฅํ•ด์„œ ์ง์ ‘ ๊ตฌํ˜„ํ•œ๋‹ค.
class TaggableTextView extends View {
 ...
}

1๋ฒˆ์€ ๋Š์Šจํ•œ ์˜๋ฏธ์—์„œ์˜ ์ปค์Šคํ…€๋ทฐ์ด๊ณ  2๋ฒˆ์ด ํƒ€์ดํŠธํ•œ ์˜๋ฏธ์—์„œ์˜ ์ปค์Šคํ…€๋ทฐ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.
์–ด์ฐŒ๋ณด๋ฉด ๋‹น์—ฐํ•˜์ง€๋งŒ 2๋ฒˆ์€ ์„ฑ๋Šฅ์ ์œผ๋กœ ์ƒ๋‹นํžˆ ์ด๋“์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ์ปดํŒฉํŠธํ•˜๊ฒŒ ์ง์ ‘์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์•„์ฃผ ๋‹จ์ˆœํ•ด ๋ณด์ด๋Š” TextView๋„ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ณด๋ฉด ์ƒ๋‹นํžˆ ๋งŽ์€ ๊ฒƒ๋“ค์ด ์ฐจ๋ ค์ ธ ์žˆ๋‹ค.

์š”์ฆ˜ ๋””๋ฐ”์ด์Šค ํ•˜๋“œ์›จ์–ด๊ฐ€ ์›Œ๋‚™ ์ข‹์•„์„œ ์ฒด๊ฐํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์œผ๋‚˜ ๋‚œ ์ตœ๊ทผ์— ๋งˆ์‹œ๋ฉœ๋กœ ๊ธฐ๊ธฐ์—์„œ ์—„์ฒญ๋‚œ ์„ฑ๋Šฅ ์ƒ์Šน์„ ๊ฒฝํ—˜ํ–ˆ๋‹ค!
์—ฌ๋Ÿฌ Child View๊ฐ€ ๋™์ ์œผ๋กœ ๋ถ™์€ UI๋ฅผ ํ•˜๋‚˜์˜ View๋กœ ์ปค์Šคํ…€ํ•˜์—ฌ ๊ตฌํ˜„ํ–ˆ๋”๋‹ˆ ๋”œ๋ ˆ์ด๊ฐ€ 4๋ถ„์˜ 1์ด ๋˜์—ˆ๋‹ค. ๋‚œ ์ด ์ดํ›„๋กœ ์ปค์Šคํ…€๋ทฐ๋ฅผ ์ฐฌ์–‘ํ•˜๊ธฐ๋กœ ํ–ˆ๊ณ  ์•ž์œผ๋กœ View๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ์›ฌ๋งŒํ•˜๋ฉด onDraw์—์„œ ๊ทธ๋ฆด ์ƒ๊ฐ์ด๋‹ค.(๊ธˆ์‚ฌ๋น )

์ด์ œ๋ถ€ํ„ฐ ์ด ๊ธ€์˜ '์ปค์Šคํ…€๋ทฐ'๋Š” 2๋ฒˆ์˜ ์ปค์Šคํ…€๋ทฐ๋ฅผ ์ง€์นญํ•œ๋‹ค.

View์˜ LifeCycle

Pasted image 20221125175757.png
๊ฐœ๋ฐœ์ž๊ฐ€ override์—์„œ ์“ฐ๋Š” ๊ฑด on์ด ๋ถ™์–ด ์žˆ๋Š” ๊ฒƒ๋“ค์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

  • onAttachedToWindow() : View๊ฐ€ Window์— ์—ฐ๊ฒฐ๋˜๋ฉด ํ˜ธ์ถœ.
  • onMeasure() : View์˜ ํฌ๊ธฐ๋ฅผ ์ธก์ •ํ•˜๋Š” ๋‹จ๊ณ„
  • onLayout() : View์˜ ์œ„์น˜์™€ ํฌ๊ธฐ๋ฅผ ํ• ๋‹นํ•˜๋Š” ๋‹จ๊ณ„
  • onDraw() : View๋ฅผ ๊ทธ๋ฆฌ๋Š” ๋‹จ๊ณ„

๋ฌด์กฐ๊ฑด ์“ฐ๋Š” ํŒ

  1. drawText()๋ฅผ ์“ธ ๋• Paint์— antiAlias๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธ€์ž๊ฐ€ ๊ณ„๋‹จํ˜„์ƒ์„ ๊ฐ–๊ณ  ๊ทธ๋ ค์ง„๋‹ค.
paintText = new Paint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
  1. ๊ธ€์ž๋ฅผ ๊ฐ€๋กœ ๊ธธ์ด๋ฅผ ์•Œ๊ณ  ์‹ถ์„ ๋•Œ
paintText.measureText(text);
  1. Paint๋‚˜ Rect๋Š” constructor์—์„œ initializeํ•˜์ž.

Avoid object allocations during draw/layout operations (preallocate and reuse instead)

Paint๋ฅผ ์“ฐ๋Š” onDraw์—์„œ ๋ฌด์‹ฌ์ฝ” new Paint๋ฅผ ์‹œ๋„ํ•˜์˜€๋”๋‹ˆ ์ด๋ ‡๊ฒŒ ๊ฒฝ๊ณ ๊ฐ€ ๋‚˜์˜จ๋‹ค.
๋ง ๊ทธ๋Œ€๋กœ onDraw๋‚˜ onLayout์—์„œ ๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•˜์ง€ ๋ง๋ผ๋Š” ๊ฒฝ๊ณ .
constructor์—์„œ initํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

public TaggableTextView(Context context, String text) {  
    super(context);
    paintText = new Paint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);  
    rect = new Rect();  
}
  1. WRAP_CONTENT๊ฐ€ ์•ˆ ๋จนํžŒ๋‹ค?
    ์ด๋ ‡๊ฒŒ ๋งŒ๋“  View์— LayoutParams์„ setํ•˜๋ ค๊ณ  ํ•  ๋•Œ wrap_content๊ฐ€ ์•ˆ ๋จน์„ ์ˆ˜๊ฐ€ ์žˆ๋‹ค.
    View์˜ LifeCycle์„ ๋– ์˜ฌ๋ฆฌ๋ฉด layout์ด draw๋ณด๋‹ค ์•ž์ธ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋‚ด๊ฐ€ draw์— ๋ญ˜ ์–ผ๋งˆ๋‚˜ ๊ทธ๋ ธ๋Š”์ง€๋Š” Layout๋‹จ๊ณ„์—์„œ๋Š” ๋ชจ๋ฅด๋Š” ๊ฒƒ์ด๋‹ค.
    ๋”ฐ๋ผ์„œ Layout ์ „ ๋‹จ๊ณ„(Measure)์—์„œ ์ง์ ‘์ ์œผ๋กœ Param์„ ์žก์•„์ค˜์•ผ ํ•œ๋‹ค.
@Override  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    setMeasuredDimension(widthMeasureSpec, dpToPx(56));  
}

์•„๋‹ˆ๋ฉด setLayoutParams() ํ›„์— requestLayout()๋กœ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ฒ ๋‹ค.
(requestLayout์œผ๋กœ wrap_content๊ฐ€ ๋จนํžˆ๋Š”์ง€ ์‹ค์ œ๋กœ ํ•ด๋ณด์ง„ ์•Š์•˜์ง€๋งŒ View๋ฅผ ์˜ฌ๋ฆด ๋•Œ๋งˆ๋‹ค onDraw๋ฅผ ๋‘ ๋ฒˆ ํƒ€๊ฒŒ ๋˜๋ฏ€๋กœ ํ”ผํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ํ”ผํ•˜๋Š” ๊ฒŒ ๋งž๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.)

#์•ˆ๋“œ๋กœ์ด๋“œ