검색결과 리스트
Total에 해당되는 글 162건
글
제목은 뭔가 거창해 보이지만, 네이밍 센스가 없어서 그냥 그렇습니다. :)
눈치 빠른분들은 아시겠지만, 멀티터치를 통해서 view 를 이동, 크기변환, 회전 시키는 방법에 대해서 살펴보려고 합니다.
사실 살펴본다기보다, 그냥 코드공유 정도가 되겠네요.
사설이지만, 언제부터인가 블로깅을 한다는게 개인적으로는 무의미한다는 생각이 들더라고요...;;;
블로깅하려면, 아는 내용이어야 하고, 아는 내용이면 사실 저에게는 의미가 없지요;;
개인적인 기록용이나 투철한 홍익인간 정신이 아니고서야 블로깅은 역시나 어렵습니다.
그나마 기록용이라는것도 github 에 개인 프로젝트를 통째로 올리는 시대이므로 불필요하고요
모쪼록 사설이 길었습니다. :)
Android: Transform View via Multi-Touch
해당 코드는 인터넷에 해당기능 관련 코드는 많은데, 제가 딱 원하는 것이 없어서 따로 만들게 되었습니다.
제가 필요한 기능은 다음과 같았습니다.
필요했던 기능/제약(지원하는 기능)
1. view 의 size 가 wrap_content 이어야 함
- 간혹 인터넷에 있는 몇몇 소스들을 보면, view 가 전체(match_parent) 임
2. translate / scale / rotate 모두 지원되어야 함
- 몇몇 소스들은 몇개만 지원됨
막상 적고나니 조건이 몇개 없네요 :)
기본 원리는 Touch 시 각 Gesture 를 받아서 저장한 뒤, matrix 에 반영하여 출력하는 것입니다.
※ Touch Gesture 는 외부 소스코드를 그대로 사용하였습니다.
보시면 아시겠지만, FlexibleView 는 ImageView 를 상속받고 있는데, 꼭 ImageView 일 필요는 없습니다.
View 를 상속받고 있는 클래스면 무엇이든 상관없습니다.
상황에 맞게 상속받아서 사용하시면 되겠네요.
자세한 설명은 소스코드로 대체하겠습니다.
[FlexibleView]
package kr.co.crystalcube.translatable;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.Button;
import android.widget.ImageView;
import kr.co.crystalcube.translatable.gesture.MoveGestureDetector;
import kr.co.crystalcube.translatable.gesture.RotateGestureDetector;
/**
* Created by Naver on 2015-12-09.
*/
public class FlexibleView extends ImageView {
public enum FlipDirection { NONE, VERTICAL, HORIZONTAL }
private ScaleGestureDetector mScaleDetector;
protected float mScaleFactor = 1.f;
private static final float MAX_SCALE_FACTOR = 30.f;
private static final float MIN_SCALE_FACTOR = 0.3f;
private float mMaxScaleFactor = 30f;
private float mMinScaleFactor = 0.3f;
private MoveGestureDetector mMoveDetector;
protected float mFocusX = 0.f;
protected float mFocusY = 0.f;
private RotateGestureDetector mRotateDetector;
protected float mRotationDegree = 0.f;
private FlipDirection mFlipDirection = FlipDirection.NONE;
protected float mFlipX = 1;
protected float mFlipY = 1;
private boolean mIsMultiTouch = false;
private double mMoveDistance = 0f;
private PointF mTouchPoint = new PointF();
private static final int MAX_CLICK_DISTANCE = 4;
private static final int MAX_LONG_CLICK_DISTANCE = 16;
public FlexibleView(Context context) {
super(context);
this.init(context);
}
public FlexibleView(Context context, AttributeSet attrs) {
super(context, attrs);
this.init(context);
}
public FlexibleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.init(context);
}
private void init(Context context) {
this.mMaxScaleFactor = MAX_SCALE_FACTOR;
this.mMinScaleFactor = MIN_SCALE_FACTOR;
this.mScaleDetector = new ScaleGestureDetector(context, this.getScaleListener());
this.mMoveDetector = new MoveGestureDetector(context, this.getMoveListener());
this.mRotateDetector = new RotateGestureDetector(context, this.getRotateListener());
}
public void setMaxScaleFactor(float max) {
this.mMaxScaleFactor = max;
}
public void setMinScaleFactor(float min) {
this.mMinScaleFactor = min;
}
protected ScaleGestureDetector.SimpleOnScaleGestureListener getScaleListener() {
return new ScaleListener();
}
protected MoveGestureDetector.SimpleOnMoveGestureListener getMoveListener() {
return new MoveListener();
}
protected RotateGestureDetector.SimpleOnRotateGestureListener getRotateListener() {
return new RotateListener();
}
public void setFlip(FlipDirection direction) {
switch (direction) {
case HORIZONTAL:
this.mFlipX *= -1; break;
case VERTICAL:
this.mFlipY *= -1; break;
case NONE:
default:
this.mFlipX = 1;
this.mFlipY = 1;
break;
}
if(this.mFlipX > 1 && this.mFlipY > 1) {
this.mFlipDirection = FlipDirection.NONE;
} else if(this.mFlipX > 1 && this.mFlipY < 1) {
this.mFlipDirection = FlipDirection.VERTICAL;
} else if(this.mFlipX < 1 && this.mFlipY > 1) {
this.mFlipDirection = FlipDirection.HORIZONTAL;
}
this.invalidate();
}
public FlipDirection getFlipDirection() {
return this.mFlipDirection;
}
public float getFlipX() {
return this.mFlipX;
}
public float getFlipY() {
return this.mFlipY;
}
public float getScaleFactor() {
return this.mScaleFactor;
}
public void setScaleFactor(float scale) {
this.mScaleFactor = scale;
this.invalidate();
}
public void setFocus(float x, float y) {
this.mFocusX = x;
this.mFocusY = y;
this.invalidate();
}
public void moveFocus(float x, float y) {
this.setFocus(this.mFocusX + x, this.mFocusY + y);
}
@Override
public boolean performClick() {
if(this.mIsMultiTouch == true || this.mMoveDistance > MAX_CLICK_DISTANCE) { return false; }
return super.performClick();
}
@Override
public boolean performLongClick() {
if(this.mIsMultiTouch == true || this.mMoveDistance > MAX_LONG_CLICK_DISTANCE) { return false; }
return super.performLongClick();
}
private void getRowPoint(MotionEvent ev, int index, PointF point){
final int location[] = { 0, 0 };
getLocationOnScreen(location);
float x=ev.getX(index);
float y=ev.getY(index);
x *= getScaleX();
y *= getScaleY();
double angle = Math.toDegrees(Math.atan2(y, x));
angle += getRotation();
final float length = PointF.length(x, y);
x = (float)(length * Math.cos(Math.toRadians(angle))) + location[0];
y = (float)(length * Math.sin(Math.toRadians(angle))) + location[1];
point.set(x, y);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(this.isEnabled() == false) { return false; }
// compute transfrom
MotionEvent.PointerProperties[] prop = new MotionEvent.PointerProperties[ev.getPointerCount()];
MotionEvent.PointerCoords[] cords = new MotionEvent.PointerCoords[ev.getPointerCount()];
MotionEvent.PointerCoords firstCoords = new MotionEvent.PointerCoords();
ev.getPointerCoords(0, firstCoords);
for(int i = 0, n = ev.getPointerCount(); i < n; i++) {
MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
ev.getPointerProperties(i, properties);
prop[i] = properties;
MotionEvent.PointerCoords cod = new MotionEvent.PointerCoords();
ev.getPointerCoords(i, cod);
PointF rawPos = new PointF();
getRowPoint(ev,i, rawPos );
cod.x = (int)rawPos.x;
cod.y = (int)rawPos.y;
cords[i] = cod;
}
MotionEvent screenBaseMotionEvent = MotionEvent.obtain(ev.getDownTime(), ev.getEventTime(), ev.getAction(), ev.getPointerCount(), prop, cords, ev.getMetaState(), ev.getButtonState(), ev.getXPrecision(), ev.getYPrecision(), ev.getDeviceId(), ev.getEdgeFlags(), ev.getSource(), ev.getFlags());
this.mScaleDetector.onTouchEvent(screenBaseMotionEvent);
this.mRotateDetector.onTouchEvent(screenBaseMotionEvent);
this.mMoveDetector.onTouchEvent(screenBaseMotionEvent);
this.computeClickEvent(ev); // adjust click event
super.onTouchEvent(ev);
this.invalidate();
return true;
}
private void computeClickEvent(MotionEvent ev) {
// check if it is moved
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
this.mIsMultiTouch = (ev.getPointerCount() < 2 ? false : true);
this.mTouchPoint = new PointF(ev.getRawX(), ev.getRawY());
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
{
if(ev.getPointerCount() > 1) { this.mIsMultiTouch = true; break; }
this.mMoveDistance = getDistance(new PointF(ev.getRawX(), ev.getRawY()), this.mTouchPoint);
break;
}
}
}
private static double getDistance(PointF point1, PointF point2) {
return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
}
@Override
protected void onDraw(Canvas canvas) {
this.setTranslationX(this.mFocusX);
this.setTranslationY(this.mFocusY);
this.setScaleX(this.mScaleFactor * this.mFlipX);
this.setScaleY(this.mScaleFactor * this.mFlipY);
this.setRotation(this.mRotationDegree);
super.onDraw(canvas);
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
float mergedScaleFactor = mScaleFactor * scaleFactor;
if(mergedScaleFactor > mMaxScaleFactor || mergedScaleFactor < mMinScaleFactor) { return false; }
mScaleFactor = mergedScaleFactor;
mScaleFactor = Math.max(mMinScaleFactor, mScaleFactor);
mScaleFactor = Math.min(mMaxScaleFactor, mScaleFactor);
return true;
}
}
private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
@Override
public boolean onMove(MoveGestureDetector detector) {
PointF delta = detector.getFocusDelta();
mFocusX += delta.x;
mFocusY += delta.y;
return true;
}
}
private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
@Override
public boolean onRotate(RotateGestureDetector detector) {
mRotationDegree -= detector.getRotationDegreesDelta();
return true;
}
}
}
[BaseGestureDetector]
package kr.co.crystalcube.translatable.gesture;
import android.content.Context;
import android.view.MotionEvent;
/**
* Created by Naver on 2015-12-07.
*/
public abstract class BaseGestureDetector {
protected final Context mContext;
protected boolean mGestureInProgress;
protected MotionEvent mPrevEvent;
protected MotionEvent mCurrEvent;
protected float mCurrPressure;
protected float mPrevPressure;
protected long mTimeDelta;
/**
* This value is the threshold ratio between the previous combined pressure
* and the current combined pressure. When pressure decreases rapidly
* between events the position values can often be imprecise, as it usually
* indicates that the user is in the process of lifting a pointer off of the
* device. This value was tuned experimentally.
*/
protected static final float PRESSURE_THRESHOLD = 0.67f;
public BaseGestureDetector(Context context) {
mContext = context;
}
/**
* All gesture detectors need to be called through this method to be able to
* detect gestures. This method delegates work to handler methods
* (handleStartProgressEvent, handleInProgressEvent) implemented in
* extending classes.
*
* @param event
* @return
*/
public boolean onTouchEvent(MotionEvent event){
final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
if (!mGestureInProgress) {
handleStartProgressEvent(actionCode, event);
} else {
handleInProgressEvent(actionCode, event);
}
return true;
}
/**
* Called when the current event occurred when NO gesture is in progress
* yet. The handling in this implementation may set the gesture in progress
* (via mGestureInProgress) or out of progress
* @param actionCode
* @param event
*/
protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event);
/**
* Called when the current event occurred when a gesture IS in progress. The
* handling in this implementation may set the gesture out of progress (via
* mGestureInProgress).
* @param actionCode
* @param event
*/
protected abstract void handleInProgressEvent(int actionCode, MotionEvent event);
protected void updateStateByEvent(MotionEvent curr){
final MotionEvent prev = mPrevEvent;
// Reset mCurrEvent
if (mCurrEvent != null) {
mCurrEvent.recycle();
mCurrEvent = null;
}
mCurrEvent = MotionEvent.obtain(curr);
// Delta time
mTimeDelta = curr.getEventTime() - prev.getEventTime();
// Pressure
mCurrPressure = curr.getPressure(curr.getActionIndex());
mPrevPressure = prev.getPressure(prev.getActionIndex());
}
protected void resetState() {
if (mPrevEvent != null) {
mPrevEvent.recycle();
mPrevEvent = null;
}
if (mCurrEvent != null) {
mCurrEvent.recycle();
mCurrEvent = null;
}
mGestureInProgress = false;
}
/**
* Returns {@code true} if a gesture is currently in progress.
* @return {@code true} if a gesture is currently in progress, {@code false} otherwise.
*/
public boolean isInProgress() {
return mGestureInProgress;
}
/**
* Return the time difference in milliseconds between the previous accepted
* GestureDetector event and the current GestureDetector event.
*
* @return Time difference since the last move event in milliseconds.
*/
public long getTimeDelta() {
return mTimeDelta;
}
/**
* Return the event time of the current GestureDetector event being
* processed.
*
* @return Current GestureDetector event time in milliseconds.
*/
public long getEventTime() {
return mCurrEvent.getEventTime();
}
}
[MoveGestureDetector]
package kr.co.crystalcube.translatable.gesture;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
/**
* Created by Naver on 2015-12-07.
*/
public class MoveGestureDetector extends BaseGestureDetector {
/**
* Listener which must be implemented which is used by MoveGestureDetector
* to perform callbacks to any implementing class which is registered to a
* MoveGestureDetector via the constructor.
*
* @see MoveGestureDetector.SimpleOnMoveGestureListener
*/
public interface OnMoveGestureListener {
public boolean onMove(MoveGestureDetector detector);
public boolean onMoveBegin(MoveGestureDetector detector);
public void onMoveEnd(MoveGestureDetector detector);
}
/**
* Helper class which may be extended and where the methods may be
* implemented. This way it is not necessary to implement all methods
* of OnMoveGestureListener.
*/
public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
public boolean onMove(MoveGestureDetector detector) {
return false;
}
public boolean onMoveBegin(MoveGestureDetector detector) {
return true;
}
public void onMoveEnd(MoveGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private static final PointF FOCUS_DELTA_ZERO = new PointF();
private final OnMoveGestureListener mListener;
private PointF mCurrFocusInternal;
private PointF mPrevFocusInternal;
private PointF mFocusExternal = new PointF();
private PointF mFocusDeltaExternal = new PointF();
public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
case MotionEvent.ACTION_DOWN:
resetState(); // In case we missed an UP/CANCEL event
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
updateStateByEvent(event);
break;
case MotionEvent.ACTION_MOVE:
mGestureInProgress = mListener.onMoveBegin(this);
break;
}
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mListener.onMoveEnd(this);
resetState();
break;
case MotionEvent.ACTION_MOVE:
updateStateByEvent(event);
// Only accept the event if our relative pressure is within
// a certain limit. This can help filter shaky data as a
// finger is lifted.
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
final boolean updatePrevious = mListener.onMove(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
public MotionEvent getEvent() {
return mPrevEvent;
}
protected void updateStateByEvent(MotionEvent curr) {
super.updateStateByEvent(curr);
final MotionEvent prev = mPrevEvent;
// Focus intenal
mCurrFocusInternal = determineFocalPoint(curr);
mPrevFocusInternal = determineFocalPoint(prev);
// Focus external
// - Prevent skipping of focus delta when a finger is added or removed
boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount();
mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y);
// - Don't directly use mFocusInternal (or skipping will occur). Add
// unskipped delta values to mFocusExternal instead.
mFocusExternal.x += mFocusDeltaExternal.x;
mFocusExternal.y += mFocusDeltaExternal.y;
}
private PointF determineFocalPoint(MotionEvent e){
// Number of fingers on screen
final int pCount = e.getPointerCount();
float x = 0f;
float y = 0f;
for(int i = 0; i < pCount; i++){
x += e.getX(i);
y += e.getY(i);
}
return new PointF(x/pCount, y/pCount);
}
public float getFocusX() {
return mFocusExternal.x;
}
public float getFocusY() {
return mFocusExternal.y;
}
public PointF getFocusDelta() {
return mFocusDeltaExternal;
}
}
[RotateGestureDetector]
package kr.co.crystalcube.translatable.gesture;
import android.content.Context;
import android.view.MotionEvent;
/**
* Created by Naver on 2015-12-07.
*/
public class RotateGestureDetector extends TwoFingerGestureDetector {
/**
* Listener which must be implemented which is used by RotateGestureDetector
* to perform callbacks to any implementing class which is registered to a
* RotateGestureDetector via the constructor.
*
* @see RotateGestureDetector.SimpleOnRotateGestureListener
*/
public interface OnRotateGestureListener {
public boolean onRotate(RotateGestureDetector detector);
public boolean onRotateBegin(RotateGestureDetector detector);
public void onRotateEnd(RotateGestureDetector detector);
}
/**
* Helper class which may be extended and where the methods may be
* implemented. This way it is not necessary to implement all methods
* of OnRotateGestureListener.
*/
public static class SimpleOnRotateGestureListener implements OnRotateGestureListener {
public boolean onRotate(RotateGestureDetector detector) {
return false;
}
public boolean onRotateBegin(RotateGestureDetector detector) {
return true;
}
public void onRotateEnd(RotateGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private final OnRotateGestureListener mListener;
private boolean mSloppyGesture;
public RotateGestureDetector(Context context, OnRotateGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
case MotionEvent.ACTION_POINTER_DOWN:
// At least the second finger is on screen now
resetState(); // In case we missed an UP/CANCEL event
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
updateStateByEvent(event);
// See if we have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if(!mSloppyGesture){
// No, start gesture now
mGestureInProgress = mListener.onRotateBegin(this);
}
break;
case MotionEvent.ACTION_MOVE:
if (!mSloppyGesture) {
break;
}
// See if we still have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if(!mSloppyGesture){
// No, start normal gesture now
mGestureInProgress = mListener.onRotateBegin(this);
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (!mSloppyGesture) {
break;
}
break;
}
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
case MotionEvent.ACTION_POINTER_UP:
// Gesture ended but
updateStateByEvent(event);
if (!mSloppyGesture) {
mListener.onRotateEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_CANCEL:
if (!mSloppyGesture) {
mListener.onRotateEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_MOVE:
updateStateByEvent(event);
// Only accept the event if our relative pressure is within
// a certain limit. This can help filter shaky data as a
// finger is lifted.
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
final boolean updatePrevious = mListener.onRotate(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
@Override
protected void resetState() {
super.resetState();
mSloppyGesture = false;
}
/**
* Return the rotation difference from the previous rotate event to the current
* event.
*
* @return The current rotation //difference in degrees.
*/
public float getRotationDegreesDelta() {
double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
return (float) (diffRadians * 180 / Math.PI);
}
}
[TwoFingerGestureDetector]
package kr.co.crystalcube.translatable.gesture;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
/**
* Created by Naver on 2015-12-07.
*/
public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
private final float mEdgeSlop;
private float mRightSlopEdge;
private float mBottomSlopEdge;
protected float mPrevFingerDiffX;
protected float mPrevFingerDiffY;
protected float mCurrFingerDiffX;
protected float mCurrFingerDiffY;
private float mCurrLen;
private float mPrevLen;
public TwoFingerGestureDetector(Context context) {
super(context);
ViewConfiguration config = ViewConfiguration.get(context);
mEdgeSlop = config.getScaledEdgeSlop();
}
@Override
protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event);
@Override
protected abstract void handleInProgressEvent(int actionCode, MotionEvent event);
protected void updateStateByEvent(MotionEvent curr){
super.updateStateByEvent(curr);
final MotionEvent prev = mPrevEvent;
mCurrLen = -1;
mPrevLen = -1;
// Previous
if(prev.getPointerCount() >= 2) {
final float px0 = prev.getX(0);
final float py0 = prev.getY(0);
final float px1 = prev.getX(1);
final float py1 = prev.getY(1);
final float pvx = px1 - px0;
final float pvy = py1 - py0;
mPrevFingerDiffX = pvx;
mPrevFingerDiffY = pvy;
}
// Current
if(curr.getPointerCount() >= 2) {
final float cx0 = curr.getX(0);
final float cy0 = curr.getY(0);
final float cx1 = curr.getX(1);
final float cy1 = curr.getY(1);
final float cvx = cx1 - cx0;
final float cvy = cy1 - cy0;
mCurrFingerDiffX = cvx;
mCurrFingerDiffY = cvy;
}
}
/**
* Return the current distance between the two pointers forming the
* gesture in progress.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpan() {
if (mCurrLen == -1) {
final float cvx = mCurrFingerDiffX;
final float cvy = mCurrFingerDiffY;
mCurrLen = (float)Math.sqrt((double)(cvx * cvx + cvy * cvy));
}
return mCurrLen;
}
/**
* Return the previous distance between the two pointers forming the
* gesture in progress.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpan() {
if (mPrevLen == -1) {
final float pvx = mPrevFingerDiffX;
final float pvy = mPrevFingerDiffY;
mPrevLen = (float)Math.sqrt((double)(pvx*pvx + pvy*pvy));
}
return mPrevLen;
}
/**
* MotionEvent has no getRawX(int) method; simulate it pending future API approval.
* @param event
* @param pointerIndex
* @return
*/
protected static float getRawX(MotionEvent event, int pointerIndex) {
float offset = event.getX() - event.getRawX();
if(pointerIndex < event.getPointerCount()){
return event.getX(pointerIndex) + offset;
}
return 0f;
}
/**
* MotionEvent has no getRawY(int) method; simulate it pending future API approval.
* @param event
* @param pointerIndex
* @return
*/
protected static float getRawY(MotionEvent event, int pointerIndex) {
float offset = event.getY() - event.getRawY();
if(pointerIndex < event.getPointerCount()){
return event.getY(pointerIndex) + offset;
}
return 0f;
}
/**
* Check if we have a sloppy gesture. Sloppy gestures can happen if the edge
* of the user's hand is touching the screen, for example.
*
* @param event
* @return
*/
protected boolean isSloppyGesture(MotionEvent event){
// As orientation can change, query the metrics in touch down
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
final float edgeSlop = mEdgeSlop;
final float rightSlop = mRightSlopEdge;
final float bottomSlop = mBottomSlopEdge;
final float x0 = event.getRawX();
final float y0 = event.getRawY();
final float x1 = getRawX(event, 1);
final float y1 = getRawY(event, 1);
boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
|| x0 > rightSlop || y0 > bottomSlop;
boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
|| x1 > rightSlop || y1 > bottomSlop;
if (p0sloppy && p1sloppy) {
return true;
} else if (p0sloppy) {
return true;
} else if (p1sloppy) {
return true;
}
return false;
}
}
'Android' 카테고리의 다른 글
정신건강에 좋은 파일 공유하기 (1) | 2019.01.04 |
---|---|
Content 의 Uri 로부터 FilePath 가져오기 (11) | 2016.12.01 |
Android Studio 2.2 에서 NDK 빌드하기 (외부 so파일 추가) (3) | 2016.10.11 |
RECENT COMMENT