【问题标题】:Image zooming/panning inside of a Gallery图库内的图像缩放/平移
【发布时间】:2011-02-16 02:44:24
【问题描述】:

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

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

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

谢谢。

【问题讨论】:

  • 不能有人为这个问题制作教程/库吗?似乎很常见,并且有大量的“半”解决方案。不过还没有找到好的:-(

标签: android


【解决方案1】:

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

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

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

然后,一旦你走这条路,你就可以让你的画廊小部件的后代巧妙地处理所有的触摸事件,只转发需要去小部件的触摸动作片段。我的意思是如果说我们放大了图像并且我们正在查看几乎整个图像,除了屏幕左侧的 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,它只传递平底锅未使用的内容。

我希望这会有所帮助。

【讨论】:

  • 嗨,Eric,我需要您的帮助 ....您能否将 Android 画廊的缩放和平移代码发送给我。我在这方面面临问题。提前致谢
  • 嗨,Eric,我也需要你的帮助。在互联网上搜索了一天的解决方案后,您似乎是唯一一个在 Android 中完成了可缩放图库的人。您介意与我们分享您的代码吗?非常感谢。
  • 你好埃里克,我在很多方面都在做同样的事情,但离成功还很远,你愿意和我分享你的 ZoomableGallery 吗?在此先感谢...
  • 嗨@Eric,我面临同样的问题。过去5天让我很头疼。我尝试了很多代码,但都达不到要求。你能分享你的代码吗?这样我们就可以摆脱这个了。等待你的 rply
【解决方案2】:

以下内容可以帮助您入门。不过,它确实需要一些工作,当我解决它时,我会更新它。此代码需要 Android 2.2 才能运行,但您可以通过将 ScaleGestureDetector 从 AOSP 中取出来使其在早期设备上运行。

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;
}

【讨论】:

    猜你喜欢
    • 2010-10-19
    • 2016-07-22
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    相关资源
    最近更新 更多