Has anybody experience on drawing objects, by vertices e.g. Polygons and obtaining their surface and perimeter.
The geometry will be drawn by hand using vertices or coordinates similar to https://play.google.com/store/apps/details?id=de.hms.xconstruction and then shapes formed. I need to obtain the surface of these closed shapes.
Is there any available example on the net?
Thanks in advance.
I think the following piece of code could be a good start. It basically draws lines between all user touches:
public class TestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawingView(this));
}
class DrawingView extends SurfaceView {
private final SurfaceHolder surfaceHolder;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private List<Point> pointsList = new ArrayList<Point>();
public DrawingView(Context context) {
super(context);
surfaceHolder = getHolder();
paint.setColor(Color.WHITE);
paint.setStyle(Style.FILL);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (surfaceHolder.getSurface().isValid()) {
// Add current touch position to the list of points
pointsList.add(new Point((int)event.getX(), (int)event.getY()));
// Get canvas from surface
Canvas canvas = surfaceHolder.lockCanvas();
// Clear screen
canvas.drawColor(Color.BLACK);
// Iterate on the list
for(int i=0; i<pointsList.size(); i++) {
Point current = pointsList.get(i);
// Draw points
canvas.drawCircle(current.x, current.y, 10, paint);
// Draw line with next point (if it exists)
if(i + 1 < pointsList.size()) {
Point next = pointsList.get(i+1);
canvas.drawLine(current.x, current.y, next.x, next.y, paint);
}
}
// Release canvas
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
return false;
}
}
Related
I have a custom surfaceView which will paint the surface based on Touch event. When i draw something on this view it is working fine. But when i tried to erase the paint, nothing got erased. Please find the sample code snippet below:
public class MySurfaceView extends SurfaceView {
private static final String TAG = "FreeHandDrawing";
public static Canvas mCanvas;
SurfaceHolder holder;
private static Path path;
private Paint paint;
private ArrayList<Path> pathArrayList = new ArrayList<>();
private boolean freeHandMode;
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
freeHandMode = false;
path = new Path();
holder = getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
setDrawingCacheEnabled(true);
this.setZOrderOnTop(true);
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(0xFF22FF11);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(8);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(freeHandMode) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.d("Action", "Placed");
path.moveTo(event.getX(), event.getY());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
Log.d("Action", "Moved");
path.lineTo(event.getX(), event.getY());
pathArrayList.add(path);
}
mCanvas = holder.lockCanvas();
if (mCanvas != null) {
if (pathArrayList.size() > 0) {
mCanvas.drawPath(pathArrayList.get(pathArrayList.size() - 1), paint);
}
holder.unlockCanvasAndPost(mCanvas);
} else {
Log.d(TAG, "Canvas is NULL");
}
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "On draw called");
}
public void eraseDrawing() {
pathArrayList.clear();
invalidate();
}
public void drawEnableDisable(boolean mode) {
freeHandMode = mode;
}
}
What is the problem with the code above ?
You should keep your drawing code in the onDraw method
#Override
protected void onDraw(Canvas canvas) {
// In case you want to delete/erase when path array/list is cleared.
if (pathArrayList.size() == 0) {
canvas.drawColor(0, Mode.CLEAR);
}
// In case, you want to draw all paths also in onDraw
for(Path path : pathArrayList) {
canvas.drawPath(path, paint);
}
}
When you clear the Path Array(list) and then upon calling invalidate(), onDraw gets triggered.
Note: You're supposed to call invalidate() at the end of onTouchEvent() to tell the system to update the screen. Calling invalidate() will get the framework to eventually call onDraw().
Also you should NOT use canvas obtained by lockCanvas as it will not be hardware accelerated. Instead you should use the canvas passed as an argument to onDraw() itself.
You can choose to make the system a bit smart by not having to draw the pull list of paths every frame and instead handle erases/redraws etc by using special flags. And otherwise just render the latest path generated by the most recent call to onTouchEvent().
I would like to add effects to my pointer. That is, while I am scrolling my finger in screen, show up effect or animation around which is showed up touched area. how to add?
I've found this:
public class TestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawingView(this));
}
class DrawingView extends SurfaceView {
private final SurfaceHolder surfaceHolder;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public DrawingView(Context context) {
super(context);
surfaceHolder = getHolder();
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
if (surfaceHolder.getSurface().isValid()) {
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.BLACK);
canvas.drawCircle(event.getX(), event.getY(), 50, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
return false;
}
}
}
I'm working on a paint-like app, but I've run it to some troubles.
I know I have re-draw all the objects each frame, but this in turn makes performance slow further on when many objects are visible.
I've also noticed that clearing the background only once, then adding objects to be drawn to the surface when painting is active makes the screen flash almost to the point of inducing epilepsy.
So what is the best way to make a paint app 100% smooth no matter how many objects that are drawn?
Epilepsy-inducing code:
public void run()
{
while (running)
{
if (!holder.getSurface().isValid())
continue;
if (drawObjectsToAdd.size() == 0)
continue;
drawing = true;
Canvas c = holder.lockCanvas();
if (redrawBackground)
{
c.drawARGB(255, 255, 255, 255);
redrawBackground = false;
}
drawObjects.addAll(drawObjectsToAdd);
drawObjectsToAdd.clear();
for (DrawObject draw : drawObjects)
{
draw.Draw(c, paint);
}
holder.unlockCanvasAndPost(c);
drawing = false;
}
}
this adds a new object once it's been added outside of the thread, but it makes the screen flash so much it gives me headaches - and I only redraw the background once.
Smooth at first but becomes laggy after a while, probably due to having to add hundreds and hundreds of objects in the end:
public void run()
{
while (running)
{
if (!holder.getSurface().isValid())
continue;
if (drawObjectsToAdd.size() > 0)
{
drawObjects.addAll(drawObjectsToAdd);
drawObjectsToAdd.clear();
redraw = true;
}
if (clear)
{
drawObjectsToAdd.clear();
drawObjects.clear();
redraw = true;
clear = false;
}
if (!redraw)
continue;
drawing = true;
Canvas c = holder.lockCanvas();
c.drawARGB(255, 255, 255, 255);
for (DrawObject draw : drawObjects)
{
try
{
draw.Draw(c, paint);
}
catch (Exception ex) { }
}
holder.unlockCanvasAndPost(c);
drawing = false;
redraw = false;
}
}
I, at least for this app wanna store all the objects that are added so it doesn't matter how it's painted as long as it's smooth all the way. Preferably, add a circle - it will render a new Bitmap on to the surface, instead of having to redraw lots of objects each frame - instead store them but do not add objects already drawn.
UPDATE
Pseudo-Code of how I want it to be:
If no background is drawn
Draw background color
If new items have been added
Draw only new items to the background
Store new items in objects list
This way, we'll only draw the background once. When a new item is added, only draw that item to the existing surface. When the objects increases, looping through every item will reduce performance greatly and it will not be pleasant to work with.
UPDATE 2:
private void Draw()
{
while (running)
{
if (!holder.getSurface().isValid())
continue;
if (picture == null)
{
picture = new Picture();
Canvas c = picture.beginRecording(getWidth(), getHeight());
c.drawARGB(255, 255, 255, 255);
picture.endRecording();
}
if (drawObjectsToAdd.size() > 0)
{
drawObjects.addAll(drawObjectsToAdd);
drawObjectsToAdd.clear();
Canvas c = picture.beginRecording(getWidth(), getHeight());
for (DrawObject draw : drawObjects)
{
draw.Draw(c, paint);
}
picture.endRecording();
drawObjects.clear();
}
Canvas c2 = holder.lockCanvas();
c2.drawPicture(picture);
holder.unlockCanvasAndPost(c2);
}
}
This last method from Update 2 makes it render all the lines like the "Snake game" when adding circles. Looks like a moving snake on a background, where some of it's circles disappear one frame and others don't the next. If I skip to redraw each frame, it will instead vary which of these circles that are visible.
what about that Picture implementation? increase MAX_DRAWERS to some reasonable value and see how it works
class SV extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private static final int MAX_DRAWERS = 8;
private boolean mRunning = true;
private List<Picture> mPictures = new LinkedList<Picture>();
private List<Drawer> mDrawers = new LinkedList<Drawer>();
private Paint mPaint;
public SV(Context context) {
super(context);
mPaint = new Paint();
mPaint.setColor(0xffffff00);
getHolder().addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(this).start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public synchronized void surfaceDestroyed(SurfaceHolder holder) {
mRunning = false;
notify();
}
#Override
public synchronized boolean onTouchEvent(MotionEvent event) {
Drawer drawer = new Drawer(event.getX(), event.getY());
mDrawers.add(drawer);
if (mDrawers.size() > MAX_DRAWERS) {
Picture picture = new Picture();
Canvas canvas = picture.beginRecording(getWidth(), getHeight());
mPaint.setAlpha(0xbb);
for (Drawer dr : mDrawers) {
dr.draw(canvas, mPaint);
}
picture.endRecording();
mPaint.setAlpha(0xff);
mPictures.add(picture);
mDrawers.clear();
Log.d(TAG, "onTouchEvent new Picture");
}
notify();
return false;
}
#Override
public synchronized void run() {
SurfaceHolder holder = getHolder();
while (mRunning) {
// Log.d(TAG, "run wait...");
try {
wait();
} catch (InterruptedException e) {
}
if (mRunning) {
// Log.d(TAG, "run woke up");
Canvas canvas = holder.lockCanvas();
canvas.drawColor(0xff0000ff);
for (Picture picture : mPictures) {
picture.draw(canvas);
}
for (Drawer drawer : mDrawers) {
drawer.draw(canvas, mPaint);
}
holder.unlockCanvasAndPost(canvas);
}
}
Log.d(TAG, "run bye bye");
}
class Drawer {
private float x;
private float y;
public Drawer(float x, float y) {
this.x = x;
this.y = y;
}
public void draw(Canvas canvas, Paint paint) {
canvas.drawCircle(x, y, 8, paint);
}
}
}
You generate your list of objects each and every frame, just to discard it after drawing. Why? Generate your list of draw objects once, then draw them.
That's all you need for your drawing thread...
public void run() {
while (running)
{
Canvas c = holder.lockCanvas();
c.drawARGB(255, 255, 255, 255);
for (DrawObject draw : drawObjects)
{
try
{
draw.Draw(c, paint);
}
catch (Exception ex) { }
}
holder.unlockCanvasAndPost(c);
}
}
I am creating an app to draw free shapes on the surface screen but i could only draw separated points my problem is . i want the points to be connected to each other when i draw them not lifting my finger from the screen . i mean as long as i am touching the screen draw.here's my code so far.
public class SurfaceViewActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawingView(this));
}
class DrawingView extends SurfaceView {
private final SurfaceHolder surfaceHolder;
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private List<Point> pointsList = new ArrayList<Point>();
public DrawingView(Context context) {
super(context);
surfaceHolder = getHolder();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (surfaceHolder.getSurface().isValid()) {
// Add current touch position to the list of points
pointsList.add(new Point((int)event.getX(), (int)event.getY()));
// Get canvas from surface
Canvas canvas = surfaceHolder.lockCanvas();
// Clear screen
canvas.drawColor(Color.BLACK);
// Iterate on the list
for(int i=0; i<pointsList.size(); i++) {
Point current = pointsList.get(i);
// Draw points
canvas.drawPoint(current.x, current.y, paint);
}
// Release canvas
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
return false;
}
}
}
you can use this function to draw smooth lines
public void drawBrzierLine(Canvas mCanvas, float xi, float yi, float xd, float yd) {
Point start = new Point((int) xi, (int) yi);
Point end = new Point((int) xd, (int) yd);
Path mPath = new Path();
mPath.reset();
mPath.moveTo(start.x, start.y);
mPath.quadTo(start.x, start.y, end.x, end.y);
mCanvas.drawPath(mPath, mPaint);
}
In onTouchEvent(MotionEvent event) you only handle ACTION_DOWN. So this code will only run when you press down on the screen. Use ACTION_MOVE instead.
http://developer.android.com/reference/android/view/MotionEvent.html
I want to place red circles where I touch an image and have a class that listens to the touch and sends the coordinates to an other class:
public class Report extends Fragment {
private Context activity;
private Point point = new Point();
private DrawingCrl imgCircle = new DrawingCrl();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.report, container,
false);
activity = this.getActivity();
//Where I'm doing the touching and respond to that:
final View touchView = rootView.findViewById(R.id.ImageC);
touchView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
point.x = Float.valueOf(event.getX());
point.y = Float.valueOf(event.getY());
touchView = imgCircle.DrawCircle(Point point);
return true;
}
});
return rootView;
public class Point {
float x;
float y;
}
}
When I call the DrawCircle I want the class DrawingCrl to do just that, draw a circle for me on the point I send to the method:
public class DrawingCrl {
private Bitmap mBitmap;
private Paint paint;
private Canvas canvas = new Canvas();
public void DrawCircle(Point point) {
mBitmap = Bitmap.createBitmap(400, 800, Bitmap.Config.ARGB_8888);
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
canvas.drawCircle(point.x, point.y, 50, paint);
}
}
I have read both getting X and Y coordinates and drawing circle and Draw Circle on touch but don't get how to do the drawing of the circle.
I'm pretty new to android so sorry my many noob failures. I hope you can help me get this working! Thanks! :)
Okay, here's how I would do it but I'm going to change your code a bit.
First I would change your DrawingCrl class to just store location (and possibly Paint object but this would probably fit better as a static shared by all DrawingCrls) like this:
public class DrawingCrl {
public Point myLoc;
public Paint paint;
public void DrawCircle(Point point) {
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
myLoc = point;
}
}
Save a Vector of DrawingCrl objects created in your Activity and create new ones in the onTouch event handler.
This goes in the Report class:
java.util.Vector<DrawingCrl> myCircles = new java.util.Vector<DrawingCrl>();
Then in 'onTouch' create a new circle object and save it:
#Override
public boolean onTouch(View v, MotionEvent event) {
point.x = Float.valueOf(event.getX());
point.y = Float.valueOf(event.getY());
// create a new Circle here and save it
DrawingCrl c = new DrawingCrl(point);
myCircles.add(c);
return true;
}
Then I would override the 'onDraw' method of your View 'touchView'.
The 'onDraw' will give you the Canvas to draw to, so don't create your own like you are doing in your DrawingDMG class right now.
#Override
protected void onDraw(Canvas c)
{
for (DrawingCrl c : myCircles)
{
c.drawCircle(c.myLoc.x, c.myLoc.y, 50, c.paint);
}
}