我为ImageView创建了一个缩放和平移类。
我想要创建的功能。 - 它可以单指触摸和移动 - 它通过两个手指触摸和移动来缩放和平移
在大多数情况下,这非常有效。 当我执行以下操作时,它有一个小错误: - 我用一根手指平移(状态:没问题) - 我放下了第二根手指,变焦和平移(状态:没问题) - 我释放了我的第二根手指(状态:图像跳了一点)
希望有人能帮我解决这个问题。
我认为必须在“案例MotionEvent.ACTION_POINTER_UP”中重置mLastTouchX和mLastTouchY
非常感谢任何帮助!
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
public class MyImageView extends ImageView {
private static final int INVALID_POINTER_ID = -1;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private float mLastGestureX;
private float mLastGestureY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
if (!mScaleDetector.isInProgress()) {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
}
break;
}
case MotionEvent.ACTION_POINTER_1_DOWN: {
if (mScaleDetector.isInProgress()) {
final float gx = mScaleDetector.getFocusX();
final float gy = mScaleDetector.getFocusY();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
}
case MotionEvent.ACTION_MOVE: {
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
mLastTouchX = x;
mLastTouchY = y;
}
else{
final float gx = mScaleDetector.getFocusX();
final float gy = mScaleDetector.getFocusY();
final float gdx = gx - mLastGestureX;
final float gdy = gy - mLastGestureY;
mPosX += gdx;
mPosY += gdy;
invalidate();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
Log.d("DEBUG", "mActivePointerId");
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
@Override
public void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(mPosX, mPosY);
if (mScaleDetector.isInProgress()) {
canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
else{
canvas.scale(mScaleFactor, mScaleFactor);
}
super.onDraw(canvas);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
invalidate();
return true;
}
}
}
答案 0 :(得分:18)
似乎'onDraw'方法的'else'语句中的canvas.scale()需要mLastGestureX和mLastGestureY来停止跳跃。 在'case MotionEvent.ACTION_POINTER_UP'中回到单指平移时,我也刷新了mLastTouchX和mLastTouchY
这是最后的,可能不适合所有人,因为它不会限制过去的图像范围,但这应该很容易实现,有很多关于该主题的讨论。
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
public class MyImageView extends ImageView {
private static final int INVALID_POINTER_ID = -1;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private float mLastGestureX;
private float mLastGestureY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
if (!mScaleDetector.isInProgress()) {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
}
break;
}
case MotionEvent.ACTION_POINTER_1_DOWN: {
if (mScaleDetector.isInProgress()) {
final float gx = mScaleDetector.getFocusX();
final float gy = mScaleDetector.getFocusY();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
}
case MotionEvent.ACTION_MOVE: {
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
mLastTouchX = x;
mLastTouchY = y;
}
else{
final float gx = mScaleDetector.getFocusX();
final float gy = mScaleDetector.getFocusY();
final float gdx = gx - mLastGestureX;
final float gdy = gy - mLastGestureY;
mPosX += gdx;
mPosY += gdy;
invalidate();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
else{
final int tempPointerIndex = ev.findPointerIndex(mActivePointerId);
mLastTouchX = ev.getX(tempPointerIndex);
mLastTouchY = ev.getY(tempPointerIndex);
}
break;
}
}
return true;
}
@Override
public void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(mPosX, mPosY);
if (mScaleDetector.isInProgress()) {
canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
else{
canvas.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY);
}
super.onDraw(canvas);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
invalidate();
return true;
}
}
}
答案 1 :(得分:2)
我一直在解决这个问题超过一个星期,它给了我很多问题。不过,我已经明显缩小了这个问题的范围。您上面的解决方案对我不起作用,但我的解决方案很接近。问题是每当第二根手指被按下或抬起时它就会跳跃。我发现这是因为mPosX和mPosY并不总是真正的所谓的代表的变量。这就是我的意思:
当调用ACTION_MOVE并且代码进入" else"声明(处理缩放事件),mPosX和mPosY仅根据焦点变化,不 变焦更改进行更改。这意味着使用两个手指进行平移工作,并且使用两个手指进行缩放工作,但mPosX和mPosY相对于变化的变焦没有适当变化。
我一直在尝试使用缩放的差异变化(mScaleDetector.getScaleFactor())以及焦点的差异变化找出解决方法,但我似乎无法通过逻辑工作足以找到有用的东西。
另一个解决方案是将所有缩放操作移动到OnTouchListener中,并完全摆脱ScaleListener。这意味着更多的数学,但它绝对是一个解决方案。
这是onDraw:
@Override
public void onDraw(Canvas c) {
c.save();
if (mScaleDetector.isInProgress()) {
c.scale(mScaleFactor, mScaleFactor, mLastGestureX - mPosX,
mLastGestureY - mPosY);
} else {
c.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY);
}
c.translate(mPosX / mScaleFactor, mPosY / mScaleFactor);
// drawing instruction here
c.restore();
}
以下是代码对指压的反应:
@Override
public boolean onTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
if (!mScaleDetector.isInProgress()) {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
}
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
if (!mScaleDetector.isInProgress()) {
final float gx = mScaleDetector.getFocusX();
final float gy = mScaleDetector.getFocusY();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (!mScaleDetector.isInProgress()) {
Log.i("hi", "SD not in progress");
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
mLastTouchX = x;
mLastTouchY = y;
} else {
Log.i("hi", "SD in progress");
final float gx = mScaleDetector.getFocusX();
final float gy = mScaleDetector.getFocusY();
final float gdx = gx - mLastGestureX;
final float gdy = gy - mLastGestureY;
mPosX += gdx;
mPosY += gdy;
// SOMETHING NEEDS TO HAPPEN RIGHT HERE.
invalidate();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
} else {
final int tempPointerIndex = ev.findPointerIndex(mActivePointerId);
mLastTouchX = ev.getX(tempPointerIndex);
mLastTouchY = ev.getY(tempPointerIndex);
}
break;
}
}
return true;
}
虽然它大多不相关,但这里是ScaleListener:
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
invalidate();
return true;
}
}
同样,这段代码不能完美运行,但它非常接近。我已经解释了上面的确切问题,但仍然无法使其正常工作。我不知道这是否会在你的通知中弹出,汉克,但希望有人会看到它并帮助我。
答案 2 :(得分:0)
汉克的解决方案适合我。我添加了一个重置功能,因此可以正常显示后续图像。
public void ResetView() {
mScaleFactor = 1.f;
mPosX = 0.f;
mPosY = 0.f;
}
答案 3 :(得分:-1)
没有查看代码我假设你有两个手指时根据2个手指进行位置计算。在这种情况下,你总会得到一个跳跃。