13 장. 커스텀뷰개발
API 에서제공하는뷰를그대로이용하면서약간변형시킨뷰 여러뷰를합쳐서한번에출력하기위한뷰 기존 API 에전혀존재하지않는뷰 public class MyView extends TextView { public class MyView extends ViewGroup { public class MyView extends View {
커스텀뷰를레이아웃 XML 에등록해서이용하려면생성자 3 개를모두정의 public class MyView extends View { Context context; public MyView(Context context) { super(context); public MyView(Context context, AttributeSet attrs) { super(context, attrs); public MyView(Context context, AttributeSet attrs, int defstyleattr) { super(context, attrs, defstyleattr); ondraw( ) 함수 이함수에서그린내용이뷰영역에출력
protected void ondraw(canvas canvas) { canvas.drawcolor(color.alpha(color.cyan)); RectF rect = new RectF(15, 15, 160, 160); Paint paint = new Paint(); paint.setantialias(true); paint.setcolor(color.red); canvas.drawarc(rect, 0, 360, false, paint); 커스텀뷰를레이아웃 XML 에등록할때는클래스명만등록하면안되고, 전체패키지명으로등록 <com.example.test4_13.myview android:layout_width="wrap_content" android:layout_height="wrap_content" /> 커스텀속성이용 res/values 폴더하위에 attrs.xml 파일을이용하며, <declare-styleable> 태그로속성을등록 <resources> <declare-styleable name="myview"> <attr name="customcolor" format="color"/> </declare-styleable> </resources>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.test4_13.myview android:layout_width="wrap_content" android:layout_height="wrap_content" custom:customcolor="#0000ff" /> </RelativeLayout> AttributeSet 을이용하여속성값을추출 public MyView(Context context, AttributeSet attrs) { super(context, attrs); this.context=context; if(attrs!= null){ TypedArray a = context.obtainstyledattributes(attrs, R.styleable.MyView); color=a.getcolor(r.styleable.myview_customcolor, Color.RED);
속성값을획득하는함수 int getattributecount(): 속성개수 String getattributename(int index): 속성명획득 String getattributevalue(int index): 속성값획득 int getattributeintvalue(int index, int defaultvalue): 속성값획득 boolean getattributebooleanvalue(int index, boolean defaultvalue): 속성값획득 float getattributefloatvalue(int index, float defaultvalue): 속성값획득 for (int i=0;i<attrs.getattributecount();i++) { attributes[i]=attrs.getattributename(i)+"="+attrs.getattributevalue(i);
크기결정 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.test4_13.myview android:id="@+id/myview" android:layout_width="wrap_content" android:layout_height="wrap_content" custom:customcolor="#0000ff" android:background="#ff0000" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="world"/> </LinearLayout> 뷰내부에서크기결정을위해 onmeasure( ) 함수를이용 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); setmeasureddimension(500, 500);
레이아웃 XML 파일의크기설정정보는 onmeasure( ) 함수의매개변수로전달 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { int widthmode = MeasureSpec.getMode(widthMeasureSpec); int widthsize = MeasureSpec.getSize(widthMeasureSpec); int heightmode = MeasureSpec.getMode(heightMeasureSpec); int heightsize = MeasureSpec.getSize(heightMeasureSpec); //... MeasureSpec.AT_MOST: 뷰내부에서지정하라는의미. 레이아웃 XML 에서 wrap_content 로선언한경우 MeasureSpec.EXACTLY: 뷰를이용하는액티비티쪽에서크기를결정한경우. 레이아웃 XML 에서 fill_parent, match_parent, 100px 등으로선언한경우 MeasureSpec.UNSPECIFIED: 모드가설정되지않았을경우
이벤트추가 public interface OnMyChangeListener { void onchange(int value); 뷰내부에 setonmychangelistener( ) 함수를호출하여객체를등록 public class MyPlusMinusView extends View { //... //Observer 를등록하기위한객체 ArrayList<OnMyChangeListener> listeners; //Observer 등록을위한함수 public void setonmychangelistener(onmychangelistener listener){ listeners.add(listener);
뷰에서이벤트를처리하기위한함수를재정의 public boolean ontouchevent(motionevent event) { //... // 데이터변경 value++; // 화면갱신 invalidate(); for(onmychangelistener listener : listeners){ //observer 에데이터전달 listener.onchange(value); //...
커스텀뷰를이용하는액티비티의코드 public class MainActivity extends AppCompatActivity implements OnMyChangeListener{ @Override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); MyPlusMinusView plusminusview= (MyPlusMinusView)findViewById(R.id.customView); // 인터페이스를구현한객체를 View 에등록 plusminusview.setonmychangelistener(this); @Override public void onchange(int value) { //...
Step by Step 13-1 Custom View Custom View 를이용하는실습 플러스, 마이너스아이콘으로숫자데이터를발생데이터를 Activity 에전달해서이용하게하는 Custom View 1. Module 생성 2. 파일복사 3. attrs.xml 생성 4. OnMyChangeListener 인터페이스생성 5. MyPlusMinusView 작성 6. activity_main.xml 작성 7. MainActivity 추가 8. 실행
13.2 그래픽프로그램 13.2.1. 뷰를그리는방법 Canvas: 그래픽함수를제공해주는클래스. 이클래스의함수를이용하여뷰의화면을그림 Paint: 그리기옵션지정. 색상, 투명도등의속성을지정 protected void ondraw(canvas canvas) { Paint paint=new Paint(); paint.setcolor(color.red); canvas.drawcircle(50, 50, 50, paint); drawpoint(float x, float y, Paint paint): 점을하나찍는함수. drawline(float startx, float starty, float stopx, float stopy, Paint paint): 선을그리는함수. drawrect(float left, float top, float right, float bottom, Paint paint): 사각형을그리는함수. drawrect(rectf rect, Paint paint): 사각형을그리는함수. drawcircle(float cx, float cy, float radius, Paint paint): 원을그리는함수. 원점에대한 x, y 좌푯값과원의반경정보로원을그림
13.2 그래픽프로그램 drawarc(rectf oval, float startangle, float sweepangle, boolean usecenter, Paint paint): 아크 (Arc) 를그리는함수. drawtext(string text, float x, float y, Paint paint): 문자열을지정된좌표에그리는함수 drawbitmap(bitmap bitmap, float left, float top, Paint paint): 이미지를지정된위치에그리는함수 drawroundrect(rectf rect, float rx, float ry, Paint paint): 모서리가둥근사각형을그리는함수 drawoval(rectf oval, Paint paint): 타원을그리는함수 Paint paint=new Paint(); paint.setcolor(color.red); paint.setstrokewidth(10); canvas.drawline(100, 10, 100, 210, paint); canvas.drawline(10, 110, 210, 110, paint); paint.setstyle(paint.style.stroke); RectF rect=new RectF(300, 10, 400, 200); canvas.drawrect(rect, paint);
13.2 그래픽프로그램 paint.setstyle(paint.style.stroke); RectF arcrect=new RectF(10, 10, 300, 300); canvas.drawarc(arcrect, 0, 90, true, paint); canvas.drawarc(arcrect, -90, -90, false, paint); RectF roundrect=new RectF(10,10,300,300); canvas.drawroundrect(roundrect, 20, 40, paint); RectF ovalrect=new RectF(10,350,300,500); canvas.drawoval(ovalrect, paint); 뷰의영역전체를지정된색으로칠하는함수 drawrgb(int r, int g, int b) drawcolor(int color) drawpaint(paint paint)
13.2 그래픽프로그램 13.2.2. Paint 클래스 그리기효과를지정하기위한클래스 setcolor(int color) setargb(int a, int r, int g, int b) setantialias(boolean aa) setstyle(paint.style style) setstrokewidth(float width) setstrokecap(paint.cap cap) setstrokejoin(paint.join join) paint.setstyle(paint.style.stroke); paint.setstrokewidth(30); canvas.drawline(10, 20, 200, 20, paint); paint.setstrokecap(paint.cap.round); canvas.drawline(10, 60, 200, 60, paint); paint.setstrokecap(paint.cap.square); canvas.drawline(10, 100, 200, 100, paint);
13.2 그래픽프로그램 13.2.3. 코드에서논리적크기획득 자바코드에서개발자가직접크기를명시할때는논리적단위를사용할수없으며, 오직픽셀단위로만적용 Paint paint=new Paint(); paint.setcolor(color.red); paint.setstyle(paint.style.stroke); paint.setstrokewidth(30); Rect rect=new Rect(10, 10, 540, 300); canvas.drawrect(rect,paint); DisplayMetrics 객체를이용해서스마트폰의크기정보를획득한다음, 자바코드에서스마트폰크기호환성을고려한크기계산
13.2 그래픽프로그램 DisplayMetrics dm=getresources().getdisplaymetrics(); float strokewidth=15*dm.density; int rectwidth=(int)(150*dm.density); int rectheight=(int)(150*dm.density); int position=(int)(10*dm.density); Paint paint=new Paint(); paint.setcolor(color.red); paint.setstyle(paint.style.stroke); paint.setstrokewidth(strokewidth); Rect rect=new Rect(position, position, rectwidth, rectheight); canvas.drawrect(rect,paint); 크기를리소스로등록하여이용하는방법 <dimen name="strokewidth">15dp</dimen> <dimen name="size">150dp</dimen> <dimen name="position">10dp</dimen> int size = context.getresources().getdimensionpixelsize(r.dimen.size); int strokewidth= context.getresources().getdimensionpixelsize(r.dimen.strokewidth); int position= context.getresources().getdimensionpixelsize(r.dimen.position);