I just got started with Android and I was wondering how to use listeners in a canvas. Particularly as part of my project, the objective is for an event to occur when we drag click from one point to another. The concept is from a game called brainvita. The game need not be understood to answer my question. All I want to know is the simplest way to make a listener for the drag clicks from one point to another on the canvas?
Do I need to map the canvas to a grid and have multiple listeners? What is the simplest way?
Additionally I'm attaching the code of the game developed so far, just the basics that help in displaying the grid!
package com.project.android.brainvita;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class GameView extends View {
Paint paint = new Paint();
Paint paintF = new Paint();
Paint paintG = new Paint();
int width, height;
int MARGIN = 4;
int MARGIN_BIG = 10;
int sx = 2;
public GameView(Context context) {
super(context);
paint.setColor(Color.WHITE);
paintF.setColor(Color.rgb(40, 60, 204));
paintG.setColor(Color.rgb(240, 30, 20));
}
public void onDraw(Canvas canvas) {
// Draw external circle
canvas.drawCircle(width / 2, height / 2, (width - MARGIN) / 2, paintF);
// Calculate radius of small circles
int radius = (width - MARGIN_BIG*2) / 14;
// Draw grid
for (int j = -3; j <= 3; j++) {
if (j >= -1 && j <= 1)
for (int i = -3; i <= 3; i++) {
canvas.drawCircle(width / 2 + (2 * radius * i), height / 2
+ (2 * radius * j), radius - sx, paint);
}
else
for (int i = -1; i <= 1; i++) {
canvas.drawCircle(width / 2 + (2 * radius * i), height / 2
+ (2 * radius * j), radius - sx, paint);
}
}
}
protected void onSizeChanged(int w, int h, int ow, int oh) {
width = w;
height = h;
}
}
In android development, a canvas is used for drawing. A view is used for interacting with the user. There isn't a direct mechanism for a canvas to receive user input. This must be handled through a view.
You'll want to add an onTouchListener to the View that is hosting your canvas and use that listener to store state information about touches from the user.
Related
I am using google vision library for face detection. Face detection is perfect and I get all the info like vertices, angles like eulerY, eulerZ.
I want to draw mask on face, drawing is ok but the face mask is not following the face position as it should, the position is not correct. Here is my edited code to draw face mask on googly eyes project.
Here is my source code:
package com.google.android.gms.samples.vision.face.googlyeyes;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.google.android.gms.samples.vision.face.googlyeyes.ui.camera.GraphicOverlay;
import com.google.android.gms.vision.face.Face;
import java.util.HashMap;
/**
* Graphics class for rendering Googly Eyes on a graphic overlay given the current eye positions.
*/
class GooglyEyesGraphic extends GraphicOverlay.Graphic {
private Paint mEyeWhitesPaint;
private Paint mEyeIrisPaint;
private Paint mEyeOutlinePaint;
private Paint mEyeLidPaint;
Paint mBoxPaint;
Context mContext;
private static final float BOX_STROKE_WIDTH = 20.0 f;
FrameLayout frameLayout;
ImageView imageView;
// Bitmap bmpOriginal;
//==============================================================================================
// Methods
//==============================================================================================
GooglyEyesGraphic(GraphicOverlay overlay, Context mContext) {
super(overlay);
this.mContext = mContext;
mEyeWhitesPaint = new Paint();
mEyeWhitesPaint.setColor(Color.WHITE);
mEyeWhitesPaint.setStyle(Paint.Style.FILL);
mEyeLidPaint = new Paint();
mEyeLidPaint.setColor(Color.YELLOW);
mEyeLidPaint.setStyle(Paint.Style.FILL);
mEyeIrisPaint = new Paint();
mEyeIrisPaint.setColor(Color.BLACK);
mEyeIrisPaint.setStyle(Paint.Style.FILL);
mEyeOutlinePaint = new Paint();
mEyeOutlinePaint.setColor(Color.BLACK);
mEyeOutlinePaint.setStyle(Paint.Style.STROKE);
mEyeOutlinePaint.setStrokeWidth(5);
mBoxPaint = new Paint();
mBoxPaint.setColor(Color.MAGENTA);
mBoxPaint.setStyle(Paint.Style.STROKE);
mBoxPaint.setStrokeWidth(BOX_STROKE_WIDTH);
mBoxPaint.setAlpha(40);
LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = li.inflate(R.layout.mask_layout, null);
imageView = (ImageView) view.findViewById(R.id.flMaskIV);
frameLayout = (FrameLayout) view.findViewById(R.id.frameLayout);
}
private volatile Face mFace;
/**
* Updates the eye positions and state from the detection of the most recent frame. Invalidates
* the relevant portions of the overlay to trigger a redraw.
*/
void updateEyes(PointF leftPosition, boolean leftOpen,
PointF rightPosition, boolean rightOpen, Face mFace) {
if (facesList.containsKey(mFace.getId())) {
PointF pointF1 = facesList.get(mFace.getId()).getPosition();
PointF pointF2 = mFace.getPosition();
double x = Math.sqrt(Math.pow(pointF2.x - pointF1.x, 2) - Math.pow(pointF2.y - pointF1.y, 2));
if (x < 0)
x = (-1 * x);
if (x < 10)
return;
Log.e("face Called", "FaceCalled");
}
this.mFace = mFace;
facesList.put(mFace.getId(), mFace);
postInvalidate();
}
public HashMap < Integer, Face > facesList = new HashMap < > ();
/**
* Draws the current eye state to the supplied canvas. This will draw the eyes at the last
* reported position from the tracker, and the iris positions according to the physics
* simulations for each iris given motion and other forces.
*/
#Override
public void draw(Canvas canvas) {
if (mFace == null)
return;
// if (facesList.containsKey(mFace.getId())) {
// PointF pointF1 = facesList.get(mFace.getId()).getPosition();
// PointF pointF2 = mFace.getPosition();
//
// double x = Math.sqrt(Math.pow(pointF2.x - pointF1.x, 2) - Math.pow(pointF2.y - pointF1.y, 2));
// if (x < 0)
// x = (-1 * x);
// if (x < 10)
// return;
// Log.e("face Called", "FaceCalled");
//
// }
//
// facesList.put(mFace.getId(), mFace);
if (this.canvas == null)
this.canvas = canvas;
applyMask();
}
Drawable drawable;
Canvas canvas;
private void applyMask() {
if (canvas == null)
return;
// Log.e("mFace.getEulerY()", "mFace.getEulerY()=> " + mFace.getEulerY());
if (GooglyEyesActivity.maskImgView != null) {
GooglyEyesActivity.maskImgView.setVisibility(View.GONE);
GooglyEyesActivity.maskImgView.setImageResource(GooglyEyesActivity.currEmoticonID);
}
float x = translateX(mFace.getPosition().x + mFace.getWidth() / 2);
float y = translateY(mFace.getPosition().y + mFace.getHeight() / 2);
// Draws a bounding box around the face.
float xOffset = scaleX(mFace.getWidth() / 2.0 f);
float yOffset = scaleY(mFace.getHeight() / 2.0 f);
float left = x - xOffset - 50;
float top = y - (yOffset) - 50;
float right = x + xOffset + 50;
float bottom = y + (yOffset) + 50;
// canvas.drawRect((int) left, (int) top, (int) right, (int) bottom, mBoxPaint);
drawable = GooglyEyesActivity.maskImgView.getDrawable();
///////////////////
canvas.save();
canvas.translate(left, top);
// frameLayout.setX(left);
// frameLayout.setY(top);
Rect rect = new Rect((int) left, (int) top, (int) right, (int) bottom);
frameLayout.measure(rect.width(), rect.height());
frameLayout.setLayoutParams(new LinearLayout.LayoutParams(rect.width(), rect.height()));
frameLayout.layout(0, 0, (int) right, (int) bottom);
frameLayout.setClipBounds(rect);
imageView.setLayoutParams(new FrameLayout.LayoutParams(rect.width(), rect.height()));
imageView.setRotationY(mFace.getEulerY());
imageView.setRotation(mFace.getEulerZ());
imageView.setImageDrawable(drawable);
frameLayout.draw(canvas);
canvas.restore();
}
}
Also i need to add animations so i tried using dlib library to get landmarks points and draw it using opengl but in opengl i dont have any function to populate the vertice array i am getting from dlib. As the dlib landmarks are in points but the array there is not in such a way. Any help will be appreciated for both scenarios.
Thank you in advance.
Thanks.
I want to create spherical animation in my android app similar to this in
News Republic app.
I have tried to create a sphere so far but can anyone guide me how to proceed to develop animations like this in android.
Do we have to use opengl only or we can achieve it with other alternative option.
Also, when the text is clicked it opens the related news in a different screen.
EDIT
I finally found some solution for this, which is available under this package.
But, the animation is not smooth enough.
Let me know if anyone can help me in the smoothing of the animations?
You don't need OpenGL. You can do that using a simple view and Canvas. I wrote a bit of code for you. You can just copy it to your project, add to xml and run:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class TagCloudView extends View {
String[] tags = new String[]{"Lemon","Orange","Strawberry","Plum","Pear","Pineapple","Blackberry","Watermelon"};
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float scroll = 0;
private float prevY;
public TagCloudView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
float r = getHeight() / 3;
paint.setColor(Color.BLACK);
paint.setTextAlign(Paint.Align.CENTER);
for (int i = 0; i < tags.length; i++) {
float t = i + scroll / getHeight();
float y = (float) (r * Math.cos(Math.PI * 2 * t / tags.length)); // parametric circle equation
float z = (float) (r * Math.sin(Math.PI * 2 * t / tags.length));
paint.setTextSize((r + z) / r/2 * 40 + 20); // magic values, change to something better
paint.setAlpha((int) ((r + z) / r/2 * 127 + 128));
canvas.drawText(tags[i], getWidth() / 2, getHeight() / 2 + y, paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() != MotionEvent.ACTION_DOWN)
scroll -= event.getY() - prevY; // only one plane
prevY = event.getY();
invalidate();
return true;
}
}
To achieve the result you described, you have to add smooth scrolling using Scroller, change the circle equation to the sphere equation, tweak parameters and add some getters/setters. Using parametric equations you can also find the text touched by the user. This View looks like this:
I know that creating objects inside the onDraw method is very costly. I want to draw a matrix of rounded rectangles, which coordinates are dynamic, and I can't cache all that rectangles, because I use a scroll view and there may be a lot of rectangles, there's no other overload for drawRoundRect method, which has primitive arguments, and I forced to create a Rectangle object in every iteration. Who can suggest an effective solution for that?
#Override
protected void onDraw(Canvas canvas) {
int h = getMeasuredHeight();
int tileSize = h / rows;
for(int i = 0; i < rows; ++i) {
for(int j = 0; j < columns; ++j) {
int x = j * tileSize;
int y = i * tileSize;
canvas.drawRoundRect(new RectF(x, y, x + tileSize, y + tileSize), 10, 10, tilePaint);
}
}
}
This is a just an example, rectangles can have arbitrary coordinates.
RectF has the set(left, top, right, bottom) method. You could allocate it on the constructor and it this method to change the Rectf's bounds.
mRect.set(x, y, x + tileSize, y + tileSize);
where mRect is RectF mRect = new RectF();
I get a
Fatal signal 11 (SIGSEGV) code=1
whenever I draw a filled polygon that crosses the screen bounds. This happens on OS 4.3 on a Samsung Note 10.1. What could cause this? I assume the OS should be able to clip polygons.
The offending code:
public SplashStarShape(Context context, int x, int y, int size) {
super(context);
paint = new Paint();
star = new Path();
star.setFillType(Path.FillType.WINDING);
paint.setAntiAlias(true);
paint.setStrokeWidth(DS.scale(10));
paint.setColor(EslColors.WHITE);
int k = 0;
for (int i = 0; i < 360; i += 36) {
int radius = ((k % 2) == 0) ? size * 2 : size - DS.scale(30);
k ++;
float px = (float)x + (float) (Math.cos(0.6 + (float)i * 3.1415926f * 2.0f / (float)360.0f) * (float)(radius/2.0f));
float py = (float)y + (float) (Math.sin(0.6 + (float)i * 3.1415926f * 2.0f / (float)360.0f) * (float)(radius/2.0f));
if (i == 0) {
star.moveTo(px, py);
} else {
star.lineTo(px, py);
}
}
star.close();
this.invalidate();
}
#Override
protected void onDraw (Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(star, paint);
}
If I reduce the radius so that the star fits on screen, there is no crash. If the tentacles stick outside the screen, I get the fatal error. Very strange!
I'm developing an analog clock widget and the dial of clock is an image.
I want to draw an arc segment like in the image(as shown in orange).
paint.setColor(Color.GREEN);
paint.setStrokeWidth(20);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(100, 100, 200, paint);
I tried with drawcircle and also with drawArc but could not proceed since i want only a part of arc and not a complete arc. Any ideas ?
I made this class hope it help you, all variable are in spanish but its quite simple,
the constructor SemiCirculo use as parameters the rgb for the semicircle and the resolution (number of triangles for your semicircle)
the CalcularPuntosPorcentaje method use as parameters the center of the circle, the starting angle, the widht of the angle, and the radio.
the method ImprimeCirculo use the canvas as parameter, it is used to draw the semicircle once it has allready been calcultaed the points of the semicircle with the previus mentioned method.
the CalcularPuntosPorcentaje method is similar to CalcularPuntosPorcentaje, but insted of the starting and the widht angle parameters it use a % from 0 to 100
finaly the SetOffset and SetColor are used to change the default starting poing o reference for the angle and the color of the semicircle
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
public class SemiCirculo {
private Path circulo;
private Paint color;
private float px, py, radio, anguloI, anchoa,offset;
private int r, g, b;
private int resolucion;
private float puntox[],puntoy[];
public SemiCirculo(int r1, int g1, int b1, int resolucion1) {
this.offset = 0;
this.color = new Paint();
this.r = r1;
this.g = g1;
this.b = b1;
this.color.setColor(Color.rgb(r, g, b));
color.setAntiAlias(true);
circulo = new Path();
this.resolucion = resolucion1;
this.puntox = new float[this.resolucion];
this.puntoy = new float[this.resolucion];
this.anguloI = 0;
this.anchoa = 1;
}
public void SetOffset(float off) {
this.offset = off;
}
public void SetColor(int r1,int g1, int b1){
this.r=r1;
this.g=g1;
this.b=b1;
this.color.setColor(Color.rgb(r, g, b));
}
public void CalcularPuntosPorcentaje(float px1, float py1,
float porcentaje, float radio1) {
this.anguloI = 0 + this.offset;
this.px = px1;
this.py = py1;
this.radio = radio1;
this.anguloI = 0;
this.anchoa = porcentaje / 100 * 360;
this.CalcularPuntos(px, py, anguloI, anchoa, radio);
}
public void CalcularPuntos(float px1, float py1, float anguloI1,
float anchoangulo, float radio1) {
this.anguloI = anguloI1 + this.offset;
this.anchoa = anchoangulo;
this.px = px1;
this.py = py1;
this.radio = radio1 ;
float angulo = 360 - this.anguloI - this.anchoa;
for (int i = 0; i < resolucion; i++) {
this.puntox[i] = this.px - (float) Math.sin(Math.toRadians(angulo))
* this.radio;
this.puntoy[i] = this.py - (float) Math.cos(Math.toRadians(angulo))
* this.radio;
angulo = (360 - this.anguloI - this.anchoa)
+ ((this.anchoa / (float) (this.resolucion)) * (i + 2));
}
this.circulo.reset();
this.circulo.moveTo(this.px, this.py);
for (int i = 0; i < resolucion; i++) {
this.circulo.lineTo(this.puntox[i], this.puntoy[i]);
}
}
public void ImprimeCirculo(Canvas canvas) {
canvas.drawPath(this.circulo, this.color);
}
}
You need to use this method:
canvas.drawArc(innerRect, -11.0f, 11.0f + 6.0f, true, paintx);
for docs see here:
http://developer.android.com/reference/android/graphics/Canvas.html#drawArc%28android.graphics.RectF,%20float,%20float,%20boolean,%20android.graphics.Paint%29
Be sure set the angle parameters correctly! And use float!
The first angle is the start of your arc and the second angle param is the sweep angle, ie how many degrees long should the angle be - measured clockwise.
Try it, it will work for sure. Just needs a little playing around :-)