1

I need to make an application where i'm drawing images on a canvas. When there are on the screen its need to be possible to pinch/pan on the canvas.When i try this code it doesn't correctly zoom on my nexus 5. What is the solution for it?

public class ZoomView extends View {

//These two constants specify the minimum and maximum zoom
private static float MIN_ZOOM = 1f;
private static float MAX_ZOOM = 5f;

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

//These constants specify the mode that we're in
private static int NONE = 0;
private static int DRAG = 1;
private static int ZOOM = 2;

private int mode;

//These two variables keep track of the X and Y coordinate of the finger when it first
//touches the screen
private float startX = 0f;
private float startY = 0f;

//These two variables keep track of the amount we need to translate the canvas along the X
//and the Y coordinate
private float translateX = 0f;
private float translateY = 0f;

//These two variables keep track of the amount we translated the X and Y coordinates, the last time we
//panned.
private float previousTranslateX = 0f;
private float previousTranslateY = 0f;    

public ZoomView(Context context) {
    super(context);
    detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction() & MotionEvent.ACTION_MASK) {

        case MotionEvent.ACTION_DOWN:
            mode = DRAG;

            //We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
            //amount for each coordinates This works even when we are translating the first time because the initial
            //values for these two variables is zero.               
            startX = event.getX() - previousTranslateX;
            startY = event.getY() - previousTranslateY;
            break;

        case MotionEvent.ACTION_MOVE:               
            translateX = event.getX() - startX;
            translateY = event.getY() - startY;

            //We cannot use startX and startY directly because we have adjusted their values using the previous translation values. 
            //This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
            double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) + 
                                        Math.pow(event.getY() - (startY + previousTranslateY), 2)
                                       );

            if(distance > 0) {
               dragged = true;
            }               

            break;

        case MotionEvent.ACTION_POINTER_DOWN:
            mode = ZOOM;
            break;

        case MotionEvent.ACTION_UP:
            mode = NONE;
            dragged = false;

            //All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and 
            //previousTranslate           
            previousTranslateX = translateX;
            previousTranslateY = translateY;
            break;

        case MotionEvent.ACTION_POINTER_UP:
            mode = DRAG;

            //This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
            //and previousTranslateY when the second finger goes up
            previousTranslateX = translateX;
            previousTranslateY = translateY;
            break;       
    }

    detector.onTouchEvent(event);

    //We redraw the canvas only in the following cases:
    //
    // o The mode is ZOOM 
    //        OR
    // o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
    //   set to true (meaning the finger has actually moved)
    if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM) {
        invalidate();
    }

    return true;
}

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();

    //We're going to scale the X and Y coordinates by the same amount
    canvas.scale(scaleFactor, scaleFactor);

    //If translateX times -1 is lesser than zero, let's set it to zero. This takes care of the left bound
    if((translateX * -1) < 0) {
       translateX = 0;
    }

    //This is where we take care of the right bound. We compare translateX times -1 to (scaleFactor - 1) * displayWidth.
    //If translateX is greater than that value, then we know that we've gone over the bound. So we set the value of 
    //translateX to (1 - scaleFactor) times the display width. Notice that the terms are interchanged; it's the same
    //as doing -1 * (scaleFactor - 1) * displayWidth
    else if((translateX * -1) > (scaleFactor - 1) * displayWidth) {
       translateX = (1 - scaleFactor) * displayWidth;
    }

    if(translateY * -1 < 0) {
       translateY = 0;
    }

    //We do the exact same thing for the bottom bound, except in this case we use the height of the display
    else if((translateY * -1) > (scaleFactor - 1) * displayHeight) {
       translateY = (1 - scaleFactor) * displayHeight;
    }

    //We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
    //because the translation amount also gets scaled according to how much we've zoomed into the canvas.
    canvas.translate(translateX / scaleFactor, translateY / scaleFactor);

    /* The rest of your canvas-drawing code */
    canvas.restore();
}

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));
        return true;
    }
}

}

Thx in advance

NoiZeR
  • 135
  • 2
  • 11
  • you are overcomplcating simple things, see my answer here: http://stackoverflow.com/questions/21633545/android-imageview-scaling-and-translating-issue – pskink Aug 05 '14 at 13:21
  • Ok thx but he doesn't find the gesturelistener how can i solve this? – NoiZeR Aug 05 '14 at 13:55
  • what gesturelistener? – pskink Aug 05 '14 at 14:15
  • The sgl (simpleonscalegesturelistener), mgl simpleonmovegesturelistener and the rgl (simpleonrotategesturelistener) it didn't compile these – NoiZeR Aug 05 '14 at 14:23
  • does it work on a canvas too? – NoiZeR Aug 05 '14 at 14:25
  • you have them here https://github.com/pskink/android-gesture-detectors, and yes it works on canvas – pskink Aug 05 '14 at 14:43
  • ok it works for 1 separate bitmap but i needs a group of bitmap en zoom in on the entirely group of bitmaps – NoiZeR Aug 05 '14 at 14:55
  • no, it shows 3 layers: {R.drawable.layer0, R.drawable.layer1, R.drawable.ic_launcher} of course you can add as many layers as you want – pskink Aug 05 '14 at 14:59
  • ok i understand but its needs to work like a canvas with 10 the same images. But when you try to zoom that its zooms on the group not on one separate image. – NoiZeR Aug 05 '14 at 15:07
  • i don't understand what you mean – pskink Aug 05 '14 at 15:40
  • For example i want 5 images on a canvas and zoom in on the 5 images. But when i add 5 images to die array ids it will zoom separatly on every image. But i want zoom in on all images at the same time with the same scale. – NoiZeR Aug 05 '14 at 20:57
  • so see Layer.draw(Canvas) method – pskink Aug 06 '14 at 04:24
  • oke i see it but then it will give an error on the contains method with an 08-05 17:23:11.199: E/AndroidRuntime(11602): java.lang.IllegalArgumentException: x must be < bitmap.width(). It will happen on the last line on this method. – NoiZeR Aug 06 '14 at 06:34
  • thx the problem is solved ! ps great code – NoiZeR Aug 06 '14 at 06:49
  • nice to hear its solved – pskink Aug 06 '14 at 06:55
  • now only touch events on the images and im almost done then xD – NoiZeR Aug 06 '14 at 06:57
  • Do you have any idea how i can detect wich image is clicked on the canvas? – NoiZeR Aug 06 '14 at 08:09
  • see private Layer target; and where it is set – pskink Aug 06 '14 at 09:59
  • this is completed but when i set 250 images on the screen with one canvas, then it is buggy en slow – NoiZeR Aug 06 '14 at 12:49
  • so you need to optimize the algorithm somehow, maybe by sorting the layers or something – pskink Aug 06 '14 at 13:00
  • But i have only 1 layer with a canvas of 250 images (all the same images) and when i try to zoom in its realy slow. i think because he needs to draw it over and over again. is there some way to do this not? – NoiZeR Aug 06 '14 at 13:09
  • why dont you combine that 250 images into one big image? – pskink Aug 06 '14 at 13:40
  • i need to make a seatplan of a room and the max number of seats is 250. – NoiZeR Aug 06 '14 at 13:47
  • Do you have some good solution? – NoiZeR Aug 06 '14 at 21:31
  • yes, combine that 250 images into one big image – pskink Aug 07 '14 at 04:41
  • i did this but when you zoom in or out you needs to redraw the image and with 250 images it is realy slow. What can i do? Does i need to show my code maybe for optimization? – NoiZeR Aug 07 '14 at 06:34
  • combine that 250 images into one image, then you will draw only ony image, not 250 – pskink Aug 07 '14 at 06:37
  • Then i would draw my image in the onDraw method in the viewport and not in the draw(Canvas) of the layer – NoiZeR Aug 07 '14 at 06:44
  • no, if those 250 images have to be grouped and scaled/rotated/translated together so whats the difference if you combine them into one big image and draw in Layer.draw method? do i miss something? – pskink Aug 07 '14 at 06:48
  • when create the image there every time you touch ( rotate, move or zoom) he will redraw it – NoiZeR Aug 07 '14 at 07:04
  • yes, but its better to draw one image than draw 250 images, right? – pskink Aug 07 '14 at 07:07
  • yes where does i need to draw the images then? because now ill do it in the draw method of the layer class and it is still slow – NoiZeR Aug 07 '14 at 07:12
  • you will draw ONE image, not 250 images, seems you dont understand what i am trying to say – pskink Aug 07 '14 at 07:14
  • yes i draw 1 image but its to slow that way :s – NoiZeR Aug 07 '14 at 07:18
  • how big is it? how do you draw it? where is the bottleneck? did you try to use a traceview? – pskink Aug 07 '14 at 07:21
  • what i know do is draw my 250 images on the canvas in the draw method. But is this correct? – NoiZeR Aug 07 '14 at 07:22
  • when i try to zoom or move it will lags very hard. but i did't use a traceview. how can i do it with a traceview? – NoiZeR Aug 07 '14 at 07:26
  • 250 images you say? what 250 images? – pskink Aug 07 '14 at 07:26
  • http://imgur.com/OyB3mMe Here you can see my code of the draw method. and seats is an ArrayList>. The Seat object will say where the images will take place on the canvas – NoiZeR Aug 07 '14 at 07:31
  • oh my God... why do you decodeResource in the loop? why do you createScaledBitmap in the loop? why do you createScaledBitmap ar all? why dont you create ONE Bitmap in onCreate and draw it? – pskink Aug 07 '14 at 07:39
  • i didt know that this was the problem :s i'm not so familiar with android – NoiZeR Aug 07 '14 at 07:42
  • But thx for the help hopefully its will be faster now – NoiZeR Aug 07 '14 at 07:47
  • it has nothing to do with android, did you notice lint warnings on the left: Avoid object allocations during draw/layout operations made by eclipse? – pskink Aug 07 '14 at 07:48
  • why do you use something else to develop? – NoiZeR Aug 07 '14 at 08:06
  • i don't use something else, i use eclipse – pskink Aug 07 '14 at 08:08
  • is it possible to set a maximum zoom and een minimum zoom on your code? – NoiZeR Aug 07 '14 at 08:24
  • sure: see SimpleOnScaleGestureListener.onScale – pskink Aug 07 '14 at 08:34

0 Answers0