我正在开发一个应用程序,用户可以在画布中添加图像并可以使用它们。
以下是我的要求:
要一个接一个地在画布中添加位图,并且如果在画布中添加了位图并在画布上添加后移动,那么下次用户添加另一个位图时,已添加的位图的位置需要保留----- Canvas默认保存它
我有三个用于检测手势的课程
问题:我能够成功检测到我在画布上添加的位图上的触摸事件,但不能检测先前在画布上添加的先前位图。因此,如果我在画布上添加一个位图,我可以成功检测到它上面的触摸事件,但是如果我添加另一个位图,那么我将无法检测到旧位图上的触摸事件,并且能够检测到新的触摸事件。仅
我几乎尝试过所有事情,但我无法获得任何见解。
这就是我添加画布视图的方式
FrameLayout gcfl = (FrameLayout) findViewById(R.id.gestureControlledFrameLayout);
final GestureViewPort gvp = new GestureViewPort(this);
gcfl.addView(gvp);
这是我的GestureViewPort.java
public class GestureViewPort extends View {
final static int FLIP_VERTICAL = 1;
final static int FLIP_HORIZONTAL = 2;
private Context context;
private static Bitmap bitmap;
public static List<Layer> layers = new ArrayList<Layer>();
private List<Bitmap> bitmapList;
public GestureViewPort(Context context) {
super(context);
this.context = context;
this.bitmapList = ProductDetailActivity.bitmapList;
if(bitmapList!=null)
{
for (int bitmapIndex = 0; bitmapIndex < bitmapList.size();bitmapIndex++)
{
if(bitmapList.get(bitmapIndex)!=null )
{
Layer l2 = new Layer(context, this, bitmapList.get(bitmapIndex));
for(int layerIndex = 0 ; layerIndex < layers.size();layerIndex++)
{
if(bitmapList.get(bitmapIndex).sameAs(layers.get(layerIndex).getBitmap()))
{
if(layers.get(layerIndex).getMoveMatrix()!=null)
{
l2.setMoveMatrix(layers.get(layerIndex).getMoveMatrix());
}
layers.remove(layers.get(layerIndex));
break;
}
}
layers.add(l2);
invalidate();
}
}
}
}
@Override
protected void onDraw(Canvas canvas) {
for(Layer l : layers)
{
if(l.getMoveMatrix() != null)
{
l.draw(canvas,l.getMoveMatrix());
}
else
{
l.draw(canvas,null);
}
}
private Layer target;
@SuppressLint("ClickableViewAccessibility") @Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
target = null;
for (int i = layers.size() - 1; i >= 0; i--)
{
Layer l = layers.get(i);
if (l.contains(event)) {
target = l;
layers.remove(l);
layers.add(l);
invalidate();
break;
}
}
}
if (target == null) {
return false;
}
return target.onTouchEvent(event);
}
public int getTopLayer(){
return layers.size() -1;
}
public void removeLayer(int topLayer){
layers.remove(topLayer);
invalidate();
}
public void addLayer(Bitmap drawable){
Layer l = new Layer(context, this, drawable);
layers.add(l);
invalidate();
}
public void flip(int type){
target.flip(type);
}
}
这是我的Layer.java
public class Layer {
Matrix matrix = new Matrix();
Matrix inverse = new Matrix();
RectF bounds;
View parent;
Bitmap bitmap;
Context context;
MoveGestureDetector mgd;
ScaleGestureDetector sgd;
RotateGestureDetector rgd;
Matrix moveMatrix ;
public Layer(){}
public Layer(Context ctx, View p, Bitmap b) {
this.context = ctx;
parent = p;
bitmap = b;
bounds = new RectF(0, 0, b.getWidth(), b.getHeight());
mgd = new MoveGestureDetector(context, mgl);
sgd = new ScaleGestureDetector(context, sgl);
rgd = new RotateGestureDetector(context, rgl);
matrix.postTranslate(50 + (float) Math.random() * 50, 50 + (float) Math.random() * 50);
}
/**
* @return the bitmap
*/
public Bitmap getBitmap() {
return bitmap;
}
/**
* @return the moveMatrix
*/
public Matrix getMoveMatrix() {
return moveMatrix;
}
/**
* @param moveMatrix the moveMatrix to set
*/
public void setMoveMatrix(Matrix moveMatrix) {
this.moveMatrix = moveMatrix;
}
public boolean contains(MotionEvent event) {
matrix.invert(inverse);
float[] pts = {event.getX(), event.getY()};
Log.e("points",String.valueOf(pts[0])+" " + String.valueOf(pts[1]));
Log.e("bounds",String.valueOf(bounds.right)+" " + String.valueOf(bounds.bottom));
inverse.mapPoints(pts);
if (!bounds.contains(pts[0], pts[1])) {
return false;
}
return Color.alpha(bitmap.getPixel((int) pts[0], (int) pts[1])) != 0;
}
public boolean onTouchEvent(MotionEvent event) {
mgd.onTouchEvent(event);
sgd.onTouchEvent(event);
rgd.onTouchEvent(event);
return true;
}
public void draw(Canvas canvas, Matrix modifiedMatrix) {
if(modifiedMatrix !=null)
{
canvas.drawBitmap(bitmap, modifiedMatrix, null);
}
else
{
canvas.drawBitmap(bitmap, matrix, null);
}
}
SimpleOnMoveGestureListener mgl = new SimpleOnMoveGestureListener() {
@Override
public boolean onMove(MoveGestureDetector detector) {
PointF delta = detector.getFocusDelta();
matrix.postTranslate(delta.x, delta.y);
setMoveMatrix(matrix);
parent.invalidate();
return true;
}
};
SimpleOnScaleGestureListener sgl = new SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
parent.invalidate();
return true;
}
};
SimpleOnRotateGestureListener rgl = new SimpleOnRotateGestureListener() {
@Override
public boolean onRotate(RotateGestureDetector detector) {
matrix.postRotate(-detector.getRotationDegreesDelta(), detector.getFocusX(), detector.getFocusY());
parent.invalidate();
return true;
};
};
}
这是我的MoveGestureDetector.java
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;
}
}
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;
}
/**
* Determine (multi)finger focal point (a.k.a. center point between all
* fingers)
*
* @param MotionEvent e
* @return PointF focal point
*/
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.java
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);
}
public float getFocusX() {
return mCurrEvent.getX() + mCurrFingerDiffX * 0.5f;
}
public float getFocusY() {
return mCurrEvent.getY() + mCurrFingerDiffY * 0.5f;
}
}