画廊内的图像缩放/平移[英] Image zooming/panning inside of a Gallery

本文是小编为大家收集整理的关于画廊内的图像缩放/平移的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在尝试在Android Gallery小部件中缩放/平移图像.图像覆盖全屏.虽然我可以在画廊中缩放/平移图像,但我无法向下/上一个图像滑动.单个图像的缩放和平移工作正常.

我创建了一个CountImageView,它将ImageView扩展到Hello Android书中的缩放和平移.然后我在适配器类的GetView()方法中返回此触摸ImageView,该方法将图像返回到库.

我在 http://groups.google .com/group/android-developers/msg/97421179bfc5a3b2 但没有回复那里.

谢谢.

推荐答案

我通过制作从Gallery小部件类派生的新类来解决了这一点.我打电话给我的zoombablegallery.我有一些手势侦听器来处理缩放和双击非多点触控或预2.0设备的缩放.

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

关键是不具有您的内心小部件消耗触摸事件.它看起来像是创建一个ZoomablePannableImageView的正确想法,以响应触摸事件和缩放进出.这似乎是一个好主意,因为这个可重用的组件也在画廊之外很好.但是,我不认为它可以很好地工作.最好的方法是创建一个ZoomableImageView,它不处理触摸事件或设置任何GesturelyEners,而是提供一个API,用于将比例因子设置为X,Y尺寸作为常规方法.

然后,一旦您走了这条路线,您可以让您的Gallery窗口小部件的后代巧妙地处理所有触摸事件,只能转发需要转到小部件的触摸运动.我的意思是,如果我们说我们被缩放到图像中,我们几乎看出,除了4像素外的屏幕外几乎是整个图像.如果我们收到触摸事件以向右滚动图像8像素.我们的画廊小工具需要将图像小部件发送到刀片4像素正确的消息.然后它需要消耗4个像素的水平运动本身.因此,不仅图像视图不仅显示它是完整的左边缘,而且它略微向右幻灯片,可能会揭示在Gallery适配器中的下一个ImageView.

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

    @Override
public boolean onTouchEvent(MotionEvent event) {}

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

我不想复制并粘贴我的整个实施,因为它凌乱且难以理解,它也可以在压力下显示一些诙谐的行为.但我不认为这是因为我所雇用的策略,而是因为我还没有清理它.

关键是要记住用户处于什么模式(缩放或平移图像内,平移整个画廊)

在用户在映像内的情况下,这里的代码看起来像处理onscroll方法内的交换机语句:

        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只是一个界面,它定义了一个界面,作为执行两件事的函数:尝试通过如此多的像素绕过内部视图,如果平移量将其超出它可以平移的边缘,则返回像素的数量它无法处理.

然后是画廊而不是传递与super.onscroll的相同的参数它只通过锅未分布的东西.

我希望这有帮助.

其他推荐答案

这是一个让你入手的东西.它确实需要一些工作,当我到处时,我会更新这个.此代码需要Android 2.2工作,但您可以通过将ScalegesTuredeter从AOS中获取之前的设备来工作.

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

本文地址:https://www.itbaoku.cn/post/102443.html

问题描述

I am trying to zoom/pan images in the android gallery widget. The images cover the full screen. Though I can zoom/pan images in the gallery, I am not able to swipe to the next/previous images. Zooming and panning for single image works fine.

I created a TouchImageView which extends ImageView with capability of zooming and panning from Hello Android book. Then I returned this TouchImageView in the getView() method of the Adapter class that returns images to the Gallery.

I found exactly same problem in google groups at http://groups.google.com/group/android-developers/msg/97421179bfc5a3b2 but no replies over there.

Thank you.

推荐答案

I solved this by making a new class derived from Gallery widget class. I called mine ZoomableGallery. I had mine implement a few gesture listeners to handle scaling and double tap for zoom on non multi-touch or pre 2.0 devices.

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

The key is to not have your inner widgets consume the touch events. It might seem like the right idea to create a ZoomablePannableImageView that responds to touch events and zooms in and out. And it seems like a great idea because this reusable component would work well outside of the Gallery as well. However I don't think it can be made to work well. The best way is to create a ZoomableImageView that doesn't handle touch events or set any gesturelisteners, but instead provides an api for setting the scale factor and panning in the X,Y dimensions as regular methods.

Then once you've gone this route you can have your descendant of the Gallery widget handle all the touch events smartly only forwarding the pieces of the touch motion that need to go to the widget. And what I mean by this is if say we are zoomed into the image and we are viewing almost the whole image except 4 pixels are out of screen to the left. If we receive a touch event to scroll the image 8 pixels to the right. Our gallery widget needs to send the image widget a pan 4 pixels right message. And then it needs to also consume 4 pixels of horizontal motion itself. So that not only does the image view reveal it's complete left edge, but then it slides a little to the right possibly revealing the next imageview in the gallery adapter.

The key functions to override in your new class that extends Gallery are:

    @Override
public boolean onTouchEvent(MotionEvent event) {}

and

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

I don't want to copy and paste my entire implementation because it is messy and hard to understand and it also can display some glitchy behavior under stress. But I don't think that's because of the strategy I employed but simply because I haven't cleaned it up yet.

The key is to remember what mode the user is in (zooming, or panning inside an image, panning the entire gallery)

In the case where the user is panning inside an image here is what my code looks like handling the switch statement inside of the onScroll method:

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

In this code mCurrentPannable refers to the view that is currently "selected" by the gallery. Pannable is just an interface that defines panHorizontally and panVertically as functions that do two things: attempt to pan the inner view by so many pixels AND if that amount of pan takes it beyond the edges of what it can pan, it returns the number of pixels that it couldn't handle.

Then the gallery instead of passing the same arguments to super.onScroll it passes only what is left unconsumed by the pan.

I hope this helps.

其他推荐答案

Here is something that will get you started. It does need some work though, and I'll update this when I get around to it. This code needs Android 2.2 to work, but you can get it to work on earlier devices by getting ScaleGestureDetector out of 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;
}