I want the program to draw a circle whenever the screen gets touched and if the screen gets touched on another position I want the program to draw a circle again but without deleting the old one!
Now my problem is that it doesn't just draw a new circle in addition to the old one. It draws a new circle and deletes the old one. I tried to find a solution but nothing worked.
So can anyone please help me?
So it's working now!
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
public class SingleTouchEventView extends View {
private Paint paint = new Paint();
List<Point> points = new ArrayList<Point>();
public SingleTouchEventView(Context context, AttributeSet attrs) {
super(context, attrs);}
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
paint.setColor(Color.GREEN);
for(Point p: points){
canvas.drawCircle(p.x, p.y, 20, paint);
}
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Point p = new Point();
p.x = (int)event.getX();
p.y = (int)event.getY();
points.add(p);
invalidate();
case MotionEvent.ACTION_MOVE: // a pointer was moved
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
break;
}
}
invalidate();
return true;
}
}
You can achieve it by maintaining a list of points as a private instance variable:
private List<Point> points = new ArrayList<Point>;
Then you can add new points to this list every time a new touch event occurs:
Point p = new Point();
p.x = event.getX();
p.y = event.getY();
points.add(p);
invalidate();
Now in the onDraw() method you can print all the points in the list:
paint.setColor(Color.GREEN);
for(Point point: points){
canvas.drawCircle(point.x, point.y, 20, paint);
}
invalidate();
Why not make a circle object which stores the coordinates of where to paint the circle, then add these objects to an array. Then in your paint method iterate through the array get the coordinates from each object and paint the circle using the coordinates?
So in your onclick method create a new circle object with the coordinates gained from the touch
one of my changing circle classes:
public class GrowCircle {
float x, y;int radius;
Paint myp = new Paint();
int colr,colg,colb;
int redvar=1;
int bluevar=5;
int greenvar=2;
int tripper=10;
int change=2;
Random rand = new Random();
public GrowCircle(float x, float y){
this.x=x;
this.y=y;
this.radius=2;
this.colr=rand.nextInt(254)+1;
this.colg=rand.nextInt(254)+1;
this.colb=rand.nextInt(254)+1;
}
public void update(){
if(tripper<=1||tripper>=15){
change=-change;
}
Random col = new Random();
myp.setColor(Color.argb(255,colr,colg,colb));
colr+=redvar;
colg+=greenvar;
colb+=bluevar;
if(colr<=5||colr>=250){
redvar=-redvar;
}
if(colg<=5||colg>=250){
greenvar=-greenvar;
}
if(colb<=5||colb>=250){
bluevar=-bluevar;
}
}
public void drawThis(Canvas canvas){
myp.setStrokeWidth(tripper);
myp.setStyle(Style.STROKE);
canvas.drawCircle(x, y, radius, myp);
}
}
so play with that and make it how you want and then do this:
///var declaration
CopyOnWriteArrayList<GrowCircle> gcirc= new CopyOnWriteArrayList<GrowCircle>();
//in update method
for(GrowCircle circ:gcirc){
circ.update();
}
// in draw method
for(GrowCircle circ:gcirc){
circ.drawThis(c);
}
//and in onTouch
float tx =event.getX();
float ty = event.getY();
gcirc.add(new GrowCircle(tx,ty));
Related
I asked this question before, but now I've stumbled around and figured out a direction.
Basically, I need to record the user's finger's movements around the screen, and replay an animation of that movement on a view upon pressing a replay button, as well as a reverse animation of it when pressing a reverse button. I'm trying to achieve this by saving the finger's (x,y) coordinates in onTouch(), which I store in an ArrayList for PointF objects, then create a path for the view to animate on based on the coordinates stored in said ArrayList. Then I'll need to animate along that path. However, I am not sure how to create a path based on an arraylist that could be of any length.
Code related to the animation so far:
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
/**
* TODO: document your custom view class.
*/
public class MyView extends View {
ArrayList<PointF>theCoords;
ArrayList<Float> xcord;
ArrayList<Float> ycord;
private Paint paint;
//coordinates
private float x;
private float y;
public MyView (Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
xcord = new ArrayList<Float>();
ycord= new ArrayList<Float>();
theCoords=new ArrayList<PointF>();
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPaint();
xcord = new ArrayList<Float>();
ycord= new ArrayList<Float>();
theCoords=new ArrayList<PointF>();
}
public MyView(Context context) {
super(context);
initPaint();
xcord = new ArrayList<Float>();
ycord= new ArrayList<Float>();
theCoords=new ArrayList<PointF>();
}
//initialize paint object
private void initPaint(){
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setTextSize(40);
}
#Override
//drawing on the canvas
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.drawCircle(x,y,50,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
theCoords.clear();
xcord.clear();
ycord.clear();
x = event.getX();
y = event.getY();
PointF p1 = new PointF(x,y);
theCoords.add(p1);
postInvalidate();
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
x = event.getX();
y = event.getY();
PointF p2 = new PointF(x,y);
theCoords.add(p2);
postInvalidate();
break;
}
return true;
}
#RequiresApi(api = Build.VERSION_CODES.R)
public void replayAnim()
{
PointF pcur;
PointF pnext;
//create path
for(int i =0;i<theCoords.size()-1;i++)
{
pcur=new PointF(theCoords.get(i));
pnext=new PointF(theCoords.get(i+1));
//how to form a path with these?
}
//anim along the path
}
public void reverseAnim()
{
//the original code I have, which only
//moves the view to its original position
for (int i = xcord.size() - 1; i >= 0; i--)
{
x = xcord.get(i);
y = ycord.get(i);
Log.d("ReverseAnimation", "x: " + Float.toString(x));
postInvalidate();
}
}
public float getX()
{
return x;
}
public float getY()
{
return y;
}
} ```
I am attempting to create the functionality for a basic security camera in my game. The camera sits in the top right corner of the canvas, and displays a beam of light which rotates similar to a security camera (moves 90° in one direction, then 90° back). The functionality I am aiming for is that when the mouse is touching the beam, the console outputs "touching".
So far, I have the camera beam rotating using g2d.rotate, however the hit-detection does not move with the rotated canvas. I have tried a multitude of solutions, including the theorem used to move points of a polygon around one point, but the triangle kept decreasing in size with each rotation.
Here is my code so far:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Camera {
private BufferedImage img;
private int x;
private int y;
private boolean moving = false;
private double angle;
private boolean up;
private Graphics2D graphicsTemp;
private AffineTransform at;
private Polygon beam;
private Point p1;
private Point p2;
private Point p3;
private int[] xArray;
private int[] yArray;
public Camera(int x, int y, Point p1, Point p2, Point p3) {
try {
img = ImageIO.read(getClass().getResource("Camera.png"));
} catch (IOException e) {
System.out.println("NO IMAGE");
}
this.x = x;
this.y = y;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
angle = 0;
up = true;
}
public void update() {
xArray = new int[] { p1.x, p2.x, p3.x };
yArray = new int[] { p1.y, p2.y, p3.y };
beam = new Polygon(xArray, yArray, 3);
}
public void paint(Graphics2D g2d) {
if (up) {
angle += 0.2;
} else {
angle -= 0.2;
}
if (angle >= 90.0 || angle <= 0.0) {
up = !up;
}
g2d.rotate(Math.toRadians(angle), p1.x, p1.y);
g2d.setColor(color);
g2d.fillPolygon(beam);
g2d.setColor(Color.BLACK);
g2d.drawPolygon(beam);
g2d.rotate(-Math.toRadians(angle), p1.x, p1.y);
}
public void testTouch(int mouseX, int mouseY) {
if (beam.contains(mouseX, mouseY)) {
System.out.println("INSIDE");
} else {
System.out.println("OUTSIDE");
}
}
You haven't rotated the Polygon that you're doing hit testing on, so it will use the original unrotated beam for hit testing.
As a simple solution, you can rotate the polygon before you use it for hit testing. Graphics2D uses the AffineTransform class to do its rotations, and so can you:
public void testTouch(int mouseX, int mouseY) {
AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(angle), p1.x, p1.y);
Shape rotatedBeam = transform.createTransformedShape(beam);
if (rotatedBeam.contains(mouseX, mouseY)) {
However you can also draw that rotated polygon directly in your paint method; it's better to rotate it once and use the rotated shape both for hit-testing and your paint method.
Note that it's also not a good idea to update your angle in the paint method since you don't know when and how often it is called. If your window was overlapped and then uncovered, your paint method will also be called.
I want to draw a rectangle. The first corner should be the point where user first touches screen. When user moves his finger, it should draw the rectangle. Here's a link that shows a video what I want to do. But I don't understand it and maybe You can help me. I just want to draw that rectangle on a white background not on an image.
My code :
package com.example.androiddrawing;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CanvasView extends View {
private Canvas canvas;
private Paint paint = new Paint();
private Paint paint2 = new Paint();
private Paint paint3 = new Paint();
private Path path = new Path();
private Point point = new Point();
private static List<Path> lines = new ArrayList<Path>();
private static List<Point> points = new ArrayList<Point>();
private float x, x2, xc, xd, x3, x4;
private float y, y2, yc, yd, y3, y4;
private boolean touchStarted = false;
public enum DrawMode {
FreeDrawMode, RectDrawMode
};
public static DrawMode currentDrawMode;
public void setDrawMode(DrawMode newDrawMode) {
this.currentDrawMode = newDrawMode;
}
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint2.setAntiAlias(true);
paint2.setStrokeWidth(5);
paint2.setColor(Color.RED);
paint2.setStyle(Paint.Style.STROKE);
paint2.setStrokeJoin(Paint.Join.ROUND);
paint3.setAntiAlias(true);
paint3.setColor(Color.BLACK);
paint3.setStrokeWidth(10);
paint3.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
for (Path p : lines)
canvas.drawPath(p, paint);
canvas.drawPath(path, paint2);
for (Point point : points)
canvas.drawCircle(point.x, point.y, 0, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
System.out.println(currentDrawMode);
if (currentDrawMode == DrawMode.FreeDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
paint2.setColor(Color.RED);
path = new Path();
path.moveTo(x, y);
touchStarted = true;
break;
// return true;
case MotionEvent.ACTION_MOVE:
// Connect the points
touchStarted = false;
path.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
if (touchStarted) {
point = new Point();
point.x = (int) x;
point.y = (int) y;
paint2.setColor(Color.BLACK);
points.add(point);
touchStarted = false;
System.out.println("siin");
} else {
System.out.println("seal");
paint2.setColor(Color.BLACK);
lines.add(path);
}
break;
default:
return false;
}
} else if (currentDrawMode == DrawMode.RectDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
paint3.setColor(Color.RED);
//CODE HERE
break;
// return true;
case MotionEvent.ACTION_MOVE:
//CODE HERE
break;
case MotionEvent.ACTION_UP:
//CODE HERE
break;
default:
return false;
}
}
// Makes our view repaint and call onDraw
invalidate();
return true;
}
}
I should write the code where I put the comments //CODE HERE, but I really don't understand, how do I have to draw a rectangle.
You can use the below code. Hope this helps you.
public class DrawSample extends View {
int mStartX;
int mStartY;
int mEndX;
int mEndY;
Paint mPaint = new Paint();
int mSelectedColor = Color.BLACK;
public DrawSample(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mPaint.setColor(mSelectedColor);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
setFocusable(true);
}
public DrawSample(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mStartX = (int) event.getX();
mStartY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
mEndX = (int) event.getX();
mEndY = (int) event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
mEndX = (int) event.getX();
mEndY = (int) event.getY();
invalidate();
break;
default:
super.onTouchEvent(event);
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(mStartX, mStartY, mEndX, mEndY, mPaint);
}
}
You need to keep the start X and Y points, then, when user stop drawing in the MotionEvent.ACTION_UP get the ending X and Y to get 4 corners of the rectangle:
canvas.drawRect(startX, startY, x, y, paint3);
You can find more info about drawRect here and here.
EDIT i dont have nice environment to test it... but I found some errors in your answer:
else if (currentDrawMode == DrawMode.RectDrawMode) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Set a new starting point
paint3.setColor(Color.RED);
startX = event.getX();
startY = event.getY();
break;
// return true;
case MotionEvent.ACTION_MOVE:
endX = event.getX();
endY = event.getY();
canvas.drawRect(startX, startY, endX, endY, paint3);
//invalidate(); // Tell View that the canvas needs to be redrawn
break;
case MotionEvent.ACTION_UP:
paint3.setColor(Color.BLACK);
canvas.drawRect(startX, startY, endX, endY, paint3);
break;
default:
return false;
}
PS: if you want add info to your question edit it, but dont post new answers!!!
I am looking to create an analogue like controller in order to move my character within my xml layout (this will look similar to that on a PlayStation or Xbox controller - the circular analogue).
My first plan was to simple have an Image View and place 4 transparent buttons over the Image View to allow for movement, however this is a bit dull and would like to get really creative with it, however I am struggling to find any kind of documentation and not really sure where to start.
Essentially, the functionality that I am after is an image which looks like an analogue (see below), the user can then press down on and move around with animation, which will trigger the relevant positional movement. Ideally i would also like to have this so that is the user simply clicks to the right on the analogue it automatically animates and then triggers the right movement.
I am not sure if this is possible at all, I require this to be implemented within my xml layout or can be created programmatically if that is easier.
please see the LAST EDIT
----------------------------------------------
You have to implement the GestureDetector.OnGestureListener interface, and use the onDown, onScroll methods to detect the gestures, and then using the onDraw(Canvas c) method of your view , redraw the bitmap of your analogue after modifying it's position (and transform it with matrix if you want)
You can also use the onTouchEvent like in this answer :
OnUp event on GestureDetector
---------------------------------------------------------------------------------------
EDIT
Here is a try, without using bitmaps, just drawCircle method :
it's not too complicated,
i wrote it fast so there is certainly improvements to do, but it works quite well as is :
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 AnalogueView extends View {
float x,y;
double r,t;
int cx, cy,w,h;
final int RADIUS = 20;
Paint black = new Paint();
Paint grey = new Paint();
Paint white = new Paint();
private int toDo;
public AnalogueView(Context context, AttributeSet attrs) {
super(context, attrs);
black.setColor(Color.BLACK);
grey.setColor(Color.GRAY);
white.setColor(Color.WHITE);
black.setFlags(Paint.ANTI_ALIAS_FLAG);
white.setFlags(Paint.ANTI_ALIAS_FLAG);
grey.setFlags(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
this.w = w;
this.h = h;
cx = w/2;
cy = h/2;
x = cx;y=cy;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawMyStuff(canvas);
switch (toDo) {
case 1:
center();
break;
default:
break;
}
}
private void drawMyStuff(final Canvas canvas) {
canvas.drawCircle(cx, cy, w/2, black);
canvas.drawCircle(cx, cy, w/2-5, grey);
canvas.drawCircle(cx, cy, w/2-10, black);
canvas.drawCircle(x, y, RADIUS+2, white);
canvas.drawCircle(x, y, RADIUS, grey);
}
// n2p : normal to polar coordinates conversion
// p2n : polar to normal coordinates conversion
// R : distance to polar center
// T : polar angle
double n2pR(double x, double y){
return distance(x,y,cx,cy);
}
double n2pT(double x, double y){
return Math.atan2((y-cy),(x-cx));
}
double p2nX(double r, double t){
return r*Math.cos(t) + cx;
}
double p2nY(double r, double t){
return r*Math.sin(t) + cy;
}
double n2pR(){
return distance(x,y,cx,cy);
}
double n2pT(){
return Math.atan2((y-cy),(x-cx));
}
double p2nX(){
return r*Math.cos(t) + cx;
}
double p2nY(){
return r*Math.sin(t) + cy;
}
double distance(double x1 , double y1, double x2, double y2 ){
return Math.sqrt(Math.pow(x1-x2, 2)+Math.pow(y1-y2, 2));
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN :
updatePosition(event);
break;
case MotionEvent.ACTION_MOVE :
updatePosition(event);
break;
case MotionEvent.ACTION_UP :
toDo = 1;
center();
break;
default :break;
}
return true;
}
private void center(){
if(r>15){
r-=15;
}else{
toDo=0;
r=0;
}
x = (float) p2nX();
y= (float) p2nY();
invalidate();
}
void updatePosition(MotionEvent e){
r= Math.min(w/2-RADIUS, n2pR(e.getX(),e.getY()));
t = n2pT(e.getX(),e.getY());
x = (float) p2nX();
y =(float) p2nY();
invalidate();
}
}
here is how to add it to a layout :
<yourpackage.AnalogueView
android:id="#+id/analogueView1"
android:layout_width="80px"
android:layout_height="80px"
/>
And for detecting the different analogue events you can add a Listener with the methods definitions you need, and use the interface within the AnalogView, then when you want to implement a behavior for the AnalogView you just add an implementation to the inteface by calling a setter for the listener (it's like the setOnClickListener for a Button). This way you will have an entirely encapsulated AnalogView that you can reuse in any other app.
-------------------------------------------------------------------------------
LAST EDIT : handling events with listener :
I embedded a Listener with the AnalogueView , now you can use it the same way you use the OnClickListener for a button,
use the setOnMoveListener method to provide your implementation of the two methods :
onMaxMoveInDirection(double polar_angle_of_direction) and
onHalfMoveInDirection(double polar_angle_of_direction)
Here is the new code for AnalogueView (no big modifications, just the interface definition, the moveListener field and fire the actions in the updatePosition) :
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 AnalogueView extends View {
OnMoveListener moveListener;
float x,y;
double r,t;
int cx, cy,w,h;
final int RADIUS = 20;
Paint black = new Paint();
Paint grey = new Paint();
Paint white = new Paint();
private int toDo;
public AnalogueView(Context context, AttributeSet attrs) {
super(context, attrs);
black.setColor(Color.BLACK);
grey.setColor(Color.GRAY);
white.setColor(Color.WHITE);
black.setFlags(Paint.ANTI_ALIAS_FLAG);
white.setFlags(Paint.ANTI_ALIAS_FLAG);
grey.setFlags(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
this.w = w;
this.h = h;
cx = w/2;
cy = h/2;
x = cx;y=cy;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawMyStuff(canvas);
switch (toDo) {
case 1:
center();
break;
default:
break;
}
}
private void drawMyStuff(final Canvas canvas) {
canvas.drawCircle(cx, cy, w/2, black);
canvas.drawCircle(cx, cy, w/2-5, grey);
canvas.drawCircle(cx, cy, w/2-10, black);
canvas.drawCircle(x, y, RADIUS+2, white);
canvas.drawCircle(x, y, RADIUS, grey);
}
// n2p : normal to polar coordinates conversion
// p2n : polar to normal coordinates conversion
// R : distance to polar center
// T : polar angle
double n2pR(double x, double y){
return distance(x,y,cx,cy);
}
double n2pT(double x, double y){
return Math.atan2((y-cy),(x-cx));
}
double p2nX(double r, double t){
return r*Math.cos(t) + cx;
}
double p2nY(double r, double t){
return r*Math.sin(t) + cy;
}
double n2pR(){
return distance(x,y,cx,cy);
}
double n2pT(){
return Math.atan2((y-cy),(x-cx));
}
double p2nX(){
return r*Math.cos(t) + cx;
}
double p2nY(){
return r*Math.sin(t) + cy;
}
double distance(double x1 , double y1, double x2, double y2 ){
return Math.sqrt(Math.pow(x1-x2, 2)+Math.pow(y1-y2, 2));
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN :
updatePosition(event);
break;
case MotionEvent.ACTION_MOVE :
updatePosition(event);
break;
case MotionEvent.ACTION_UP :
toDo = 1;
center();
break;
default :break;
}
return true;
}
private void center(){
if(r>15){
r-=15;
}else{
toDo=0;
r=0;
}
x = (float) p2nX();
y= (float) p2nY();
invalidate();
}
void updatePosition(MotionEvent e){
r= Math.min(w/2-RADIUS, n2pR(e.getX(),e.getY()));
t = n2pT(e.getX(),e.getY());
x = (float) p2nX();
y =(float) p2nY();
if(moveListener != null)
if(r == w/2-RADIUS)
moveListener.onMaxMoveInDirection(t);
else if(r >= w/4-RADIUS/2)
moveListener.onHalfMoveInDirection(t);
invalidate();
}
public void setOnMoveListener(OnMoveListener listener){
moveListener =listener;
}
public interface OnMoveListener{
public void onHalfMoveInDirection(double polarAngle);
public void onMaxMoveInDirection(double polarAngle);
}
}
And here is how to provide an implementation :
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import com.analog.AnalogueView.OnMoveListener;
public class MainActivity extends Activity {
AnalogueView analogue;
TextView showMoveEvent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showMoveEvent = (TextView) findViewById(R.id.showMoveEvent);
analogue = (AnalogueView) findViewById(R.id.analogueView1);
analogue.setOnMoveListener(new OnMoveListener() {
#Override
public void onMaxMoveInDirection(double polarAngle) {
showMoveEvent.setText("max move in "+polarAngle+" direction");
}
#Override
public void onHalfMoveInDirection(double polarAngle) {
showMoveEvent.setText("half move in "+polarAngle+" direction");
}
});
}
}
Result : showing events in a text field :
I have an array of objects that need to be drawn to a canvas; each object is represented as:
scatterPlot.java
package scatter.plot;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.view.View;
public class scatterPoint extends View {
private final Point coordinates;
private final int itemShape;
private Paint itemColour = new Paint(Paint.ANTI_ALIAS_FLAG);
private scatterPoint[] mShapes;
public scatterPoint(Context context, Point p, int shape, Paint colour) { // Constructor
super(context);
coordinates = p;
itemShape = shape;
itemColour = colour;
}
//get set points
public void setPoints(scatterPoint[] p){
mShapes = p;
}
public scatterPoint[] getScatterPoints(){
return mShapes;
}
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
int radius = 10;
for (scatterPoint i : mShapes) {
switch(itemShape){
case 0:
canvas.drawRect(i.coordinates.x - radius, i.coordinates.y - radius, i.coordinates.x + radius, i.coordinates.y + radius, i.itemColour);
break;
case 1:
Path path = new Path();
path.moveTo(i.coordinates.x - radius, i.coordinates.y - radius);
path.lineTo(i.coordinates.x, i.coordinates.y + radius);
path.lineTo(i.coordinates.x + radius, i.coordinates.y - radius);
path.lineTo(i.coordinates.x - radius, i.coordinates.y - radius);
path.close();
canvas.drawPath(path, i.itemColour);
break;
case 2:
canvas.drawCircle(i.coordinates.x, i.coordinates.y, radius, i.itemColour);
break;
}
}
}
public Point getCoordinates(){
return coordinates;
}
public int getShape(){
return itemShape;
}
public Paint getColour(){
return itemColour;
}
}
Relevant methods from main (ScatterPlotActivity.java):
package scatter.plot;
import java.util.Random;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.WindowManager;
import android.widget.FrameLayout;
public class ScatterPlotActivity extends Activity {
FrameLayout main;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
main = (FrameLayout) findViewById(R.id.main_view);
scatterPoint[] points = generatePoints();
//
//add scatterPoint to main
//
}
public scatterPoint[] generatePoints(){
//
return points;
}
}
Any ideas on what is wrong with how I'm trying to draw these objects?
I think the problem comes from the fact that you're creating each time a new FrameLayout. Declare your FrameLayout in your main and then call your method drawPoints like this:
drawPoint(points[i], main);
and your method will look like this :
public void drawPoint(scatterPoint point, FrameLayout main) {
main.addView(point);
}
This way they will always be added to the same Layout.
The problem is:
public void drawPoint(scatterPoint point) {
FrameLayout main = (FrameLayout) findViewById(R.id.main_view);
main.addView(point);
}
Make your FrameLayout main; a global variable. Then, right after you call setContentView(R.layout.x); in onActivityCreated(), call main = (FrameLayout) findViewById(R.id.main_view);
You only want to make this call once. Every time the current implementation calls public void drawPoint(scatterPoint point);, the FrameLayout is being reinitialized and clearing all the old drawings.
By the looks of things, you are actually drawing all of your scatter point views. However, due to how you are adding them to the FrameLayout, they are stacking on top of each other. Look in your drawPoint() method. You add the point to FrameLayout, which was designed to only handle one view (for more on this statement refer to FrameLayout), and the FrameLayout just draws the new view right over the last view drawn.
class ScatterShape {
public float mX = 0, mY = 0;
public int mShape = 0;
public Paint mColor = new Paint();
public ScatterShape(float x, float y, int shape, int color) {
mX = x;
mY = y;
mShape = shape;
mColor.setColor(color);
}
}
public class ScatterShaperDrawer extends View {
private List<ScatterShape> mShapes = new ArrayList<ScatterShapes>();
private float mmRadius = 5;
// Make getter and setter
#Override public void onDraw(Canvas canvas) {
for (ScatterShape i : mShapes) {
switch(i.mShape){
case 0:
canvas.drawRect(i.mX - mRadius, i.mY - mRadius, i.mX + mRadius, i.mY + mRadius, itemColor);
break;
case 1:
Path path = new Path();
path.moveTo(i.mX - mRadius, i.mY - mRadius);
path.lineTo(i.mX, i.mY + mRadius);
path.lineTo(i.mX + mRadius, i.mY - mRadius);
path.lineTo(i.mX - mRadius, i.mY - mRadius);
path.close();
canvas.drawPath(path, i.mColor);
break;
case 2:
canvas.drawCircle(i.mX, i.mY, mRadius, i.mColor);
break;
}
}
}
}