Last Updated
Viewed 06 Times

I am creating an app that loads an image into a custom imageview. This custom imageview allows the user to draw with their finger over the image. The image is loaded into an imageview with picasso. Everything works fine but I am using photoview to zoom but when zoomed in the drawn line on the image does scale with the zoom. Is there any way to achieve this?

No zoom applied: enter image description here

Zoom applied: enter image description here

As you can see the red circle does not scale with the image, I would like it to be tied to the image some how.

Here is my custom imageview that handles all the drawing:

public class PaintImageView extends AppCompatImageView implements View.OnTouchListener {
//set a default max and min dot size so user can change size of drawing line
private final int DEFAULT_DOT_SIZE = 10;
private final int MAX_DOT_SIZE = 100;
private final int MIN_DOT_SIZE = 10;
private int dotSize;

//Set default pen coulour
private int penColour;
private final int DEFAULT_COLOUR = Color.parseColor("#F82323");

//instead of having one path we can have multiple so each colour can be set to new paint object
private ArrayList<Path> pathsArrList;
private ArrayList<Paint> paintsArrList;

private Path path;
private Paint paint;

private float pointX, pointY, oldPointX, oldPointY;

//constructors
public PaintImageView(Context context) {
    super(context);
    this.initVariables();
}

public PaintImageView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    this.initVariables();
}

public PaintImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.initVariables();
}

public void setPenColour(int penColour) {
    this.penColour = penColour;

}

public int getPenColour() {
    return penColour;
}

//Initialize Instance variables
private void initVariables() {
    dotSize = DEFAULT_DOT_SIZE;
    penColour = DEFAULT_COLOUR;

    this.pathsArrList = new ArrayList<>();
    this.paintsArrList = new ArrayList<>();

    path = new Path();
    this.pointX = this.pointY = this.oldPointX = this.oldPointY = (float) 0.0;
    this.setOnTouchListener(this);

    this.addPath(false);
}

//Adds path and paint to ArrayLists
private void addPath(boolean fill){
    path = new Path();
    pathsArrList.add(path);
    paint = new Paint();
    paintsArrList.add(paint);

    paint.setColor(penColour);

    //Decide whether you want to fill cirlce or set stroke
    if(!fill){
        paint.setStyle(Paint.Style.STROKE);
        }

    paint.setStrokeWidth(dotSize);
}

public String getDotSize(){

    return String.valueOf(dotSize);
}

//Change size of line to draw
public void changeDotSize(int increment){
    this.dotSize += increment;
    this.dotSize = Math.max(dotSize,MIN_DOT_SIZE);
    this.dotSize = Math.min(dotSize,MAX_DOT_SIZE);
}

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

    //iterate through arrLists and draw them all instead of one
    for(int i = 0; i < pathsArrList.size(); i++){
        canvas.drawPath(pathsArrList.get(i),paintsArrList.get(i));



    }
}

//Functionality for reset button
public void resetPaint(){
    this.initVariables();
    this.invalidate();
}

public void reDraw(){
    path = new Path();
    this.pointX = this.pointY = this.oldPointX = this.oldPointY = (float) 0.0;
    this.setOnTouchListener(this);

    this.addPath(false);
}

//Handle on touch Events for paint drawn by user
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    pointX = motionEvent.getX();
    pointY = motionEvent.getY();

    switch(motionEvent.getAction()){
        case MotionEvent.ACTION_DOWN:
            this.addPath(true);
            this.path.addCircle(pointX,pointY,dotSize/2,Path.Direction.CW);
            this.addPath(false);
            this.path.moveTo(pointX,pointY);
            break;
            case MotionEvent.ACTION_MOVE:
                this.path.lineTo(pointX,pointY);
                break;
                case MotionEvent.ACTION_UP:
                    this.addPath(true);
                    if(oldPointX == pointX && oldPointY == pointY){
                        //If they match put a circle on screen at this location
                        this.path.addCircle(pointX,pointY,dotSize/2,Path.Direction.CW);
                    }
                    break;
    }

    this.invalidate();

    //update old values to new values to track on touch paint
    oldPointX = pointX;
    oldPointY = pointY;

    return true;
}

}

Here is the classthat uses picasso to load image:

public class EditMapImage extends AppCompatActivity implements View.OnClickListener {

//Create new object of PaintImageView
private PaintImageView paintImageView;

PhotoViewAttacher photoViewAttacher;

//Instance Variables
//Find ImageButtons, Buttons, Textview and declare and initialise dot size increment values
private ImageButton saveMapButton, resetButton;
private Button redColourButton, purpleColourButton, greenColourButton, dotSizePlus, dotSizeMinus;
private TextView displayDotSize;
private static final int DOT_SIZE_INCREMENT = 10;

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

    checkIntent();


}

private void initializeVariables() {
    //Find buttons and associate with variables
    saveMapButton = findViewById(R.id.saveEditImagebutton);
    resetButton = findViewById(R.id.resetButton);
    redColourButton = findViewById(R.id.redButton);
    purpleColourButton = findViewById(R.id.purpleButton);
    greenColourButton = findViewById(R.id.greenButton);
    dotSizePlus = findViewById(R.id.dotPlusButton);
    dotSizeMinus = findViewById(R.id.dotMinusButton);

    //Set on click listeners
    saveMapButton.setOnClickListener(this);
    resetButton.setOnClickListener(this);
    redColourButton.setOnClickListener(this);
    purpleColourButton.setOnClickListener(this);
    greenColourButton.setOnClickListener(this);
    dotSizePlus.setOnClickListener(this);
    dotSizeMinus.setOnClickListener(this);

    paintImageView = findViewById(R.id.mapEditScreen);

    //Update textview dotSize with the current size of dot by increments
    displayDotSize = findViewById(R.id.dotSize);
    displayDotSize.setText(paintImageView.getDotSize());
}

//This will check to see if the intent extras exist and if they do get the extra
private void checkIntent(){
    if(getIntent().hasExtra("image_url") && getIntent().hasExtra("name_url")){

        String imageUrl = getIntent().getStringExtra("image_url");
        String nameUrl = getIntent().getStringExtra("name_url");

        setMapImage(imageUrl, nameUrl);

    }
}

Matrix matrix = new Matrix();

private void setMapImage(final String imageUrl, String nameUrl){

    //Set the Text view
    TextView name  = findViewById(R.id.mapNameEditor);
    name.setText(nameUrl);


    //Set the Image
    final PaintImageView imageView = findViewById(R.id.mapEditScreen);
    Picasso.get().load(imageUrl).fit().centerInside().into(imageView,new Callback.EmptyCallback() {
        //Will center image in middle of imageview with scale type matrix.
        @Override
        public void onSuccess() {
            Drawable d = imageView.getDrawable();

            RectF imageRectF = new RectF(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            RectF viewRectF = new RectF(0, 0, imageView.getWidth(), imageView.getHeight());
            matrix.setRectToRect(imageRectF, viewRectF, Matrix.ScaleToFit.CENTER);
            imageView.setImageMatrix(matrix);
        }
    });

    //Implement zoom
    final Switch zoom = findViewById(R.id.zoomSwitch);

    zoom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if(zoom.isChecked()){
                photoViewAttacher = new PhotoViewAttacher(imageView);
                photoViewAttacher.setZoomable(true);
            }else{
                Matrix theMatrix = new Matrix();
                photoViewAttacher.getSuppMatrix(theMatrix);
                photoViewAttacher.setZoomable(false);
                photoViewAttacher.setDisplayMatrix(theMatrix);
                paintImageView.reDraw();

            }
        }
    });
}

//Gets called everytime a button is pressed
@Override
public void onClick(View view) {
    //Find which button was pressed

    switch(view.getId()){
        case R.id.redButton: paintImageView.setPenColour(Color.parseColor("#F82323"));
            paintImageView.getPenColour();
            Toast.makeText(this, "RED",Toast.LENGTH_SHORT).show();
            break;
        case R.id.purpleButton: paintImageView.setPenColour(Color.parseColor("#7C4DFF"));
            paintImageView.getPenColour();
            Toast.makeText(this, "PURPLE",Toast.LENGTH_SHORT).show();
            break;
        case R.id.greenButton: paintImageView.setPenColour(Color.parseColor("#00C853"));
            paintImageView.getPenColour();
            Toast.makeText(this, "GREEN",Toast.LENGTH_SHORT).show();
            break;
        case R.id.dotPlusButton: paintImageView.changeDotSize(+DOT_SIZE_INCREMENT);
            displayDotSize.setText(paintImageView.getDotSize());
            break;
        case R.id.dotMinusButton: paintImageView.changeDotSize(-DOT_SIZE_INCREMENT);
            displayDotSize.setText(paintImageView.getDotSize());
            break;
        case R.id.resetButton: paintImageView.resetPaint();
            displayDotSize.setText(paintImageView.getDotSize());
            Toast.makeText(this, "RESET",Toast.LENGTH_SHORT).show();
            break;
        case R.id.saveEditImagebutton:
            Toast.makeText(this, "SAVED",Toast.LENGTH_SHORT).show();
            break;

    }
}

}

I am trying to figure out how to simply draw a line on an image that is being set in Picasso. I found that if I simply set the image, given a URI, with Picasso and try to draw paint to it using the following:

canvas = new Canvas(bitmap);
image.draw(canvas);
topEdge = new Paint();
topEdge.setColor(context.getResources().getColor(R.color.blue));
topEdge.setStrokeWidth(5);
canvas.drawLine(c1.getX(), c1.getY(), c2.getX(), c2.getY(), topEdge);

Then I get a crash saying that the bitmap needs to be mutable first. So I added this above that code:

Bitmap workingBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);

And then create the canvas with new Canvas(mutableBitmap) instead. This removed the crash, however nothing is being drawn. I believe this is because my Picasso is setting the image before, so now I need to reset Picasso with this new mutable bitmap. The problem is this code is in the onSuccess() callback for Picasso. What can I do to allow Paint to be drawn on an image through Picasso?

Similar Question 2 : Android Drawing Circle Chart


I need to create something like this. Circle chart with with X zones with the same size but diffrent percentage value and color inside. Any idea or advice how to write this? Thx for help!

enter image description here

I need to load App icon into image view. It is too slow to load it in list view.

I tried to use Picasso or Glide to load it.

I could not find out how to load Drawable object (NOT FROM RESOURCES) into image view using any of those libraries?

The function for getting the drawable:

public Drawable getIcon() {
    if (icon == null) {
        icon = getResolveInfo().loadIcon(ctx.getPackageManager());
    }
    return icon;
}

Similar Question 4 (3 solutions) : Picasso not loading url into ImageView

Similar Question 5 (1 solutions) : OutOfMemoryError when using ImageView in ListAdapter?

Similar Question 6 (2 solutions) : how to show image in imageView from url

Similar Question 7 (2 solutions) : How to put image in circle imageview [duplicate]

cc