I have a bitmap drawn to a canvas that I want to be able to paint transparent onto. When I move my finger to do the drawing, the paint does not paint to where my finger is. Instead it paints below and to the right of where I am touching.
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
bitmapCanvas.drawColor(Color.TRANSPARENT);
bitmapCanvas.drawCircle(x, y, 40, eraserPaint);
Matrix matrix = new Matrix();
matrix.postTranslate(tattoo.getX(), tattoo.getY());
canvas.drawBitmap(bitmap, matrix, paint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
invalidate();
return true;
}
Related
Background:
To give a bit of background ⇒ the app should simply show the user an area (SignatureActivity / SignatureCanvasView) to put a signature. I found a snippet, which works pretty well to draw in.
Issue: Trying to retrieve the bitmap in the MainActivity to show it in an ImageView shows an empty Image. Also writing the retrieved Bitmap creates an png file, which is pretty much empty, since it has even not a background color.
That's how it's done in MainActivity:
private void insertSignature(){
ivSign.setImageBitmap(signature);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == SIGNATURE_REQUEST){
if(resultCode == RESULT_OK){
signature = BitmapFactory.decodeByteArray(data.getByteArrayExtra("SignatureBitmap"), 0, data.getByteArrayExtra("SignatureBitmap").length);
//signature = (Bitmap) getIntent().getParcelableExtra("SignatureBitmap");
if(signature != null){
insertSignature();
Log.e("ActivityResult: ", "SignatureActivity finished with RESULT_OK");
}
}else if(resultCode == RESULT_CANCELED){
Log.e("ActivityResult: ", "SignatureActivity was cancelled");
}else{
Log.e("ActivityResult", "Unknown activity result!");
}
}
}
That is the code for the view (SignatureCanvasView), which maintains the signature functionality:
public class SignatureCanvasView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
Context context;
private Paint mPaint;
private float mX, mY;
private static final float TOLERANCE = 5;
public SignatureCanvasView(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;
// we set a new Path
mPath = new Path();
// and we set a new Paint with the desired attributes
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(2f);
}
public byte[] getBitmap() {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
return byteArray;
}
// override onSizeChanged
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// your Canvas will draw onto the defined Bitmap
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw the mPath with the mPaint on the canvas when onDraw
canvas.drawPath(mPath, mPaint);
}
// when ACTION_DOWN start touch according to the x,y values
private void startTouch(float x, float y) {
mPath.moveTo(x, y);
mX = x;
mY = y;
}
// when ACTION_MOVE move touch according to the x,y values
private void moveTouch(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOLERANCE || dy >= TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
public void clearCanvas() {
mPath.reset();
invalidate();
}
// when ACTION_UP stop touch
private void upTouch() {
mPath.lineTo(mX, mY);
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
moveTouch(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
upTouch();
invalidate();
break;
}
return true;
}
}
The getBitmap()-method of the SignatureCanvasView is called in SignatureActivity to put it as extra, before finishing:
public class SignatureActivity extends AppCompatActivity {
//...
public void Save(){
this.getIntent().putExtra("SignatureBitmap",scw.getBitmap());
setResult(RESULT_OK,this.getIntent());
finish();
}
//...
}
I appreaciate any hints and suggestions to solve the issue.
You are drawing Path onto canvas attached with SignatureCanvasView, not on the canvas attached with Bitmap mBitmap.
// override onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw the mPath with the mPaint on the canvas when onDraw
// canvas.drawPath(mPath, mPaint);
boolean drawPathTwice = true;
if (drawPathTwice) {
canvas.drawPath(mPath, mPaint); // this will be visible to user
mCanvas.drawPath(mPath, mPaint);// draw path onto canvas attached with mBitmap
// drawing path 2 times(wasting resources).
} else {
mCanvas.drawPath(mPath, mPaint); // draw path onto canvas attached with mBitmap
canvas.drawBitmap(mBitmap, 0, 0, null);
// we have drawn path onto bitmap canvas, so view's canvas will be
// empty, to give touch feedback we can draw our bitmap containing
// path onto view's canvas.
}
}
Better option would be to have a class scope boolean which will control
drawing on bitmap (you can set it to true in upTouch() method).
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
if (drawOnBitmap) { // this code will be executed only on ACTION_UP
// event
mCanvas.drawPath(mPath, mPaint); // draw path onto canvas attached with mBitmap
drawOnBitmap = false;
}
}
Im creating a drawing app, and i have a view that uses a canvas to draw onto. I'm scaling the image using a matrix, and when the image is zoomed in and there is a touch event i try getting the inverse of the matrix and then map the points using the inverse matrix to get the points that are on the canvas from the motionevent points but its not matching up how it should and wondering what I'm doing wrong to get the canvas location from the screen touch location.
public class CustomDrawableView extends View {
private Paint mBitmapPaint;
private ShapeDrawable mDrawable;
private ScaleGestureDetector detector;
Matrix drawMatrix;
Bitmap bm;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.drawBitmap(bm, drawMatrix, mBitmapPaint);
mDrawable.draw(canvas);
canvas.restore();
}
public Pair<Float,Float> GetRealValues(float eventX, float eventY){
Matrix inverse = new Matrix();
drawMatrix.invert(inverse);
float[] point = {eventX,eventY};
inverse.mapPoints(point);
return new Pair<>(point[0],point[1]);
}
#Override
//reads motions and calls methods to set starting and ending points and to
//draw canvas depending on the motion
public boolean onTouchEvent(MotionEvent event) {
//get the canvas location from the MotionEvent location
Pair<Float,Float> realvals = GetRealValues(event.getX(),event.getY());
//drawing mode where you can draw or zoom in on canvas
if(drawing) {
//not zooming mode where you draw on canvas
if (!zoom) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//do stuff
break;
case MotionEvent.ACTION_MOVE:
//do stuff
break;
case MotionEvent.ACTION_UP:
//do stuff
break;
case MotionEvent.ACTION_POINTER_UP:
break;
}
}
//zoom mode where you can scale the canvas
if (zoom) {
detector.onTouchEvent(event);
invalidate();
}
}
return true;
}
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private float MIN_ZOOM = 1f;
private float MAX_ZOOM = 10f;
#Override
public boolean onScale(ScaleGestureDetector detector) {
Matrix transformationMatrix = new Matrix();
scaleFactor *= detector.getScaleFactor();
float tscale = detector.getScaleFactor();
if(scaleFactor < MIN_ZOOM || scaleFactor > MAX_ZOOM){
float prescaleFactor = scaleFactor/tscale;
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
tscale = scaleFactor/prescaleFactor;
}
transformationMatrix.postScale(tscale, tscale);
drawMatrix.postConcat(transformationMatrix);
return true;
}
}
Found an answer. My ondraw method needs to be like this
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
float[] mvals = new float[9];
drawMatrix.getValues(mvals);
canvas.translate(mvals[Matrix.MTRANS_X], mvals[Matrix.MTRANS_Y]);
canvas.scale(mvals[Matrix.MSCALE_X], mvals[Matrix.MSCALE_Y]);
canvas.drawBitmap(bm, 0, 0, mBitmapPaint);
mDrawable.draw(canvas);
canvas.restore();
}
I'm not sure why i need to translate and scale the canvas individually in order for the inverse matrix to give me the correct canvas points from the touch points, I'm guessing that
canvas.drawBitmap(bm, drawMatrix, mBitmapPaint);
rounds the matrix values to ints because the canvas points i got from mapping the points on the inverse matrix were just slightly off when drawing the canvas that way.
I have extended View to use canvas. I have draw basic drawings in onDraw() method, when user touches in the canvas I have to draw an image there, for that I have used canvas inside onTouchEvent() method,it is not drawing anything there, the code is given below, what is the problem and how can i resolve this
public class ScreenView extends View(){
static Canvas canvas;
Bitmap bm;
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
bm = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
canvas.draw.......
......
...........
}
public boolean onTouchEvent(final MotionEvent event) {
handleTouches(event.getX(), event.getY());
return false;
}
public void handleTouches(float x, float y) {
xLocTouch = (int) x;
yLocTouched = (int) y;
Paint paint = new Paint();
paint.setColor(Color.BLACK);
canvas.drawBitmap(bm, xLocTouch ,yLocTouched , paint);
}
}
You should call invalidate() method inside onTouchEvent, then your onDraw() method will be called, and you just should store your x and y coordinates, and then draw bitmap to this coordinates, like this:
public class ScreenView extends View {
int xLocTouched;
int yLocTouched;
Bitmap bm;
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
bm = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
//your basic drawings also should depends on xLocTouched and yLocTouched.
Paint paint = new Paint();
paint.setColor(Color.BLACK);
canvas.drawBitmap(bm, xLocTouched ,yLocTouched , paint);
}
public boolean onTouchEvent(final MotionEvent event) {
xLocTouched = (int) event.getX();
yLocTouched = (int) event.getY();
invalidate();
return false;
}
}
I'm making an application which can crop the image of the person. The layout of my application will give a better description
Here the rectangle will be used to capture the image defined by its length and width. The rectangle is movable. How would I go about re-sizing the rectangle. For eg. in WhatsApp when you touch the region inside the rectangle, the rectangle moves. If you touch the edges of rectangle, it provides the ability to re-size image suitable for cropping. So, I have 2 questions. 1) How to receive Touch events on edges of rectangle and 2) How would I re-size my rectangle. I'm using canvas to draw my rectangle. The code for this is as follows:
public class CustomView extends Views
{
public CustomView(Context context)
{
super(context);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);;
paint.setColor(Color.BLUE);
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(0, 0, 300, 300, paint);
}
}
You should implement the OnTouchListener and check in MotionEvent.ACTION_DOWN if you touch the rectangle and modify right, left, bottom and top of rectangle in MotionEvent.ACTION_MOVE
I provided a not tested example:
public class CustomView extends View {
private float left=0,right = 300, top = 0, bottom = 300;
public CustomView(Context context)
{
super(context);
setOnTouchListener( new OnTouchListener() {
float oldX, oldY;
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean touch = false;
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
touch = isTouchBorderRect();
break;
case MotionEvent.ACTION_UP:
touch = false;
case MotionEvent.ACTION_MOVE:
if (touch){
float newX = event.getRawX();
float newY = event.getRawY();
float deltaX = newX-oldX;
float deltaY = newY-oldY;
left-=deltaX;
right +=deltaX;
bottom += deltaY;
top -= deltaY;
}
break;
}
return false;
}
});
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);;
paint.setColor(Color.BLUE);
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(left, top, right, bottom, paint);
}
}
In isTouchBorderRect() method you should check if you have touched the rectangle.
This code is not tested but show the idea that you want to develop.
I have the following app which allows a circle to be drawn on touch. When the screen is touched a second time the first touch circle is removed and a new circle is created where the second touch occurs. How can allow multiple circles to appear for as many times the screen is touched?(i.e. 5 touch events = 5 circles appear on the same canvas in their touch locations).
public class Lab12Activity extends Activity {
Point pt = new Point();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawView(this));
}
class DrawView extends View implements View.OnTouchListener {
public DrawView(Context context) {
super(context);
this.setOnTouchListener(this);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.GRAY);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawCircle(pt.x, pt.y, 15, paint);
paint.setColor(Color.BLUE);
canvas.drawRect(0, 0, 225, 150, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setColor(Color.WHITE);
paint.setTextSize(30);
canvas.drawText("Clear", 75, 75, paint);
canvas .drawText("Tap to add Circles", 300, 75, paint);
}
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
pt.x = (int) event.getX();
pt.y = (int) event.getY();
invalidate();
}
return true;
}
}
}
use event.getActionIndex (); it returns the pointer index of the touch event.