1
votes

I am currently implementing a view in Android that involves using a larger than the screen size bitmap as a background and then having drawables drawn ontop of this. This is so as to simulate a "map" that can be scrolled horizontally aswell as vertically.

Which is done by using a canvas and then drawing to this the full "map" bitmap, then putting the other images on top as an overlay and then drawing only the viewable bit of this to screen.

Overriding the touch events to redraw the screen on a scroll/fling.

I'm sure this probably has a huge ammount of overhead (by creating a canvas of the full image whilst using(drawing) only a fifth of it) and could be done in a different way as to the explained, but I was just wondering what people would do in this situation, and perhaps examples?

If you need more info just let me know,

Thanks,

Simon

3

3 Answers

3
votes

I cooked up an example of how to do this using the BitmapRegionDecoder API. The example has a large (6000,4000) image of the world that the user can scroll around in full resolution. The initial tag is quite small and easy to understand.

0
votes

I write this class to an project I'm developing.

public class ScrollableImage extends View {
    private Bitmap bmLargeImage; // bitmap large enough to be scrolled
    private Rect displayRect = null; // rect we display to
    private Rect scrollRect = null; // rect we scroll over our bitmap with
    private int scrollRectX = 0; // current left location of scroll rect
    private int scrollRectY = 0; // current top location of scroll rect
    private float scrollByX = 0; // x amount to scroll by
    private float scrollByY = 0; // y amount to scroll by

    private int width, height;

    private Paint background;

    public ScrollableImage(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollableImage(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setSize(int width, int height) {
        background = new Paint();
        background.setColor(Color.WHITE);
        this.width = width;
        this.height = height;

        // Destination rect for our main canvas draw. It never changes.
        displayRect = new Rect(0, 0, width, height);
        // Scroll rect: this will be used to 'scroll around' over the
        // bitmap in memory. Initialize as above.
        scrollRect = new Rect(0, 0, width, height);
        // scrollRect = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
    }

    public void setImage(Bitmap bmp) {
        if (bmLargeImage != null)
            bmLargeImage.recycle();

    bmLargeImage = bmp;
        scrollRect = new Rect(0, 0, width, height);
        scrollRectX = 0;
        scrollRectY = 0;
        scrollByX = 0;
        scrollByY = 0;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return true; // done with this event so consume it
    }

    public void notifyScroll(float distX, float distY) {
        scrollByX = distX; // move update x increment
        scrollByY = distY; // move update y increment
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (bmLargeImage == null)
            return;

        if (scrollByX != 0 || scrollByY != 0) {
            // Our move updates are calculated in ACTION_MOVE in the opposite     direction
            // from how we want to move the scroll rect. Think of this as
            // dragging to
            // the left being the same as sliding the scroll rect to the right.
            int newScrollRectX = scrollRectX - (int) scrollByX;
            int newScrollRectY = scrollRectY - (int) scrollByY;
            scrollByX = 0;
            scrollByY = 0;

            // Don't scroll off the left or right edges of the bitmap.
            if (newScrollRectX < 0)
                newScrollRectX = 0;
            else if (newScrollRectX > (bmLargeImage.getWidth() - width))
                newScrollRectX = (bmLargeImage.getWidth() - width);

            // Don't scroll off the top or bottom edges of the bitmap.
            if (newScrollRectY < 0)
                newScrollRectY = 0;
            else if (newScrollRectY > (bmLargeImage.getHeight() - height))
                newScrollRectY = (bmLargeImage.getHeight() - height);
            scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX
                    + width, newScrollRectY + height);

            scrollRectX = newScrollRectX;
            scrollRectY = newScrollRectY;
        }

        canvas.drawRect(displayRect, background);
        // We have our updated scroll rect coordinates, set them and draw.
        canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, background);

    }
}

And in the gesture listener I has this implementation of onScroll

Where img is your ScrollableImage instance.

Remember to use the setImage with your large image. Edit: Also use setSize to set the size of your display.

        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {
                img.notifyScroll(-distanceX, -distanceY);
                img.invalidate();
            return true;
        }
0
votes

I would divide the huge image into tiles and then draw the appropriate tiles depending on which part of the image has to be shown. Pretty much what Google Maps does. You can check http://openzoom.org/. There is nothing in Android but I think you can follow the same approach.