0

The behavior which i need to attend is preserving whatever i drew according to the zoom level. I was able to achieve this behavior using another solution to the same problem here Canvas - zooming in, shifting, and scaling on Android. However, i faced a problem which may sound ridiculous, however i couldn't manage to solve it. When i call imageView.setImageBitmap(Bitmap b) in my main activity, the bitmap is never shown. It always show a white page to draw on.

Please, how to show my bitmap on the custom view in here ?

Here is the code:

ScaleImageView.java

public class ScaleImageView extends View {


private Bitmap imgBitmap = null;

private int containerWidth;
private int containerHeight;
float[] mv = new float[9];
Paint background;

Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();

PointF start = new PointF();

float currentScale;
float curX;
float curY;

//We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
float targetX;
float targetY;
float targetScale;
float targetScaleX;
float targetScaleY;
float scaleChange;
float targetRatio;
boolean isAnimating = false;
float oldDist = 1f;
PointF mid = new PointF();

private Handler mHandler = new Handler();

float minScale;
float maxScale = 8.0f;
float screenDensity;

private GestureDetector gestureDetector;

public static final int DEFAULT_SCALE_FIT_INSIDE = 0;

private int defaultScale;

private Path drawPath;
public Paint drawPaint;
private Canvas drawCanvas;
Rect clipBounds;

public ArrayList<Path> paths = new ArrayList<>();
public ArrayList<Paint> colors = new ArrayList<>();

// Creates Path and Paint for drawing
public void setUpDrawing() {

    drawPath = new Path();
    drawPaint = new Paint();
    drawPaint.setColor(Color.RED);
    drawPaint.setAntiAlias(true);
    drawPaint.setStrokeWidth(8);
    drawPaint.setStyle(Paint.Style.STROKE);
    drawPaint.setStrokeJoin(Paint.Join.ROUND);
    drawPaint.setStrokeCap(Paint.Cap.ROUND);
}


public ScaleImageView(Context context) {
    super(context);
    setFocusable(true);
    setFocusableInTouchMode(true);
    screenDensity = context.getResources().getDisplayMetrics().density;
    initPaints();
    gestureDetector = new GestureDetector(context, new MyGestureDetector());
    setUpDrawing();
}

public ScaleImageView(Context context, AttributeSet attrs) {
    super(context, attrs);

    screenDensity = context.getResources().getDisplayMetrics().density;
    initPaints();
    gestureDetector = new GestureDetector(context,new MyGestureDetector());

    defaultScale = ScaleImageView.DEFAULT_SCALE_FIT_INSIDE;
    setUpDrawing();
}

private void initPaints() {
    background = new Paint();
    setUpDrawing();
}

@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
    super.onSizeChanged(width, height, oldWidth, oldHeight);

    containerWidth = width;
    containerHeight = height;

    if (imgBitmap != null) {
        drawCanvas = new Canvas(imgBitmap);
        int imgHeight = imgBitmap.getHeight();
        int imgWidth = imgBitmap.getWidth();

        float scale;
        int initX = 0;
        int initY = 0;

        if (defaultScale == ScaleImageView.DEFAULT_SCALE_FIT_INSIDE) {
            if (imgWidth > containerWidth) {
                scale = (float) containerWidth / imgWidth;
                float newHeight = imgHeight * scale;
                initY = (containerHeight - (int) newHeight) / 2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(0, initY);
            } else {
                scale = (float) containerHeight / imgHeight;
                float newWidth = imgWidth * scale;
                initX = (containerWidth - (int) newWidth) / 2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = scale;
            minScale = scale;
        } else {
            if (imgWidth > containerWidth) {
                initY = (containerHeight - (int) imgHeight) / 2;
                matrix.postTranslate(0, initY);
            } else {
                initX = (containerWidth - (int) imgWidth) / 2;
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = 1.0f;
            minScale = 1.0f;
        }


        invalidate();
    }
}

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

    canvas.save();
    if (imgBitmap != null && canvas != null) {
        if (drawCanvas == null)
            drawCanvas = new Canvas(imgBitmap);


        clipBounds = canvas.getClipBounds();

        canvas.drawBitmap(imgBitmap, matrix, background);

        canvas.concat(matrix);
        for (int i = 0; i < colors.size(); i++) {
            canvas.drawPath(paths.get(i), colors.get(i));
        }
        canvas.drawPath(drawPath, drawPaint);

    }
    canvas.restore();
    invalidate();
}

public void reDrawUndo() {
    System.out.println("paths.size" + paths.size());
    if (paths.size() > 0) {
        paths.remove(paths.size() - 1);
        colors.remove(colors.size() - 1);
        invalidate();
    }
}

//Checks and sets the target image x and y co-ordinates if out of bounds
private void checkImageConstraints() {
    if (imgBitmap == null) {
        return;
    }

    float[] mvals = new float[9];
    matrix.getValues(mvals);

    currentScale = mvals[0];

    if (currentScale < minScale) {
        float deltaScale = minScale / currentScale;
        float px = containerWidth / 2;
        float py = containerHeight / 2;
        matrix.postScale(deltaScale, deltaScale, px, py);
        invalidate();
    }

    matrix.getValues(mvals);
    currentScale = mvals[0];
    curX = mvals[2];
    curY = mvals[5];

    int rangeLimitX = containerWidth - (int) (imgBitmap.getWidth() * currentScale);
    int rangeLimitY = containerHeight - (int) (imgBitmap.getHeight() * currentScale);


    boolean toMoveX = false;
    boolean toMoveY = false;

    if (rangeLimitX < 0) {
        if (curX > 0) {
            targetX = 0;
            toMoveX = true;
        } else if (curX < rangeLimitX) {
            targetX = rangeLimitX;
            toMoveX = true;
        }
    } else {
        targetX = rangeLimitX / 2;
        toMoveX = true;
    }

    if (rangeLimitY < 0) {
        if (curY > 0) {
            targetY = 0;
            toMoveY = true;
        } else if (curY < rangeLimitY) {
            targetY = rangeLimitY;
            toMoveY = true;
        }
    } else {
        targetY = rangeLimitY / 2;
        toMoveY = true;
    }

    if (toMoveX == true || toMoveY == true) {
        if (toMoveY == false) {
            targetY = curY;
        }
        if (toMoveX == false) {
            targetX = curX;
        }

        //Disable touch event actions
        isAnimating = true;
        //Initialize timer
        mHandler.removeCallbacks(mUpdateImagePositionTask);
        mHandler.postDelayed(mUpdateImagePositionTask, 100);
    }
}


@Override
public boolean onTouchEvent(MotionEvent event) {

    matrix.getValues(mv);

    float touchX = (event.getX() * (1 / mv[4]) - (mv[2] / mv[4]));
    float touchY = (event.getY() * (1 / mv[4]) - (mv[5] / mv[4]));

    // Is drawing mode on?
    if (ScaleImageActivity.flag) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                drawPath.moveTo(touchX, touchY);
                break;
            case MotionEvent.ACTION_MOVE:
                drawPath.lineTo(touchX, touchY);
                break;
            case MotionEvent.ACTION_UP:
                Paint newPaint = new Paint();
                newPaint.set(drawPaint);
                colors.add(newPaint);
                paths.add(drawPath);
                drawPath = new Path();
                drawPath.reset();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                System.out.println("bleh");
                break;
            default:
                return false;
        }
    } else {
        if (gestureDetector.onTouchEvent(event)) {
            return true;
        }

        if (isAnimating == true) {
            return true;
        }

        //Handle touch events here
        float[] mvals = new float[9];
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                if (isAnimating == false) {
                    savedMatrix.set(matrix);
                    start.set(event.getX(), event.getY());
                    mode = DRAG;
                }
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;

                matrix.getValues(mvals);
                curX = mvals[2];
                curY = mvals[5];
                currentScale = mvals[0];

                if (isAnimating == false) {
                    checkImageConstraints();
                }
                break;

            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG && isAnimating == false) {
                    matrix.set(savedMatrix);
                    float diffX = event.getX() - start.x;
                    float diffY = event.getY() - start.y;

                    matrix.postTranslate(diffX, diffY);

                    matrix.getValues(mvals);
                    curX = mvals[2];
                    curY = mvals[5];
                    currentScale = mvals[0];
                } else if (mode == ZOOM && isAnimating == false) {
                    float newDist = spacing(event);
                    if (newDist > 10f) {
                        matrix.set(savedMatrix);
                        float scale = newDist / oldDist;
                        matrix.getValues(mvals);
                        currentScale = mvals[0];

                        if (currentScale * scale <= minScale) {
                            matrix.postScale(minScale / currentScale, minScale / currentScale, mid.x, mid.y);
                        } else if (currentScale * scale >= maxScale) {
                            matrix.postScale(maxScale / currentScale, maxScale / currentScale, mid.x, mid.y);
                        } else {
                            matrix.postScale(scale, scale, mid.x, mid.y);
                        }


                        matrix.getValues(mvals);
                        curX = mvals[2];
                        curY = mvals[5];
                        currentScale = mvals[0];
                    }
                }

                break;
        }
    }
    //Calculate the transformations and then invalidate
    invalidate();
    return true;
}

private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
}

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


public void setImageBitmap(Bitmap b) {

    if (b != null) {

        imgBitmap = b;

        containerWidth = getWidth();
        containerHeight = getHeight();

        int imgHeight = imgBitmap.getHeight();
        int imgWidth = imgBitmap.getWidth();

        float scale;
        int initX = 0;
        int initY = 0;

        matrix.reset();

        if (defaultScale == ScaleImageView.DEFAULT_SCALE_FIT_INSIDE) {
            if (imgWidth > containerWidth) {
                scale = (float) containerWidth / imgWidth;
                float newHeight = imgHeight * scale;
                initY = (containerHeight - (int) newHeight) / 2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(0, initY);
            } else {
                scale = (float) containerHeight / imgHeight;
                float newWidth = imgWidth * scale;
                initX = (containerWidth - (int) newWidth) / 2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = scale;
            minScale = scale;
        } else {
            if (imgWidth > containerWidth) {
                initX = 0;
                if (imgHeight > containerHeight) {
                    initY = 0;
                } else {
                    initY = (containerHeight - (int) imgHeight) / 2;
                }

                matrix.postTranslate(0, initY);
            } else {
                initX = (containerWidth - (int) imgWidth) / 2;
                if (imgHeight > containerHeight) {
                    initY = 0;
                } else {
                    initY = (containerHeight - (int) imgHeight) / 2;
                }
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = 1.0f;
            minScale = 1.0f;
        }
     }

        invalidate();

                         }


private Runnable mUpdateImagePositionTask = new Runnable() {
    public void run() {
        float[] mvals;

        if (Math.abs(targetX - curX) < 5 && Math.abs(targetY - curY) < 5) {
            isAnimating = false;
            mHandler.removeCallbacks(mUpdateImagePositionTask);

            mvals = new float[9];
            matrix.getValues(mvals);

            currentScale = mvals[0];
            curX = mvals[2];
            curY = mvals[5];

            //Set the image parameters and invalidate display
            float diffX = (targetX - curX);
            float diffY = (targetY - curY);

            matrix.postTranslate(diffX, diffY);
        } else {
            isAnimating = true;
            mvals = new float[9];
            matrix.getValues(mvals);

            currentScale = mvals[0];
            curX = mvals[2];
            curY = mvals[5];

            //Set the image parameters and invalidate display
            float diffX = (targetX - curX) * 0.3f;
            float diffY = (targetY - curY) * 0.3f;

            matrix.postTranslate(diffX, diffY);
            mHandler.postDelayed(this, 25);
        }

        invalidate();
    }
};

private Runnable mUpdateImageScale = new Runnable() {
    public void run() {
        float transitionalRatio = targetScale / currentScale;
        float dx;
        if (Math.abs(transitionalRatio - 1) > 0.05) {
            isAnimating = true;
            if (targetScale > currentScale) {
                dx = transitionalRatio - 1;
                scaleChange = 1 + dx * 0.2f;

                currentScale *= scaleChange;

                if (currentScale > targetScale) {
                    currentScale = currentScale / scaleChange;
                    scaleChange = 1;
                }
            } else {
                dx = 1 - transitionalRatio;
                scaleChange = 1 - dx * 0.5f;
                currentScale *= scaleChange;

                if (currentScale < targetScale) {
                    currentScale = currentScale / scaleChange;
                    scaleChange = 1;
                }
            }


            if (scaleChange != 1) {
                matrix.postScale(scaleChange, scaleChange, targetScaleX, targetScaleY);
                mHandler.postDelayed(mUpdateImageScale, 15);
                invalidate();
            } else {
                isAnimating = false;
                scaleChange = 1;
                matrix.postScale(targetScale / currentScale, targetScale / currentScale, targetScaleX, targetScaleY);
                currentScale = targetScale;
                mHandler.removeCallbacks(mUpdateImageScale);
                invalidate();
                checkImageConstraints();
            }
        } else {
            isAnimating = false;
            scaleChange = 1;
            matrix.postScale(targetScale / currentScale, targetScale / currentScale, targetScaleX, targetScaleY);
            currentScale = targetScale;
            mHandler.removeCallbacks(mUpdateImageScale);
            invalidate();
            checkImageConstraints();
        }
    }
};

class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onDoubleTap(MotionEvent event) {
        if (isAnimating == true) {
            return true;
        }

        scaleChange = 1;
        isAnimating = true;
        targetScaleX = event.getX();
        targetScaleY = event.getY();

        if (Math.abs(currentScale - maxScale) > 0.1) {
            targetScale = maxScale;
        } else {
            targetScale = minScale;
        }
        targetRatio = targetScale / currentScale;
        mHandler.removeCallbacks(mUpdateImageScale);
        mHandler.post(mUpdateImageScale);
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return super.onFling(e1, e2, velocityX, velocityY);
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }
}

     }

ScaleImageActivity.java

public class ScaleImageActivity extends Activity implements OnClickListener {

private Button btndraw, btnzoom;
private Bitmap bmp , alteredBitmap;
private ScaleImageView imageview;
public static boolean flag = true;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.batata);

    initwidget();


}

private void initwidget() {

    imageview = (ScaleImageView) findViewById(R.id.image);
    btndraw = (Button) findViewById(R.id.activity_main_zoom_draw);
    btnzoom = (Button) findViewById(R.id.activity_main_zoom_zoom);


    btndraw.setOnClickListener(this);
    btnzoom.setOnClickListener(this);

    Intent choosePictureIntent = new Intent(
            Intent.ACTION_PICK,
            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(choosePictureIntent, 0);

}

@Override
public void onClick(View arg0) {
    // TODO Auto-generated method stub
    if (btndraw.equals(arg0))
        flag = true;

     else if (btnzoom.equals(arg0))
        flag = false;

}

protected void onActivityResult(int requestCode, int resultCode,
                                Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);

    if (resultCode == RESULT_OK) {

        Uri imageFileUri = intent.getData();

        try {

            BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            bmpFactoryOptions.inJustDecodeBounds = true;
          bmp = BitmapFactory
                    .decodeStream(
                            getContentResolver().openInputStream(
                                    imageFileUri), null, bmpFactoryOptions);

            bmpFactoryOptions.inJustDecodeBounds = false;
            bmp = BitmapFactory
                    .decodeStream(
                            getContentResolver().openInputStream(
                                    imageFileUri), null, bmpFactoryOptions);



            alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getWidth(), bmp.getConfig());

            imageview.setImageBitmap(alteredBitmap);


        }

        catch (Exception e) {
            Log.v("ERROR", e.toString());

        }

    }

}

 }

.XML Layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/activity_main_zoom_zoom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Zoom" />

        <Button
            android:id="@+id/activity_main_zoom_draw"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Draw" />

    </LinearLayout>


   <julien.pc.drawing.ScaleImageView
       android:id="@+id/image"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" />

</LinearLayout>
Community
  • 1
  • 1
Julien Raad
  • 51
  • 1
  • 5
  • what do you need `drawCanvas` for? also what is the `Matrix` values used for drawing your `Bitmap`? for testing instead of `canvas.drawBitmap(imgBitmap, matrix, background);` try `canvas.drawBitmap(imgBitmap, 0, 0, null);` – pskink Jun 20 '16 at 14:31
  • BTW, for a similar view see my answer [here](http://stackoverflow.com/a/21657145/2252830) – pskink Jun 20 '16 at 14:36
  • I eliminated `drawCanvas` it is not needed i added it before for testing purposes. `canvas.drawBitmap(imgBitmap, 0, 0, null)` doesn't do the trick as i need the matrix for scaling in here. – Julien Raad Jun 21 '16 at 06:57
  • that's why i asked whats the value of your Matrix, so did you use `Log.d` to show it? i wrote to use `canvas.drawBitmap(imgBitmap, 0, 0, null)` just for testing purposes only if you see anything at all.. – pskink Jun 21 '16 at 07:00
  • Actually as i said in my post, this is not my own solution. I couldn't manage to do it on my own so i used @ono solution on [link](http://stackoverflow.com/questions/20006428/zoomview-canvas-matrix-android/20011005#20011005) and slightly modified it for personal use. He was able to show the image as a background here [link](http://stackoverflow.com/questions/19458094/canvas-zooming-in-shifting-and-scaling-on-android/37922743#37922743) but it isn't working with me. btw thank you for your replies – Julien Raad Jun 21 '16 at 07:18
  • ok if you dont want to `Log.d` your matrix i cannot help more – pskink Jun 21 '16 at 07:24

0 Answers0