【发布时间】:2016-07-12 11:23:42
【问题描述】:
最近我一直在尝试在我放置在 FrameLayout 中的图片上实现拖动和缩放。我想要实现的很简单:能够拖动图片并缩放它。我去了Android开发者网站,关注了guide there。
然后按照我写的那个网站上的代码示例MyCustomView:
public class MyCustomView extends ImageView {
private static final int INVALID_POINTER_ID = 0xDEADBEEF;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
private float mLastTouchX, mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private LayoutParams mLayoutParams;
private int mPosX, mPosY;
public MyCustomView(Context context) {
super(context);
mScaleDetector = new ScaleGestureDetector(context, new CustomScaleListener());
mLayoutParams = (LayoutParams) super.getLayoutParams();
if (mLayoutParams != null) {
mPosX = mLayoutParams.leftMargin;
mPosY = mLayoutParams.topMargin;
} else {
mLayoutParams = new LayoutParams(300, 300);
mLayoutParams.leftMargin = 0;
mLayoutParams.topMargin = 0;
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events
mScaleDetector.onTouchEvent(ev);
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
//final float x = MotionEventCompat.getX(ev, pointerIndex);
//final float y = MotionEventCompat.getY(ev, pointerIndex);
final float x = ev.getRawX();
final float y = ev.getRawY();
// Remember where we started (for dragging)
mLastTouchX = x;
mLastTouchY = y;
// Save the ID of this pointer (for dragging)
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
//final float x = MotionEventCompat.getX(ev, pointerIndex);
//final float y = MotionEventCompat.getY(ev, pointerIndex);
final float x = ev.getRawX();
final float y = ev.getRawY();
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
//TODO: Update the location of this view
mPosX += dx;
mPosY += dy;
mLayoutParams.leftMargin += dx;
mLayoutParams.topMargin += dy;
super.setLayoutParams(mLayoutParams);
invalidate();
mLastTouchX = x;
mLastTouchY = y;
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 = MotionEventCompat.getActionIndex(ev);
final int pointerID = MotionEventCompat.getPointerId(ev, 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 = MotionEventCompat.getX(ev, newPointerIndex);
//mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);
mLastTouchX = ev.getRawX();
mLastTouchY = ev.getRawY();
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
}
break;
}
}
return true;
}
private class CustomScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
在 MainActivity 中,我只是简单地实例化了一个 MyCustomView 对象并将其附加到后台的 ViewGroup,这是一个 FrameLayout。 xml 文件只有一个 FrameLayout。
public class MainActivity extends AppCompatActivity {
private ViewGroup layoutRoot;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
layoutRoot = (ViewGroup) findViewById(R.id.view_root);
final MyCustomView ivAndroid = new MyCustomView(this);
ivAndroid.setImageResource(R.mipmap.ic_launcher);
ivAndroid.setLayoutParams(new FrameLayout.LayoutParams(300, 300));
layoutRoot.addView(ivAndroid);
}
}
困扰我的问题来了: Android 开发者网站使用这个来获取触摸图片的手指坐标:
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float y = MotionEventCompat.getY(ev, pointerIndex);
但是效果很差!图片在移动,但并不完全跟随我的手指,它总是比我的手指移动的少,最重要的是,它会闪烁。
这就是为什么您可以在 MyCustomView 中看到我已注释掉这一行并改为使用此代码的原因:
final float x = ev.getRawX();
final float y = ev.getRawY();
虽然这一次画面按照我的手指流畅移动,但这种变化带来了一个新问题。在Android Developerwebsite进行拖拽缩放,有一个设计原则是这样说的:
在拖动(或滚动)操作中,应用程序必须跟踪原始指针(手指),即使其他手指放置在屏幕上也是如此。例如,假设在拖动图像时,用户将第二根手指放在触摸屏上并抬起第一根手指。如果您的应用只是跟踪单个指针,它会将第二个指针视为默认指针并将图像移动到该位置。
在我开始使用 ev.getRawX() 和 ev.getRawY() 之后,在屏幕上添加第二根手指就可以解决上述问题。但是 MotionEventCompat.getX(ev, pointerIndex) 和 MotionEventCompat.getY(ev, pointerIndex) 没有。
有人可以帮我解释一下为什么会这样吗?我知道 MotionEventCompat.getX(ev, pointerIndex) 在某种调整后返回坐标,而 ev.getRawX() 返回绝对坐标。但我不明白调整究竟是如何工作的(有公式或图形解释吗?)。我也想知道为什么使用 MotionEventCompat.getX(...) 会阻止图片跳到屏幕上的第二根手指(在第一根手指抬起之后)。
最后但同样重要的是,缩放代码根本不起作用。如果有人教我,那也将不胜感激!
【问题讨论】:
-
见this
-
您好,很抱歉我没有看到这个。我不太了解您的代码背后的机制...(对不起,我是 Android 新手)如果可能的话,您能解释一下您的 Layer 类是什么吗?
-
运行代码,你会看到三层
标签: android android-framelayout