Development/Android

22. Canvas & Paint

궁선이 2018. 5. 5. 01:47

액티비티 위에 그림을 그리고 이미지를 출력하는 기능에 대해서 포스팅 하겠습니다.

기본적으로 두가지의 클래스를 사용합니다.

1. Canvas
점, 선, 원, 사각형 그리기/ 텍스트 쓰기/ 이미지 출력 등의 기능을 제공합니다.

2. Paint
펜 두께지정, 색상 선택, 스타일 선택, 글꼴 선택 등의 기능을 제공합니다.

기본적인 구현 순서는 다음과 같습니다.
1. View 클래스를 상속 받는 클래스 생성
2. OnDraw() 메소드 Overriding
3. Paint 객체 생성하고 Canvas에 그리기
4. onTouchEvent() 메소드 구현
5. 액티비티에 추가하기


Paint 클래스의 메서드에 대한 자세한 내용은 아래 링크를 참고하시기 바랍니다.

마찬 가지로 Canvas에 대한 레퍼런스도 링크로 올리겠습니다.

안드로이드 developer 사이트를 자주 이용하시기 바랍니다.
코드에 메서드 설명을 간략하게 붙이겠습니다.

1. 클래스 생성

View 를 상속받는 클래스를 다음과 같이 생성합니다.

public class MyCanvas extends View { public MyCanvas(Context context) { super(context); } public MyCanvas(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } }
2. 레이아웃에 추가

그림을 그리고자 하는 액티비티의 레이아웃에 위에서 생성한 클래스를 붙여줍니다.

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.mskir.hw11.MainActivity"> // 아래와 같이 레이아웃에 붙여줍니다. <com.example.mskir.hw11.MyCanvas android:id="@+id/canvas" android:layout_width="match_parent" android:layout_height="420dp" /> </LinearLayout>
3. 액티비티에서 객체 생성
public class MainActivity extends AppCompatActivity { MyCanvas myCanvas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myCanvas = (MyCanvas)findViewById(R.id.canvas); }


이제 그림을 그리기 위한 기본적인 준비작업이 끝이 났습니다.
본격적으로 그림을 그려보도록 하겠습니다.

4. 도형 그리기

onDraw() 메서드를 오버라이딩 하면 앱을 실행시
onDraw안에 구현한 그림을 자동으로 그려줍니다.

사각형을 그리는 코드입니다.

public class MyCanvas extends View { public MyCanvas(Context context) { super(context); } public MyCanvas(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); //색상 지정 paint.setStyle(Paint.Style.STROKE); //스타일 paint.setStrokeWidth(5); // 선의 두께 조절 canvas.drawRect(10,10,100,100,paint); // 4개의 좌표를 이어 사각형을 그림 } }

다음은 글자를 쓰는 코드와 채워진 도형을 그리는 코드입니다.
위 코드의 onDraw에 바꿔서 넣어보세요.

Paint paint = new Paint(); //텍스트 쓰는 코드 paint.setStyle(Paint.Style.FILL); paint.setColor(Color.BLUE); paint.setTextSize(30); canvas.drawText("안녕하세요",10,240,paint); //원을 그리는 코드 canvas.drawCircle(150,300,30,paint); //좌표에 따라 선을 이어 도형을 그리고 내부를 채워넣는 코드 paint.setColor(Color.CYAN); Path path = new Path(); path.moveTo(50,400); path.lineTo(50+50,330); path.lineTo(50+100,400); path.lineTo(50+150,330); path.lineTo(50+200,400); canvas.drawPath(path,paint);
5. Bitmap 그리기

BitmapFactory 클래스를 사용합니다.

마찬가지로 위의 onDraw내부에 넣어봅시다.

Paint paint = new Paint(); //일반 비트맵 이미지 Bitmap img = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher); canvas.drawBitmap(img,10,10,paint); //크기가 조정된 비트맵 이미지 Bitmap smallBitmap = Bitmap.createScaledBitmap(img,img.getWidth()/2,img.getHeight()/2,false); canvas.drawBitmap(smallBitmap,10,100,paint);


6. Touch Event

화면에 터치이벤트가 발생할 시의 처리를 통해
이미지나 글씨를 쓰는 기능입니다.

return 값이 false일 경우 touch event가 실행되지 않으니 true로 설정해 주어야 합니다.
아래 코드는 터치에따라 글씨를 쓰게 해주는 코드입니다.

invalidate는 리스트뷰와 비슷하게 TouchEvent에서 그린 그림을 
onDraw를 호출하여 실제 화면에 적용시킵니다.

int startX, startY; @Override public boolean onTouchEvent(MotionEvent event) { int X = (int)event.getX(); // 터치시 해당 좌표값을 얻어온다. int Y = (int)event.getY(); //뷰를 누를때 if(event.getAction() == MotionEvent.ACTION_DOWN) { startX = X; startY = Y; } // 뷰를 누른 후 움직일 때 else if(event.getAction() == MotionEvent.ACTION_MOVE){ if(startX != -1) { mCanvas.drawLine(startX, startY, X, Y, paint); invalidate(); startX = X; startY = Y; } } //뷰에서 손가락을 뗄 때 else if(event.getAction() == MotionEvent.ACTION_UP){ if(startX != -1) { mCanvas.drawLine(startX, startY, X, Y, paint); invalidate(); startX = -1; startY = -1; } } //invalidate() -> onDraw()함수를 호출하여 그림을 적용한다. return true; }
7. 캔버스 회전/이동/확대/기울기
//회전 canvas.rotate(45, this.getWidth()/2, this.getHeight()/2); //이동 canvas.translate(-100, 100); //확대 canvas.scale(1.2f, 1.2f); //기울기 canvas.skew(0.3f, 0.3f);
8. Bluring 효과

생성자에 코드를 추가하여 줍니다.

public MyCanvas(Context context) { super(context); this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);\ }

BlurMaskFilter을 생성하여 Paint에 적용시켜 줍니다.

// INNER,NORMAL,OUTER,SOLID 존재 BlurMaskFilter blur = new BlurMaskFilter(15,BlurMaskFilter.Blur.INNER); paint.setMaskFilter(blur); invalidate();
9. ColorFilter 효과

1. matrix(4 x 5 배열) 생성
2. ColorMatrix생성
3. ColorMatrixColorFilter 생성
4. Paint에 설정

float[] matrixarray = { 2f, 0f, 0f, 0f, -25f, 0f, 2f, 0f, 0f, -25f, 0f, 0f, 2f, 0f, -25f, 0f, 0f, 0f, 1f, 0f, }; ColorMatrix matrix = new ColorMatrix(matrixarray); paint.setColorFilter(new ColorMatrixColorFilter(matrix));
10. 비트맵 객체로 이미지 그리기

메모리에 만들어지는 이미지는 Bitmap 객체로 관리된다.
기존의 이미지를 다시 그려야 할 때 미리 그려놓은 Bitmap을 화면에 표시하여준다.

1. View클래스를 상속 받는 클래스 생성
2. Member 변수 선언
3. onSizeChanged() 메소드에서 Bitmap 객체 생성
4. onDraw()에서 Bitmap을 화면에 그리기
5. onTouchEvent에서 점과 점을 연결하여 선을 그리기

public class MyCanvas extends View { Bitmap mBitmap; Canvas mCanvas; Paint mPaint; int oldX = -1; int oldY = -1; public MyCanvas(Context context) { super(context); this.mPaint = new Paint(); this.mPaint.setColor(Color.BLACK); } public MyCanvas(Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.mPaint = new Paint(); this.mPaint.setColor(Color.BLACK); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888); //메모리에 Bitmap설정 및 캔버스와 Bitmap 연결 mCanvas = new Canvas(); mCanvas.setBitmap(mBitmap); mCanvas.drawColor(Color.YELLOW); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); //메모리에 그림을 하나 그려놓는다. mCanvas.drawBitmap(bitmap, 20, 20, mPaint); bitmap.recycle(); } //메모리에 있는 Bitmap을 가져다가 View에 그리기 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(mBitmap != null) { canvas.drawBitmap(mBitmap, 0, 0, null); } } // 터치로 선 그리기 @Override public boolean onTouchEvent(MotionEvent event) { int X = (int)event.getX(); int Y = (int)event.getY(); if(event.getAction() == MotionEvent.ACTION_DOWN) { oldX = X; oldY = Y; } else if(event.getAction() == MotionEvent.ACTION_MOVE){ if(oldX != -1 && !stamp) { mCanvas.drawLine(oldX, oldY, X, Y, mPaint); invalidate(); } oldX = X; oldY = Y; }else if(event.getAction() == MotionEvent.ACTION_UP){ if(oldX != -1) { mCanvas.drawLine(oldX, oldY, X, Y, mPaint); invalidate(); } oldX = -1; oldY = -1; } return true; } //그려진 것들을 모두 지운다(하얀색으로 덮어버린다.) public void clear(){ mBitmap.eraseColor(Color.WHITE); invalidate(); } //비트맵 객체 저장/비트맵 지우기 public boolean save(String file_name){ try{ FileOutputStream out = new FileOutputStream(file_name); mBitmap.compress(Bitmap.CompressFormat.JPEG,100,out); //100->품질 out.close(); ToasStrokeWidth(3); } }