图库内的图像缩放/平移

时间:2011-02-16 02:44:25

标签: android

我正在尝试在android gallery小部件中缩放/平移图像。图像覆盖全屏。虽然我可以在库中缩放/平移图像,但我无法滑动到下一张/上一张图像。单张图像的缩放和平移工作正常。

我创建了一个TouchImageView,它扩展了ImageView,可以从Hello Android书籍中进行缩放和平移。然后我在Adapter类的getView()方法中返回了这个TouchImageView,它将图像返回到Gallery。

我在http://groups.google.com/group/android-developers/msg/97421179bfc5a3b2的Google群组中发现了完全相同的问题,但没有回复。

谢谢。

2 个答案:

答案 0 :(得分:5)

我通过创建一个从Gallery小部件类派生的新类来解决这个问题。我打电话给我的ZoomableGallery。我已经实现了一些手势监听器来处理缩放和双击以便在非多点触控或2.0之前的设备上进行缩放。

public class ZoomableGallery extends Gallery implements OnDoubleTapListener, OnGestureListener, OnScaleGestureListener {}

关键是不要让你的内部小部件消耗触摸事件。创建一个响应触摸事件并放大和缩小的ZoomablePannableImageView似乎是正确的想法。这似乎是一个好主意,因为这个可重用的组件也可以在Gallery之外运行良好。但是我认为它不能很好地运作。最好的方法是创建一个不处理触摸事件或设置任何gesturelisteners的ZoomableImageView,而是提供一个api,用于将X,Y维度中的比例因子和平移设置为常规方法。

然后,一旦你走了这条路线,你可以让你的Gallery小部件的后代巧妙地处理所有的触摸事件,只转发需要去的小部件的触摸动作。我的意思是,如果我们放大图像,我们几乎观察整个图像,除了左边的4个像素在屏幕之外。如果我们收到触摸事件,则将图像向右滚动8个像素。我们的图库小部件需要向图像小部件发送平移4像素的正确消息。然后它还需要消耗4个像素的水平运动本身。因此,不仅图像视图显示它的完整左边缘,而且它向右滑动一点,可能会在图库适配器中显示下一个图像视图。

在扩展Gallery的新类中覆盖的关键函数是:

    @Override
public boolean onTouchEvent(MotionEvent event) {}

    @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
        float distanceY) {}

我不想复制和粘贴我的整个实现,因为它很麻烦且难以理解,并且它还可以在压力下显示一些小故障行为。但我不认为这是因为我采用的策略,只是因为我还没有清理它。

关键是要记住用户所处的模式(在图像内部进行缩放或平移,平移整个图库)

如果用户在图像内部进行平移,那么我的代码就像处理onScroll方法中的switch语句一样:

        case INNERDRAG:
        float unhandledPan = mCurrentPannable.panHorizontally(distanceX);

        // Negative unhandled pan means that we are shifting over to the image to the right

        if (unhandledPan != 0.0f) {
            if (unhandledPan < 0.0f)
                mMode = GALPANRIGHT;
            else
                mMode = GALPANLEFT;

            return super.onScroll(e1,e2,0.0f-unhandledPan,distanceY);

        } else {
            return true;
        }       

在此代码中,mCurrentPannable引用图库当前“选中”的视图。 Pannable只是一个界面,它将panHorizo​​ntally和panVertically定义为执行两项操作的函数:尝试通过如此多的像素平移内部视图,如果平移量超出它可以平移的边缘,则返回像素数它无法处理。

然后画廊而不是将相同的参数传递给super.onScroll,它只传递泛没有消耗的东西。

我希望这会有所帮助。

答案 1 :(得分:1)

这是让你开始的东西。它确实需要一些工作,当我解决它时我会更新它。此代码需要安装Android 2.2,但您可以通过从AOSP获取ScaleGestureDetector来使其在早期设备上运行。

public class FullGallery extends Gallery implements OnDoubleTapListener, OnGestureListener, OnScaleGestureListener {
static final int NONE = 0;
static final int DRAG = 1;  
int mode = NONE;
String TAG = "Gallery";


private Context c;
private final LayoutInflater mInflater;

private ScaleGestureDetector mScaleDetector;
private GestureDetector mDetector;
private float mScaleFactor = 1.f;

float new_distance_touch, old_distance_touch, init_x, init_y;

Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();

PointF mid = new PointF();
PointF start = new PointF();

ImageView imgPicture;

public FullGallery(Context context, AttributeSet attrSet) {
    super(context, attrSet);
    mInflater = LayoutInflater.from(context);
    c = context;
    mDetector = new GestureDetector(c,this);
    mScaleDetector = new ScaleGestureDetector(c, this);
}

private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2){
    return e2.getX() > e1.getX();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    imgPicture = (ImageView) super.getSelectedView();

    if (mDetector.onTouchEvent(event)) {
        Log.d("onTouchEvent", "--[ MOVEMENT ]--");
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            init_x = event.getX();
            init_y = event.getY();
            midPoint(mid, event);
            savedMatrix.set(matrix);
            start.set(event.getX(), event.getY());
            mode = DRAG;
            break;
        case MotionEvent.ACTION_MOVE:
            if (mode == DRAG) {
                matrix.set(savedMatrix);
                matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
            }
            break;
        }

        imgPicture = (ImageView) super.getSelectedView();
        imgPicture.setImageMatrix(matrix);

        return true;
    }
    else if(mScaleDetector.onTouchEvent(event)) { // scale detector for zoom
        Log.d("onTouchEvent", "--[ SCALE ]--");
        return true;
    }
    else 
        return false;
}


@Override
public boolean onScale(ScaleGestureDetector detector) {

    mScaleFactor *= detector.getScaleFactor();
    mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));

    if (new_distance_touch > 10f) {
        matrix.set(savedMatrix);
        matrix.postScale(mScaleFactor, mScaleFactor, mid.x, mid.y);
        Log.d("ZOOMMING",matrix.toShortString());
    }
    else {
        matrix.set(savedMatrix);
        matrix.postTranslate(init_x - start.x, init_y - start.y);
        Log.d("PANNING",matrix.toShortString());
    }

    imgPicture.setImageMatrix(matrix);

    imgPicture.invalidate();

    Log.d("MATRIX", matrix.toString());
    return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
    Log.d(TAG, "-- onScaleBegin --");
    matrix = imgPicture.getImageMatrix();
    savedMatrix.set(matrix);
    start.set(init_x, init_y);
    return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {
    Log.d(TAG, "-- onScaleEnd --");
    old_distance_touch = detector.getPreviousSpan();
    new_distance_touch = detector.getCurrentSpan();

}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    Log.d(TAG, "-- onFling --");

    float velMax = 2500f;
    float velMin = 1000f;
    float velX = Math.abs(velocityX);
    if (velX > velMax) {
      velX = velMax;
    } else if (velX < velMin) {
      velX = velMin;
    }
    velX -= 600;
    int k = 500000;
    int speed = (int) Math.floor(1f / velX * k);
    setAnimationDuration(speed);

    int kEvent;
    if (isScrollingLeft(e1, e2)) {
      kEvent = KeyEvent.KEYCODE_DPAD_LEFT;
    } else {
      kEvent = KeyEvent.KEYCODE_DPAD_RIGHT;
    }
    onKeyDown(kEvent, null);

    return true;
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    Log.d(TAG, "-- onScroll --");
    return super.onScroll(e1, e2, distanceX, distanceY);
}




private void midPoint(PointF point, MotionEvent event) {
    float x = event.getX(0) + event.getX(1);
    float y = event.getY(0) + event.getY(1);
    point.set(x / 2, y / 2);
}

@Override
public void onGesture(GestureOverlayView overlay, MotionEvent event) {
    // TODO Auto-generated method stub

}

@Override
public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
    // TODO Auto-generated method stub

}

@Override
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
    // TODO Auto-generated method stub

}

@Override
public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
    // TODO Auto-generated method stub

}

@Override
public boolean onDoubleTap(MotionEvent e) {
    // TODO Auto-generated method stub
    return false;
}

@Override
public boolean onDoubleTapEvent(MotionEvent e) {
    // TODO Auto-generated method stub
    return false;
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
    // TODO Auto-generated method stub
    return false;
}