class Draw extends View {
private Paint mPaint;
public Button mCircles;
DrawFragment mDrawFragment;
private Circle mCurrentCircle;
private List<Circle> mCircle= new ArrayList<>();
public Draw(Context context){
this(context,null);
}
public Draw(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint=new Paint();
mPaint.setColor(Color.RED);
mPaint.setAlpha(80);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Circle circle : mCircle) {
float x1 = circle.getCenter().x;
float x2 = circle.getFinal().x;
float y1 = circle.getCenter().y;
float y2 = circle.getFinal().y;
double radius = Math.sqrt(Math.pow(dpx1 - dpx2, 2) + Math.pow(dpy1 - dpy2, 2));
float rad = (float) radius;
canvas.drawCircle(dpx1, dpy1, rad, mPaint);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mCurrentCircle = new Circle(current);
mCircle.add(mCurrentCircle);
break;
case MotionEvent.ACTION_MOVE:
if (mCurrentCircle != null) {
mCurrentCircle.setFinal(current);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
mCurrentCircle = null;
invalidate(); break;
}
}
Log.i(TAG, "onTouchEvent: at x="+current.x + ", y =" + current.y);
return true;
}
1.I'm saving these coordinates of circles drawn on canvas and this is to mark damages on a car.
but when i use these values to display on a bigger device , Circle positions are not intact .. what can be done?
All the coordinates are referenced to a device with a screen determined by pixels and pixels per inches.
If you wish to use the same information on a different device, you must set a sort of anchor point which works for you as a reference.
For example, if those circles will be drawn over a background image of a car, you should be able to reference theme to the position of that picture.
With that information, on any other device, you should be able to redraw those circles on any device based on the fact the coordinates are not referenced to the device but are reference to the car image.
I hope this help you to re think the problem.
Related
So I am using onDraw in a custom View class to draw shapes on a RelativeLayout + TableLayout all that works fine, and I have another class that I use to draw lines from Point A to Point B etc. example below:
My Goal:
If I drag my finger from Point A (objectA) to Point B(objectB), how can I delete those 2 View Objects from the canvas? I added a method:
objectA.delete();
objectB.delete();
that should delete both A and B when I drag my finger through MotionEvent, but it only deletes one and not the other, so i am thinking it's not retroactive?
Codes below:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
/// get the child that corresponds to the first touch
DotView objectA = getChildForTouch((TableLayout) v, x, y);
return true;
case MotionEvent.ACTION_MOVE:
///used the x - y to get the object for every other shape the user`S finger passes over.
DotView objectB = getChildForTouch((TableLayout) v, x, y);
/// just update positions
line.setCoords(mStartX, mStartY, (int) x, (int) y);
objectA.delete(); ///Delete first shape
objectB.delete(); ///Delete second shape
break;
case MotionEvent.ACTION_UP:
///Gets the last shape where the user released their fingers
endView = getChildForTouch((TableLayout) v, x, y);
break;
Delete method inside the: DotView extends View class:
private static class DotView extends View {
private static final int DEFAULT_SIZE = 100;
private Paint mPaint = new Paint();
private Rect mBorderRect = new Rect();
private Paint mCirclePaint = new Paint();
private int mRadius = DEFAULT_SIZE / 4;
public DotView(Context context) {
super(context);
mPaint.setStrokeWidth(2.0f);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
mCirclePaint.setColor(Color.CYAN);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.parseColor("#0099cc"));
mBorderRect.left = 0;
mBorderRect.top = 0;
mBorderRect.right = getMeasuredWidth();
mBorderRect.bottom = getMeasuredHeight();
canvas.drawRect(mBorderRect, mPaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,
mRadius, mCirclePaint);
}
public void delete(){
mPaint.setColor(Color.TRANSPARENT);
}
}
Just something simple to fake delete the circles
Thanks in advance guys.. more codes can be provided if need be.
Edit: If I can accomplish this in any other way please feel free to share. (Draw circles on a Grid and delete the ones I used my finger to draw my line over)
First of all try changing your delete() method to this:
public void delete(){
mPaint.setColor(Color.TRANSPARENT);
invalidate();
}
You need to let your View know that you want it to redraw itself (that's why the invalidate() call).
I want to create a heart shape drown on screen when user touches the smartphone screen(working on a drawing android app). So i setup the BitmapShader as below code
//Initialize the bitmap object by loading an image from the resources folder
Bitmap fillBMP = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart);
fillBMP = Bitmap.createScaledBitmap(fillBMP, 20, 20, false);
//Initialize the BitmapShader with the Bitmap object and set the texture tile mode
shader= new BitmapShader(fillBMP, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
Then i assign the shader paint object.
paint.setShader(preset.shader);
I have also setup the touch listener to track the user finger. On user touch i draw this on canvas object as.
Path path = new Path();
path.moveTo(mid1.x, mid1.y);
path.quadTo(midmid.x, midmid.y, mid2.x, mid2.y);
canvas.drawPath(path, paint);
This give me this
Where some heart shape are croped and also these are repeated. What i want not these are repeated and never croped like this.
Thanks in advance.
I had a bit of trouble uploading to Git, so for now, I'll post the solution here.
Here's a helper class I wrote to create the effect you want. I added comments in-line to explain what I'm doing.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
// Use this class with a Canvas to create the effect you want.
public class CurvedBitmapDrawer
{
private Context mContext;
private Paint mPaint;
private int mResourceId;
private Bitmap mBitmap;
private Path mPath;
private int mBitmapMargin;
// Create with a context so that this class can use resource ids.
public CurvedBitmapDrawer(Context context) {
mContext = context;
mPath = new Path();
}
// getters setters for paint.
// this paint will be used to draw the bitmaps, and the strokeWidth value in the paint
// will be used to set the thickness of the curve / line that is drawn.
public Paint getPaint() {
return mPaint;
}
public void setPaint(Paint paint) {
mPaint = paint;
}
// getters setters for the space between the bitmaps.
public int getBitmapMargin()
{
return mBitmapMargin;
}
public void setBitmapMargin(int bitmapMargin)
{
mBitmapMargin = bitmapMargin;
}
// getters setters for res id
public int getResourceId() {
return mResourceId;
}
public void setResourceId(int resourceId)
{
mResourceId = resourceId;
mBitmap = null;
}
// alternative optional getters setters for bitmap.
public Bitmap getBitmap()
{
return mBitmap;
}
public void setBitmap(Bitmap bitmap)
{
mBitmap = bitmap;
mResourceId = 0;
}
// I decided to only use a local path here, but feel free to change it.
// call getPath to perform actions on the path that is drawn by this class.
public Path getPath()
{
return mPath;
}
// draw method. comments inline.
public void draw(Canvas canvas)
{
// grab a bitmap in the desired size.
final Bitmap scaledBitmap = getScaledBitmap();
// find the center of the bitmap.
final float centerX = scaledBitmap.getWidth() / 2;
final float centerY = scaledBitmap.getHeight() / 2;
// wrap the path with a measurement tool for paths - PathMeasure
final PathMeasure pathMeasure = new PathMeasure(mPath, false);
// initialize the distance to the center of the bitmap.
float distance = scaledBitmap.getWidth() / 2;
// initialize position and slope buffers.
float[] position = new float[2];
float[] slope = new float[2];
float slopeDegree;
// draw so long as the distance traveled on the path isn't longer than
// the total distance of the path.
while (distance < pathMeasure.getLength())
{
// grab the position & slope (tangent) on a particular distance along the path.
pathMeasure.getPosTan(distance, position, slope);
// convert the vector to a degree.
slopeDegree = (float)((Math.atan2(slope[1], slope[0]) * 180f) / Math.PI);
// preserve the current state of the canvas
canvas.save();
// translate the canvas to the position on the path.
canvas.translate(position[0] - centerX, position[1] - centerY);
// rotate the canvas around the center of the bitmap the amount of degrees needed.
canvas.rotate(slopeDegree, centerX, centerY);
// draw the bitmap
canvas.drawBitmap(scaledBitmap, 0, 0, mPaint);
// revert the bitmap to the previous state
canvas.restore();
// increase the distance by the bitmap's width + the desired margin.
distance += scaledBitmap.getWidth() + mBitmapMargin;
}
}
// returns a scaled bitmap from the asset specified.
private Bitmap getScaledBitmap()
{
// no bitmap or resId, return null (no special handing of this! add if you like).
if (mBitmap == null && mResourceId == 0)
return null;
// if no bitmap is specified, create one from the resource id.
// Optimization: be sure to clear the bitmap once done.
if (mBitmap == null)
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), mResourceId);
// width / height of the bitmap[
float width = mBitmap.getWidth();
float height = mBitmap.getHeight();
// ratio of the bitmap
float ratio = width / height;
// set the height of the bitmap to the width of the path (from the paint object).
float scaledHeight = mPaint.getStrokeWidth();
// to maintain aspect ratio of the bitmap, use the height * ratio for the width.
float scaledWidth = scaledHeight * ratio;
// return the generated bitmap, scaled to the correct size.
return Bitmap.createScaledBitmap(mBitmap, (int)scaledWidth, (int)scaledHeight, true);
}
}
Here's a usage example:
ImageView image = (ImageView)findViewById(R.id.img);
CurvedBitmapDrawer drawer = new CurvedBitmapDrawer(this);
Paint paint = new Paint();
paint.setStrokeWidth(50);
drawer.setPaint(paint);
drawer.setResourceId(R.drawable.heart_icon);
drawer.setBitmapMargin(10);
Path path = drawer.getPath();
path.moveTo(80, 90);
path.cubicTo(160, 470, 750, 290, 440, 880);
Bitmap finalBitmap = Bitmap.createBitmap(800, 1000, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(finalBitmap);
drawer.draw(canvas);
image.setImageBitmap(finalBitmap);
I've tested it here and it seems to work well, minus some edge cases perhaps. Here's what it looks like with the given usage example:
Remember to set the stroke width on the paint object you supply, or else nothing will be drawn (and probably will cause an exception with the current code).
Hope this helps you.
I want to let the user to draw lines on the image and user can erase tline they draw. So far I can draw lines on the custom ImageView created as below. However, I tried a lot of method and all of them draw black line when I selected erase mode. I want the part after earse still is the Image I set and implement the erase just like drawing lines. How can I do this?
public class PaintView : ImageView
{
private Paint _Paint = new Paint();
private Paint ErasePaint= new Paint();
public Bitmap _Bmp;
private Canvas _Canvas = null;
private PointF _StartPt = new PointF();
private PointF _EndPt = new PointF();
public int status=1; //1 draw, 0 erase
public PaintView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public void Initialize()
{
_Paint = new Paint ();
_Paint.Color = new Color (255, 255, 255);
_Paint.StrokeWidth = 5;
_Paint.StrokeCap = Paint.Cap.Round;
int alpha = 3;
_Bmp = Bitmap.CreateScaledBitmap(_Bmp,_Bmp.Width*alpha, _Bmp.Height*alpha, false);
_Canvas = new Canvas(_Bmp);
this.SetImageBitmap(_Bmp);
}
override public bool OnTouchEvent(MotionEvent e)
{
if (status == 1) {
switch (e.Action)
{
case MotionEventActions.Down:
_StartPt.Set(e.GetX() - 1, e.GetY() - 1);// for just a tapping
DrawLine(e);
break;
case MotionEventActions.Move:
DrawLine(e);
break;
case MotionEventActions.Up:
DrawLine(e);
break;
}
}
if (status == 0) {
switch (e.Action)
{
case MotionEventActions.Down:
_StartPt.Set(e.GetX() - 1, e.GetY() - 1);// for just a tapping
eraseCanvas(e);
break;
case MotionEventActions.Move:
eraseCanvas(e);
break;
case MotionEventActions.Up:
eraseCanvas(e);
break;
}
}
return true;
}
private void DrawLine(MotionEvent e)
{
_EndPt.Set(e.GetX(), e.GetY());
_Canvas.DrawLine(_StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y, _Paint);
_StartPt.Set(_EndPt);
Invalidate();
}
private void eraseCanvas(MotionEvent e)
{
_EndPt.Set(e.GetX(), e.GetY());
ErasePaint.Color = Color.Transparent;
ErasePaint.StrokeWidth = 5;
ErasePaint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.Clear));
_Canvas.DrawLine(_StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y, ErasePaint);
_StartPt.Set(_EndPt);
Invalidate();
}
}
Well then, the way I do it in my app is to set the bitmap I draw on as a background to the canvas View on initialization - this should make the line go transparent after the drawing operation is done. I am not really familiar with Xamarin but in Android Java I just go something like:
BitmapDrawable bd = new BitmapDrawable(bmp);
canvas.setBitmapDrawable(bd); //or this.setBitmapDrawable(bd);
after instantiating the Canvas. It's because the xfer mode clear scrapes off everything showing View beneath the Canvas, so the line stays black. I believe you should adapt it to your code fairly easy.
If it works as I described (the line is black during drawing, and then poof - goes transparent), then to make it transparent right away, the way to go for me was to turn off hardware acceleration when drawing with Mode.Clear, again, Android Java:
boolean bool = "rubber".equals(tool);
if (bool) {
this.setLayerType(LAYER_TYPE_SOFTWARE, canvasPaint); //or canvas.setLayer...
}
//drawing stuff
if (bool) {
this.setLayerType(LAYER_TYPE_HARDWARE, canvasPaint);
}
Good luck, let me know how did it go or if you need some clarification or something. :)
Below is my code where I am drawing circle on the Google Map in Android. It is working fine for me.
Problem Statement:-
Currently the circle that is getting drawn on the google map is very dark. I need to make that circle little bit light with the same color that I am having currently. Is that possible to make that circle little bit light by tweaking few parameters in the paint or color? It is more darker in the center part, don't know why. I just need to make it light in all the four circles that I have currently.
class MapOverlay extends Overlay {
private GeoPoint pointToDraw;
int[] imageNames=new int[6];
private Point mScreenPoints;
private Bitmap mBitmap;
private Paint mCirclePaint;
public MapOverlay(GPSLocationListener gpsLocationListener, int currentUser) {
imageNames[0]=currentUser;
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(0x30000000);
mCirclePaint.setStyle(Style.FILL_AND_STROKE);
mBitmap = BitmapFactory.decodeResource(getResources(),imageNames[0]);
mScreenPoints = new Point();
}
public void setPointToDraw(GeoPoint point) {
pointToDraw = point;
}
public GeoPoint getPointToDraw() {
return pointToDraw;
}
#Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
super.draw(canvas, mapView, shadow);
if (pointToDraw == null) {
return true;
}
mScreenPoints = mapView.getProjection().toPixels(pointToDraw, mScreenPoints);
int totalCircle=5;
int radius=40;
int centerimagesize=35;
for (int i = 1; i <= totalCircle; i ++) {
canvas.drawCircle(mScreenPoints.x,mScreenPoints.y, i*radius, mCirclePaint);
}
canvas.drawBitmap(mBitmap, (mScreenPoints.x-(centerimagesize/2)),(mScreenPoints.y-(centerimagesize/2)), null);
super.draw(canvas,mapView,shadow);
return true;
}
}
Snapshot of my Circle currently-
Any suggestion will be of great help.
Problem
The reason why it gets darker in the center, is because you are drawing four circles, one over the other. When you draw the second circle, the part that is commoun to both, is painted twice, so it becomes darker. When you draw the tird circle, it becomes even darker ...
Solution
If you want all circles with same color, you should use Style.STROKE for all the circles, except the larger one, which shoud use Style.FILL_AND_STROKE. With that you only draw the lines for the small circles and fill all area when drawing the big one.
Code
mCirclePaint.setStyle(Style.STROKE);
for (int i = 1; i <= totalCircle-1; i ++) {
canvas.drawCircle(mScreenPoints.x,mScreenPoints.y, i*radius, mCirclePaint);
}
mCirclePaint.setStyle(Style.FILL_AND_STROKE);
canvas.drawCircle(mScreenPoints.x,mScreenPoints.y, totalCircle*radius, mCirclePaint);
Regards
I draw a circle where the user touches a image view. It draws the circle fine, but it is not where the user touches. It is always off the left by a lot. Here is the code:
getting the touch:
image.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View arg0, MotionEvent arg1) {
System.out.println("Touch recieved at "+arg1.getX() + " " + arg1.getY());
touchX = (arg1.getX());
touchY = (arg1.getY());
System.out.println("Touch recieved at "+touchX + " " + touchY);
image.setImageBitmap(createImage());
return true;
}
});
and drawing on the image:
public Bitmap createImage(){
Bitmap image = bmp.copy(Bitmap.Config.RGB_565, true);
Canvas canvas = new Canvas(image);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.GREEN);
canvas.drawCircle(touchX, touchY, radius, paint);
System.out.println("Drew a circle at "+touchX+" " + touchY+" with a radius of "+radius+".");
return image;
}
Any ideas?
Try to draw at
touchX=touchX- image.getWidth() / 2;
touchY=touchY- image.getHeight() / 2;
this will draw the center of the image the place you touch
You have to add touch listener to the layout not to the image. Add setOnTouchListener to the layout. No need of touch listener to the image.