Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android issue: View with endless scrolling

I have a custom View with scroll implemented, but it seems like an ENDLESS scrolling over the image.
Even when I find the edge of the image, it keeps scrolling to a blank background.

I can't use a WebView because I also have some Canvas sutff.
Does anyone know how to set limits for this issue?
How to fit the edges of the image for the scrolling?


EDIT: I found the best solution with @JosephEarl help.
I set just left and top bounds because my image is larger than the screen.
Also I turn off the bounds while using the zoom funcionality, otherwise I couldn't move it anymore.

1) In the ACTION_MOVE case of your onTouch event, inser this code:

if(!isZoomed) {
    if(mPosX < 0)
        mPosX = 0;
    else if(mPosX > mWidth)
        mPosX = mWidth;
    if(mPosY < 0)
        mPosY = 0;
    else if(mPosY > mHeight)
        mPosY = mHeight;
}


2) Turn on or turn off the bounds while zoom is used.
Add the following code to your ACTION_POINTER_UP case:

case MotionEvent.ACTION_POINTER_UP: {

    final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    final int pointerId = ev.getPointerId(pointerIndex);

    if (pointerId == mActivePointerId) {
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
        mLastTouchX = ev.getX(newPointerIndex);
        mLastTouchY = ev.getY(newPointerIndex);
        mActivePointerId = ev.getPointerId(newPointerIndex);
        isZoomed = true;

    } else
        isZoomed = false;

    break;

}


And that's all.
Here is all the related methods and complete onTouch event:

private float scaleFactor = 1.f;
private ScaleGestureDetector detector;

private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;

private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;

private float mWidth;
private float mHeight;
private boolean isZoomed = false;

// OTHER CODE GOES HERE

@Override
public boolean onTouchEvent(MotionEvent ev) {
    detector.onTouchEvent(ev);

    final int action = ev.getAction();
    switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            final float x = ev.getX();
            final float y = ev.getY();

            mLastTouchX = x;
            mLastTouchY = y;
            mActivePointerId = ev.getPointerId(0);
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
            final float x = ev.getX(pointerIndex);
            final float y = ev.getY(pointerIndex);

            if (!detector.isInProgress()) {
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;
                mPosX += dx;
                mPosY += dy;

                if(!isZoomed) {
                    if(mPosX < 0)
                        mPosX = 0;
                    else if(mPosX > mWidth)
                        mPosX = mWidth;
                    if(mPosY < 0)
                        mPosY = 0;
                    else if(mPosY > mHeight)
                        mPosY = mHeight;
                }

                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 = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);

            if (pointerId == mActivePointerId) {
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = ev.getX(newPointerIndex);
                mLastTouchY = ev.getY(newPointerIndex);
                mActivePointerId = ev.getPointerId(newPointerIndex);
                isZoomed = true;

            } else
                isZoomed = false;

            break;

        }
    }

    return true;
}

private class ScaleListener extends
        ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scaleFactor *= detector.getScaleFactor();
        scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
        invalidate();
        return true;
    }
}

@Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld){
     super.onSizeChanged(xNew, yNew, xOld, yOld);
     mWidth = xNew;
     mHeight = yNew;
}

// OTHER CODE GOES HERE
like image 725
Rayssa Küllian Avatar asked Nov 20 '12 22:11

Rayssa Küllian


1 Answers

It seems from your code that at no point do you attempt to detect the size of the scrollable area and limit the position to those bounds, i.e. you should check to make sure mPosX and mPosY do not exceed their bounds.

The system will not automatically limit the position of your custom View, that is something you need to do yourself.

The top and left bound will be 0, 0 -- you should make sure mPosX and mPosY are no greater than this.

The right bound will be (width of container view - width of scrolling view) -- this should be negative (if it is greater then set the right bound to be 0) and you should make sure mPosX is not less than this. The bottom bound will be (height of container - height of scrolling view) -- this again should be negative and you should make sure mPosY is not less than that.

To sum up, when the touch event starts calculate the bounds:

// Calculate our bounds.
int leftBound = 0;
int topBound = 0;
int rightBound = imageWidth - getWidth();
if (rightBound > 0) {
    rightBound = 0;
}
int bottomBound = imageHeight - getHeight();
if (bottomBound > 0) {
    bottomBound = 0;
}

Where imageWidth and imageHeight are the width and height of whatever you are scrolling.

Then while scrolling make sure the bounds are obeyed:

if (mPosX > leftBound) {
    mPosX = leftBound;
} else if (mPosX < rightBound) {
    mPosX = rightBound;
}

if (mPosY > topBound) {
    mPosY = topBound;
} else if (mPosY < bottomBound) {
    mPosY = bottomBound;
}
like image 172
Joseph Earl Avatar answered Sep 22 '22 22:09

Joseph Earl