본문 바로가기

Programming/Android

CustomView

// Start MyView.java

---------------------------------------------------------------------------------------------------------------------------

package kr.or.ddit.excustom;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

// View 상속 -  View 는 처음에 크기가 정해지지 않음...
public class MyView extends View {
 
 private int mode;
 
 public void setMode(int mode) {
  this.mode = mode;
 }

 // xml에서 MyView 클래스(custom class)를 사용하고 싶으면
 // 인자 2개, 3개짜리 생성자가 존재해야한다.
 public MyView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  // TODO Auto-generated constructor stub
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 }

 public MyView(Context context) {
  super(context);
  
 }
 
 // 이 뷰의 크기가 처음 결정되거나, 이후 변경될 때 실행
 // 뷰의 크기를 판단하여 어떤 작업을 수행해야 하는 경우,
 // 이 메서드에서 혹은 이 메서드 실행 후에 판단해야 한다.
 @Override      // 현 너비, 현 높이, 변경전 너비, 변경전 높이
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  Log.d("ddit", "MyView 크기 : onCreate : " + w + ", " + h);
  super.onSizeChanged(w, h, oldw, oldh);
 }

 // 이 뷰가 화면에 그려져야할 필요가 있을 때 호출되는 메서드.
 // 인자로 전달된 Canvas 객체의 drawXXXX() 메서드들로 그림을 그리면,
 // 그 그림이 이 뷰(이 표면)에 출력된다.
 @Override
 protected void onDraw(Canvas canvas) {
  Paint pnt = new Paint(); // 붓과 물감 역할을 하는 객체
  
  switch (mode) {
  case 1:
   canvas.drawColor(Color.RED); // 영역 전체를 지정된 색으로 색칠
   // 상수에 없는 색은 canvas.drawColor(Color.argb(alpha, red, green, blue)())메서드로 조합
    
   pnt.setColor(Color.WHITE);
   canvas.drawPoint(10, 10, pnt); // 10, 10 에 점 찍기
   canvas.drawLine(20, 10, 200, 50, pnt); // 20, 10~200, 50 직선 그리기
   canvas.drawCircle(100, 90, 50, pnt); // 중심이 100,90이고 반지름이 50인 원 그리기
   
   pnt.setColor(Color.YELLOW);
   pnt.setAlpha(100); // 투명도
   canvas.drawRect(10, 120, 200, 220, pnt);
   // 10, 120 과  200, 220을 대각선 꼭짓점으로 하는 직사각형 그리기
   
   pnt.setColor(Color.BLACK);
   pnt.setAlpha(255);
   pnt.setTextSize(30); // 글자 크기
   // Paint 객체의 set메서드로 글자크기, 글자체, 밑줄, 장평, 기울임 등 설정 가능
   canvas.drawText("Hello world~*", 10, 250, pnt); // 글씨쓰기  
   break;

  case 2: 
   pnt.setColor(Color.WHITE);
   canvas.drawPaint(pnt); // 지정된 페인트로 정체영열을 색칠
   pnt.setColor(Color.CYAN);
   RectF r = new RectF(10,10,100,100); // 10, 10~100, 100 직사각형 정
   canvas.drawRoundRect(r, 15, 15, pnt); // 모서리가 둥근 사각
   r.set(100, 10, 150, 100);
   canvas.drawOval(r, pnt); // 지정된 사각형에 접하는 타원 그리기
   r.set(10, 110, 100, 200);
   canvas.drawArc(r, 10, 150, true, pnt); // 원호 (부채꼴)
   r.set(110, 110, 200, 200);
   canvas.drawArc(r, 10, 150, false, pnt); // 원호 (반달)   
   break;
  
  case 3:
   canvas.drawColor(Color.LTGRAY);
   pnt.setColor(Color.BLACK);
   pnt.setStrokeWidth(15); // 선두께
   pnt.setStrokeCap(Paint.Cap.BUTT); // 선 끝모양
   canvas.drawLine(10, 10, 200, 10, pnt);
   pnt.setStrokeCap(Paint.Cap.ROUND); // 선 끝모양
   canvas.drawLine(10, 30, 200, 30, pnt);
   pnt.setStrokeCap(Paint.Cap.SQUARE); // 선 끝모양
   canvas.drawLine(10, 50, 200, 50, pnt);
   
   pnt.setStrokeWidth(5); // 선두께
   pnt.setStyle(Paint.Style.FILL); //도형 내부 색칠 여부
   canvas.drawCircle(30, 180, 20, pnt);
   pnt.setStyle(Paint.Style.FILL_AND_STROKE); //도형 내부 색칠 여부
   canvas.drawCircle(80, 180, 20, pnt);
   pnt.setStyle(Paint.Style.STROKE); //도형 내부 색칠 여부
   canvas.drawCircle(130, 180, 20, pnt);  
   break;
   
  case 4:
   canvas.drawColor(Color.BLACK);
   pnt.setStrokeWidth(5);
   pnt.setColor(Color.YELLOW);
   pnt.setStyle(Paint.Style.STROKE);
   
   Path path = new Path(); // 경로 정보를 저장하는 객체
   path.addRect(100, 0, 150, 90, Path.Direction.CW);  // 그리는 방향 : CW - 시계방향
   path.addCircle(50, 50, 40, Path.Direction.CCW); // CCW - 반시계 방향
   canvas.drawPath(path, pnt);
   
   path.reset(); // 초기화(저장된 내용 모두 삭제)
   path.moveTo(10, 110); // 기준점을 이동
   // 기본적으로 path에서 기준점은 마지막 경로의 마지막 지점이다.
   path.lineTo(50, 150); // 10, 110~50, 150 직선 경로
   path.rLineTo(50, -30); // 50, 150~100, 120 직선경로  
   // r이 붙은 메서드는 기준점(시작점)으로부터의 상대좌표(상대경로) 사용
   path.quadTo(120, 170, 200, 110); // 2차베지어곡선 경로
   // 100, 120에서 출발하여 120, 170에 근접하고, 200, 110에서 끝나는 2차 베지어 곡선
   canvas.drawPath(path, pnt);
   
   path.reset();
   path.moveTo(10, 220);
   path.cubicTo(80, 150, 150, 220, 220, 180);
   // 10,220 에서 시작하고 80, 150과 150, 220에 근접하고 220, 180에서 끝나는 3차 베지어 곡선
   //canvas.drawPath(path, pnt);
   
   pnt.setTextSize(20);
   pnt.setStyle(Paint.Style.FILL);
   canvas.drawTextOnPath("경로 위에 글씨를 쓰자~", path, 0, 0, pnt);
   //       path위 글자 시작위치, path와 글자와의 사이
   //canvas.drawTextOnPath(text, path, hOffset, vOffset, paint)  
   break;
   
  case 5:
   // Bitmap 생성
   // 1. 이미지 정보가 없는 빈비트맵 객체 또는 다른 비트맵의 사본으로 생성.
   //   Bitmap.createBitmap() 메서드 사용
   // 2. 다양한 이미지소스 (파일, 스트림, 배열, 리소스..) 를 비트맵 객체로 변환
   //   BitmapFactory.decodeXXXX() 메서드 사용
   Bitmap bm =
     BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); 
   // bm의 이미지를 이 뷰의 10,10에 베껴 그리기
   canvas.drawBitmap(bm, 10, 10, null); 
   // bm 이미지의 전체 영역을 이 뷰의 80, 10~110, 60 사각형 영역에 베껴 그리기
   canvas.drawBitmap(bm, null, new Rect(80, 10, 110, 60), null);
   // bm 이미지에서 10,20~40,40 사각형 영역에 해당하는 일부분만 이 뷰의 120,10~200,100 사각형 영역에 베껴 그리기.
   canvas.drawBitmap(bm, new Rect(10, 20, 40, 40), new Rect(120, 10, 200, 100), null);
   
   // Matrix : 이미지의 확대, 축소, 미러링, 이동, 회전 효과를 설정하는 객체.
   Matrix mtx = new Matrix();
   mtx.postScale(1.5f, -0.7f); // x축 방향으로 1.5배, y축 방향으로 0.7배로 크기변경
   // 음수는 미러링을 의미
   Bitmap bm1 = 
     Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), mtx, false);
      // bm 의 전체 영역을 복사하여 새로운 Bitmap 객체를 생성
   
   mtx.postRotate(45); // 45도 회전효과
   Bitmap bm2 = 
     Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), mtx, false);
   
   canvas.drawBitmap(bm1, 10, 250, pnt);
   canvas.drawBitmap(bm2, 100, 250, pnt);
   
   
   // Bitmap : 도화지 역할(실제 픽셀별 색상정보가 저장되는 공간)
   // Canvas : 그림 그리는 도구의 집합체
   // Paint : 붓과 물감 역할
   
   Bitmap backBmp = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888);
   // 점 1개의 색상을 32 비트에 저장하면서 200 X 100 이미지를 저장할 수 있는 비트맵 객체 생성
   Canvas backCvs = new Canvas(backBmp);
   // backCsv 의 drawXXX 메서드로 그림을 그리면,
   // 캔버스와 연결된 비트맵 객체 (backBmp)에 해당 이미지가 저장.(setPixel)
   backCvs.drawColor(Color.DKGRAY);
   pnt.setColor(Color.WHITE);
   for(int x=0; x<200; x+=5){
    backCvs.drawLine(x, 0, 200-x, 100, pnt);
   }
   canvas.drawBitmap(backBmp, 10, 120, null);  
   break;
  }
  
  super.onDraw(canvas);
 }

}

//end MyView.java

---------------------------------------------------------------------------------------------------------------------------

//start ExCustomActivity.java

---------------------------------------------------------------------------------------------------------------------------

 package kr.or.ddit.excustom;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class ExCustomActivity extends Activity implements OnClickListener {

 private MyView mv;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
 
  setContentView(R.layout.activity_ex_custom);

  // xml에서 추가하지 않고 layout에 직접 MyView를 셋팅할떄.
//  MyView mv = new MyView(this);
//  mv.setBackgroundColor(Color.LTGRAY);
//  LinearLayout layout =  (LinearLayout) findViewById(R.id.layout1);
//  layout.addView(mv);
  
//  Log.d("ddit", "MyView 크기 : onCreate : " + mv.getWidth() + ", " + mv.getHeight());

  findViewById(R.id.button1).setOnClickListener(this);
  findViewById(R.id.button2).setOnClickListener(this);
  findViewById(R.id.button3).setOnClickListener(this);
  findViewById(R.id.button4).setOnClickListener(this);
  findViewById(R.id.button5).setOnClickListener(this);
  
  // xml에서 MyView 를 추가할때
  // MyView를 public class로 만들어야 하기 때문에 
  // 클래스파일을 하나 생성한다.
  // 불러올 때 기본 생성자가 자동으로 생김.
  mv = (MyView) findViewById(R.id.myView1); 
  
 }

 @Override
 public void onClick(View v) {
  switch (v.getId()) {
  case R.id.button1:
   // MyView 의 mode 값을 1로 변경
   mv.setMode(1);
   break;
   
  case R.id.button2:
   mv.setMode(2);
   break;
   
  case R.id.button3:
   mv.setMode(3);
   break;
   
  case R.id.button4:
   mv.setMode(4);
   break;
   
  case R.id.button5:
   mv.setMode(5);
   break;
  }
  
  mv.invalidate();
  // 뷰를 다시 그리게(onDraw) 하고 싶을때,
  // 해당 뷰의 invalidate()를 호출하면 된다.
 }
}


// 1. 화면 상단(MyView 위)에 버튼 5개를 배치하고
// 2. 각 버튼을 누르면 해당되는 예제가 MyView 에 그려지도록 구현
//   뷰를 다시 그리도록 하고 싶을 때(onDraw()를 다시 실행시키고 싶을때) 에는
//    onDraw를 직접 호출하지 않고,
//    뷰의 invalidate() 메서드를 호출하면 onDraw()가 다시 호출된다.

---------------------------------------------------------------------------------------------------------------------------

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/layout1"
    tools:context=".ExCustomActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/button1"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ex1" />

        <Button
            android:id="@+id/button2"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ex2" />

        <Button
            android:id="@+id/button3"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ex3" />

        <Button
            android:id="@+id/button4"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ex4" />

        <Button
            android:id="@+id/button5"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ex5" />

    </LinearLayout>

    <kr.or.ddit.excustom.MyView
        android:id="@+id/myView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    
</LinearLayout>

---------------------------------------------------------------------------------------------------------------------------

MyView를 만들고 Custom 뷰에서 만들어 쓰기.

[출처] Custom View|작성자 라데시

'Programming > Android' 카테고리의 다른 글

[안드로이드] 탭 레이아웃(Tab Layout) 사용하기 - Intent 방식1  (0) 2013.06.22
안드로이드에서 화면 캡쳐(코드)  (0) 2013.05.04
구글맵 길찾기  (0) 2012.10.25
SWT / JFACE  (0) 2012.08.10
JNI, NDK  (0) 2012.08.10