반응형
애플에서 '핀치'라고 명칭을 정한 멀티터치는 두 손가락을 이용해 손가락 사이를 벌리면 이미지가 점차 확대되고, 좁히면 이미지가 작아지도록 만든 기능이다
한 손가락으로 터치했을때의 좌표뿐만 아니라 두 번째 손가락으로 터치했을 때의 좌표 값까지 알 수 있어야한다
getPointerCount 메소드는 몇 개의 손가락이 터치되었는지 알 수 있도록 해주는 것으로 만약 반환된 값이 1이라면 한 개의 손가락, 2라면 두 개의 손가락이 터치된 상태이다
이벤트 처리에 사용되는 getX와 getY 메소드는 손가락이 하나일때 X와 Y의 좌표 값을 가져오지만, getX(int pointerIndex)와 getY(int pointerIndex) 메소드는 여러 개의 손가락이 터치되었을 때 각각의 손가락이 가지는 인덱스의 값을 이용해 좌표 값을 확인할 수 있도록 한다
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="두 손가락을 이용해 터치해 보세요." />
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"></LinearLayout>
</LinearLayout>
ImageDisplayView.java
package com.example.a70_multitouch;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class ImageDisplayView extends View implements View.OnTouchListener {
private static final String TAG = "ImageDisplayView";
Context mContext;
Canvas mCanvas;
Bitmap mBitmap;
Paint mPaint;
int lastX;
int lastY;
Bitmap sourceBitmap;
Matrix mMatrix;
float sourceWidth = 0.0F;
float sourceHeight = 0.0F;
float bitmapCenterX;
float bitmapCenterY;
float scaleRatio;
float totalScaleRatio;
float displayWidth = 0.0F;
float displayHeight = 0.0F;
int displayCenterX = 0;
int displayCenterY = 0;
public float startX;
public float startY;
public static float MAX_SCALE_RATIO = 5.0F;
public static float MIN_SCALE_RATIO = 0.1F;
float oldDistance = 0.0F;
int oldPointerCount = 0;
boolean isScrolling = false;
float distanceThreshold = 3.0F;
public ImageDisplayView(Context context) {
super(context);
mContext = context;
init();
}
public ImageDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
private void init() {
Log.d(TAG, "init() 호출");
mPaint = new Paint();
mMatrix = new Matrix();
lastX = -1;
lastY = -1;
setOnTouchListener(this);
}
// 뷰가 초기화되고 나서 화면에 보이기 전에 크기가 정해지면 호출되는 메소드 안에서 메모리 상에 새로운 비트맵 객체 생성
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.d(TAG, "onSizeChanged() 호출 : " + w + ", " + h + ", " + oldw + ", " + oldh);
if (w > 0 && h > 0) {
newImage(w, h);
redraw();
}
}
public void newImage(int width, int height) {
Log.d(TAG, "newImage() 호출 : " + width + ", " + height);
Bitmap img = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.setBitmap(img);
mBitmap = img;
mCanvas = canvas;
displayWidth = (float) width;
displayHeight = (float) height;
displayCenterX = width / 2;
displayCenterY = height / 2;
}
public void drawBackground(Canvas canvas) {
Log.d(TAG, "drawBackground() 호출 : " + canvas);
if (canvas != null) {
canvas.drawColor(Color.BLACK);
}
}
// 뷰가 화면에 그려지는 메소드 안에서 메모리 상의 비트맵 객체 그리기
protected void onDraw(Canvas canvas) {
Log.d(TAG, "onDraw() 호출 : " + canvas);
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
public void setImageData(Bitmap image) {
Log.d(TAG, "setImageData() 호출 : " + image);
recycle();
sourceBitmap = image;
sourceWidth = sourceBitmap.getWidth();
sourceHeight = sourceBitmap.getHeight();
bitmapCenterX = sourceBitmap.getWidth() / 2;
bitmapCenterY = sourceBitmap.getHeight() / 2;
scaleRatio = 1.0F;
totalScaleRatio = 1.0F;
}
public void recycle() {
Log.d(TAG, "recycle() 호출");
if (sourceBitmap != null) {
sourceBitmap.recycle();
}
}
public void redraw() {
Log.d(TAG, "redraw() 호출");
if (sourceBitmap == null) {
Log.d(TAG, "sourceBitmap is null in redraw().");
return;
}
drawBackground(mCanvas);
float originX = (displayWidth - (float) sourceBitmap.getWidth()) / 2.0F;
float originY = (displayHeight - (float) sourceBitmap.getHeight()) / 2.0F;
mCanvas.translate(originX, originY);
mCanvas.drawBitmap(sourceBitmap, mMatrix, mPaint);
mCanvas.translate(-originX, -originY);
invalidate();
}
// 뷰를 터치할 때 호출되는 메소드 다시 정의
public boolean onTouch(View v, MotionEvent ev) {
Log.d(TAG, "onTouch() 호출 : " + v + ", " + ev);
final int action = ev.getAction();
int pointerCount = ev.getPointerCount();
Log.d(TAG, "Pointer Count : " + pointerCount);
switch (action) {
case MotionEvent.ACTION_DOWN:
if (pointerCount == 1) {
float curX = ev.getX();
float curY = ev.getY();
startX = curX;
startY = curY;
} else if (pointerCount == 2) {
oldDistance = 0.0F;
isScrolling = true;
}
return true;
case MotionEvent.ACTION_MOVE:
if (pointerCount == 1) {
if (isScrolling) {
return true;
}
float curX = ev.getX();
float curY = ev.getY();
if (startX == 0.0F) {
startX = curX;
startY = curY;
return true;
}
float offsetX = startX - curX;
float offsetY = startY - curY;
if (oldPointerCount == 2) {
} else {
Log.d(TAG, "ACTION_MOVE : " + offsetX + ", " + offsetY);
if (totalScaleRatio > 1.0F) {
// 한 손가락으로 움직일때 moveImage 메소드 호출
moveImage(-offsetX, -offsetY);
}
startX = curX;
startY = curY;
}
} else if (pointerCount == 2) {
float x1 = ev.getX(0);
float y1 = ev.getY(0);
float x2 = ev.getX(1);
float y2 = ev.getY(1);
float dx = x1 - x2;
float dy = y1 - y2;
float distance = new Double(Math.sqrt(new Float(dx * dx + dy * dy).doubleValue())).floatValue();
float outScaleRatio = 0.0F;
if (oldDistance == 0.0F) {
oldDistance = distance;
break;
}
if (distance > oldDistance) {
if ((distance - oldDistance) < distanceThreshold) {
return true;
}
outScaleRatio = scaleRatio + (oldDistance / distance * 0.05F);
} else if (distance < oldDistance) {
if ((oldDistance - distance) < distanceThreshold) {
return true;
}
outScaleRatio = scaleRatio - (distance / oldDistance * 0.05F);
}
if (outScaleRatio < MIN_SCALE_RATIO || outScaleRatio > MAX_SCALE_RATIO) {
Log.d(TAG, "Invalid scaleRatio : " + outScaleRatio);
} else {
Log.d(TAG, "Distance : " + distance + ", ScaleRatio : " + outScaleRatio);
// 두 손가락으로 움직이고 있을 때 scaleImage 메소드 호출
scaleImage(outScaleRatio);
}
oldDistance = distance;
}
oldPointerCount = pointerCount;
break;
// 손가락을 떼었을 때
case MotionEvent.ACTION_UP:
if (pointerCount == 1) {
float curX = ev.getX();
float curY = ev.getY();
float offsetX = startX - curX;
float offsetY = startY - curY;
if (oldPointerCount == 2) {
} else {
moveImage(-offsetX, -offsetY);
}
} else {
isScrolling = false;
}
return true;
}
return true;
}
private void scaleImage(float inScaleRatio) {
Log.d(TAG, "scaleImage() called : " + inScaleRatio);
mMatrix.postScale(inScaleRatio, inScaleRatio, bitmapCenterX, bitmapCenterY);
mMatrix.postRotate(0);
totalScaleRatio = totalScaleRatio * inScaleRatio;
redraw();
}
private void moveImage(float offsetX, float offsetY) {
Log.d(TAG, "moveImage() called : " + offsetX + ", " + offsetY);
mMatrix.postTranslate(offsetX, offsetY);
redraw();
}
}
MainActivity
package com.example.a70_multitouch;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.LinearLayout;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout container = findViewById(R.id.container);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.beach);
ImageDisplayView view = new ImageDisplayView(this);
view.setImageData(bitmap);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
container.addView(view, params);
}
}
반응형
'안드로이드' 카테고리의 다른 글
화면에 카메라 미리보기 넣기 (0) | 2021.11.05 |
---|---|
카메라로 사진 찍어 저장 (0) | 2021.11.04 |
페인트보드(그림판) 만들기 (0) | 2021.11.02 |
BtimapFactory 클래스 (0) | 2021.11.02 |
비트맵 이미지 사용 (0) | 2021.11.02 |