I want to simulate a collision between two balls, one controlled by the accelerometer and the other just moving randomly.
How do I implement these collisions? I've managed to set up the balls and the condition for collision, but beyond that, I have no idea how I can represent the collision vectors. An algorithm or a hint would be appreciated :) I have the x and y velocities and positions of the two balls stored in variables. I also managed to use some basic (yet wrong) collision mechanism, the random motion ball reverses its x and y velocities. But I'm having problems there too, if the balls are moving too fast, they overlap. What do I do?
package perseus.gfx.test;
import everything;
public class Ball extends View {
RectF lol;
Paint paint, lpaint;
Bitmap bitmap;
Canvas canvas;
private float ballx = 150;
private float bally = 140;
private double speedx = 0;
private double speedy = 0; //ignore
private double accx, accy=0;
private float rad = 15;
private float mult = 0.5f;
private float friction = -0.001f;
private double xv, yv, xS, yS;
private long ltm = -1;
int width, height;
int xmax, ymax;
int xmin, ymin;
public Ball(Context context) {
super(context);
lol = new RectF();
paint = new Paint();
paint.setColor(Color.CYAN);
lpaint = new Paint();
lpaint.setColor(Color.GRAY);
}
public double getSpeedX()
{
return this.speedx;
}
public double getSpeedY()
{
return this.speedy;
}
public void setSpeedX(double x)
{
this.speedx = x;
}
public void setSpeedY(double y)
{
this.speedy = y;
}
public void setColor(int c)
{
paint.setColor(c);
}
public float getRad()
{
return this.rad;
}
public void setRad(float rad)
{
this.rad = rad;
}
public void setAX(double accx)
{
this.accx = accx;
}
public void setAY(double accy)
{
this.accy = accy;
}
public float getX()
{
return this.ballx;
}
public void setX(float ballx)
{
this.ballx = ballx;
}
public float getY()
{
return this.bally;
}
public void setY(float bally)
{
this.bally = bally;
}
public void moveBall() {
xv = accx * mult;
yv = accy * mult;
ballx -= xv * mult;
bally -= yv * mult;
ballx +=speedx;
bally +=speedy;
/*ballx +=speedx;
bally +=speedy;*/
// Collision detection
if (ballx + rad > xmax) {
speedx = -speedx;
ballx = xmax-rad;
}
else if (ballx - rad < 0) {
speedx = -speedx;
ballx = rad;
}
if (bally + rad > 2*ymax/3) {
speedy = -speedy;
bally = 2*ymax/3 - rad;
}
else if (bally - rad < 0) {
speedy = -speedy;
bally = rad;
}
try {
Thread.sleep(20);
} catch(InterruptedException e) {}
invalidate();
}
#Override
public void onMeasure(int widthM, int heightM)
{
width = View.MeasureSpec.getSize(widthM);
height = View.MeasureSpec.getSize(heightM);
xmax = width-1;
ymax = height-1;
xmin = 0;
ymin = 0;
setMeasuredDimension(width, height);
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
#Override
public void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, 0, 0, paint);
lol.set(ballx - rad, bally-rad, ballx + rad, bally+rad);
canvas.drawLine(0, 2*height/3, width, 2*height/3, lpaint);
/*canvas.drawOval(lol, paint);*/
canvas.drawCircle(ballx, bally, rad, paint);
canvas.drawText(xv + " " + yv, 0, height/2, lpaint);
canvas.save();
moveBall();
canvas.restore();
}
}
This is my ball class, and my main activity would be:
package perseus.gfx.test;
import everything;
public class GfxActivity extends Activity implements OnSeekBarChangeListener, OnItemSelectedListener, SensorEventListener {
ViewGroup.LayoutParams vg = new ViewGroup.LayoutParams(-1, -1);
double velx, vely;
double x, y;
float radi;
double finx, finy;
float finrad;
long lastSensorUpdate = -1;
SensorManager sm;
static double ux, uy; //initial ball velocity
SeekBar velo, rad;
Spinner colour;
Ball ball, tester;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ball = new Ball(this);
tester = new Ball(this);
setContentView(R.layout.main);
addContentView(ball, vg);
addContentView(tester, vg);
sm = (SensorManager)getSystemService(SENSOR_SERVICE);
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME);
colour = (Spinner)findViewById(R.id.color);
colour.setOnItemSelectedListener(this);
ArrayAdapter<CharSequence> aa = ArrayAdapter.createFromResource(this, R.array.colors, android.R.layout.simple_spinner_item);
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
colour.setAdapter(aa);
velo = (SeekBar)findViewById(R.id.vel);
velo.setOnSeekBarChangeListener(this);
rad = (SeekBar)findViewById(R.id.rad);
rad.setOnSeekBarChangeListener(this);
ux = ball.getSpeedX();
uy = ball.getSpeedY();
radi = ball.getRad();
tester.setSpeedX(5);
tester.setSpeedY(3);
tester.setX(0);
tester.setY(0);
tester.setColor(Color.DKGRAY);
}
#Override
public void onProgressChanged(SeekBar seeker, int arg1, boolean arg2) {
switch(seeker.getId())
{
case R.id.vel:
x = ball.getSpeedX();
y = ball.getSpeedY();
if(x<0)
finx = -ux-arg1;
else if(x >= 0)
finx = ux+arg1;
if(y<0)
finy = -uy-arg1;
else if(finy>=0)
finy = uy+arg1;
ball.setSpeedX(finx);
ball.setSpeedY(finy);
break;
case R.id.rad:
finrad = radi + arg1;
ball.setRad(finrad);
break;
}
}
#Override
public void onStartTrackingTouch(SeekBar arg0) {
//nothing lol
}
#Override
public void onStopTrackingTouch(SeekBar arg0) {
//nothing lol
}
#Override
public void onItemSelected(AdapterView<?> parent, View v, int position,
long id) {
switch(position)
{
case 0:
ball.setColor(Color.CYAN);
break;
case 1:
ball.setColor(Color.RED);
break;
case 2:
ball.setColor(Color.BLUE);
break;
case 3:
ball.setColor(Color.GREEN);
break;
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
#Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent sensorevent) {
if(sensorevent.sensor.getType() == Sensor.TYPE_ORIENTATION)
{
ball.setAX(sensorevent.values[2]);
ball.setAY(sensorevent.values[1]);
}
if((Math.pow(ball.getX() - tester.getX(), 2) + Math.pow(ball.getY() - tester.getY(), 2)) <= (ball.getRad() + tester.getRad())*(ball.getRad() + tester.getRad()))
{
/*tester.setSpeedX(-1*tester.getSpeedX());
tester.setSpeedY(-1*tester.getSpeedY());*/
}
}
}
Ball and Tester are my two balls, Ball is controlled by the accelerometer. I'm aware that the code isn't very efficient, but I thought I'll work on implementing the collision first.
You'll need to consider a couple things before you can refine your collision algorithm - is the collision to be an elastic collision? Can you treat the two balls as point sources? Either way your momentum vectors will be conserved, so you can use Newton's P initial = P final to determine the new vectors.
The two balls are likely overlapping because the rate at which you are running your collision detection algorithm is slower than the rate at which you change the position of the balls.
If you post a code sample, we can help you better.
Considering you have xVel1, yVel1, xVel2, yVel2; xPos1, yPos1, xPos2, yPos2; mass1, mass2; after detecting the collision:
double theta = Math.atan2(yPos1-yPos2, xPos1-xPos2);
double c = Math.cos(theta);
double s = Math.sin(theta);
double xVel1prime = xVel1*c+yVel1*s;
double xVel2prime = xVel2*c+yVel2*s;
double yVel1prime = yVel1*c-xVel1*s;
double yVel2prime = yVel2*c-xVel2*s;
double P = (mass1*xVel1prime+mass2*xVel2prime);
double V = (xVel1prime-xVel2prime);
double v2f = (P+mass1*V)/(mass1+mass2);
double v1f = v2f-xVel1prime+xVel2prime;
xVel1prime = v1f;
xVel2prime = v2f;
xVel1 = xVel1prime*c-yVel1prime*s;
xVel2 = xVel2prime*c-yVel2prime*s;
yVel1 = yVel1prime*c+xVel1prime*s;
yVel2 = yVel2prime*c+xVel2prime*s;
//velocities updated
Related
I wanted to show profile status using a Progress bar just like Linkedin, I searched everywhere but didn't get any reference related to that.
My code:
public class ProgressDrawable extends Drawable {
private static final int NUM_SEGMENTS = 5;
private final int mForeground;
private final int mBackground;
private final Paint mPaint = new Paint();
private final RectF mSegment = new RectF();
public ProgressDrawable(int fgColor, int bgColor) {
mForeground = fgColor;
mBackground = bgColor;
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
#Override
public void draw(Canvas canvas) {
float level = getLevel() / 10000f;
Rect b = getBounds();
float gapWidth = b.height() / 10f;
float segmentWidth = (b.width() - (NUM_SEGMENTS - 1) * gapWidth) / NUM_SEGMENTS;
mSegment.set(0, 0, segmentWidth, b.height());
mPaint.setColor(mForeground);
for (int i = 0; i < NUM_SEGMENTS; i++) {
float loLevel = i / (float) NUM_SEGMENTS;
float hiLevel = (i + 1) / (float) NUM_SEGMENTS;
if (loLevel <= level && level <= hiLevel) {
float middle = mSegment.left + NUM_SEGMENTS * segmentWidth * (level - loLevel);
canvas.drawRect(mSegment.left, mSegment.top, middle, mSegment.bottom, mPaint);
mPaint.setColor(mBackground);
canvas.drawRect(middle, mSegment.top, mSegment.right, mSegment.bottom, mPaint);
} else {
canvas.drawRect(mSegment, mPaint);
}
mSegment.offset(mSegment.width() + gapWidth, 0);
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
In this code, all the blocks have same color, but as per requirement, all block colors should be different.
I am working on a small simple game in which the hurdles coming out from top and there is an static ball which can only move on x-axis.When the hurdles coming out from top the user have to move the ball to avoid the collision.
I am placing 3 moving hurdles at a time.but my problem is they are coming out together i.e all three hurdles have the same y-axis values.I want it to come out one by one with some specific distance.
How can i achieve this.
Here is my GamePanel Class:
public class GamePanel extends SurfaceView implements Runnable {
private Thread thread = null;
private Ball ball;
private SurfaceHolder surfaceHolder;
private Paint paint;
private Canvas canvas;
volatile boolean playing = true;
private int hurdleCount = 3;
private Hurdles[] hurdles;
private int screenX, screenY;
private Rect ball_detectCollision;
public GamePanel(Context context, final int screenX, final int screenY) {
super(context);
ball = new Ball(context, screenX, screenY);
surfaceHolder = getHolder();
this.screenX = screenX;
this.screenY = screenY;
paint = new Paint();
canvas = new Canvas();
hurdles = new Hurdles[hurdleCount];
for (int i = 0; i < hurdleCount; i++) {
hurdles[i] = new Hurdles(context, screenX, screenY);
}
ball_detectCollision = new Rect(ball.getBall_x(), ball.getBall_y(), ball.getBitmap().getWidth(), ball.getBitmap().getHeight());
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
System.out.println("Surface Created");
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
System.out.println("Surface Changed");
thread = new Thread(GamePanel.this);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
System.out.println("Surface Destroyed");
}
});
}
private void draw() {
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.RED);
canvas.drawBitmap(ball.getBitmap(), updatedValue, ball.getBall_y(), paint);
ball.setBall_x(updatedValue);
ball_detectCollision.left = ball.getBall_x();
ball_detectCollision.top = screenY - ball.getBitmap().getHeight() - 260;
ball_detectCollision.right = ball.getBall_x() + ball.getBitmap().getWidth();
ball_detectCollision.bottom = screenY - ball.getBitmap().getHeight() - 260 + ball.getBitmap().getHeight();
for (int i = 0; i < hurdleCount; i++) {
canvas.drawBitmap(hurdles[i].getBitmap(), hurdles[i].getX(), hurdles[i].getY(), paint);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
#Override
public void run() {
while (playing) {
update();
draw();
control();
}
}
private void update() {
for (int i = 0; i < hurdleCount; i++) {
hurdles[i].update();
if (Rect.intersects(getBall_detectCollision(), hurdles[i].getDetectCollision())) {
System.out.println("Collision Detected");
playing = false;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
showGameOverMessage();
}
});
}
}
}
public void pause() {
playing = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
And this is my Hurdle Class
public class Hurdles {
private Bitmap bitmap;
private int x;
private int y;
private int speed = 20;
private int maxX;
private int minX;
private int maxY;
private int minY;
private Rect detectCollision;
public Hurdles(Context context, int screenX, int screenY) {
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.hurdle);
maxX = screenX - bitmap.getWidth();
maxY = screenY;
minX = 0;
minY = 0;
Random generator = new Random();
detectCollision = new Rect(x, y, bitmap.getWidth(), bitmap.getHeight());
x = generator.nextInt(maxX);
y = minY;
}
public void update() {
y += speed;
if (y > maxY - getBitmap().getHeight()) {
Random generator = new Random();
y = minY;
x = generator.nextInt(maxX);
}
detectCollision.left = x;
detectCollision.right = x + bitmap.getWidth();
detectCollision.top = y;
detectCollision.bottom = y + bitmap.getHeight();
}
If you want to add delay/gap between hurdles, you can do it in your GamePanel constructor like :
public GamePanel(Context context, final int screenX, final int screenY) {
super(context);
int gapBetweenHurdles = 100;
int gap = 0;
for (int i = 0; i < hurdleCount; i++) {
//(screenY - gap) will move your hurdle above the screen
hurdles[i] = new Hurdles(context, screenX, screenY - gap);
//increment the gap
gap += gapBetweenHurdles;
}
......
}
So the gap between the hurdles is 100 pixels as i have written randomly. If you want specific gap, you can set gapBetweenHurdles to some percentage of the screen height.
EDIT:
You have to pass the initial X and Y position to the hurdle constructor and then in update method of the hurdle class increment the Y value and in Hurdle class, getY() should return Y.
Try changing the code like this:
handler.postDelayed(new Runnable() {
#Override
public void run() {
showGameOverMessage();
}
},timeOffsetInMillis);
If the gaps can be the same you can set timeOffsetInMillis = i * gapInMillis
So I am new to android games development and I have 4 CircleMapObjects which are yellow coloured I am using an InputProcessor to manage touch/click events. Now in the touchDown() method what I want is when a Circle is touched/clicked in the game (anywhere inside the circle ) then it should change to say the colour green. I am using the libgdx framework and am new to the framework, I have come across Actor however I've found that it draws rectangular shapes from looking at the API.
The InputHandler class:
public class InputHandler implements InputProcessor {
private Spot spot;
public InputHandler(Spot spot){
this.spot = spot;
}
#Override
public boolean keyDown(int keycode) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean keyUp(int keycode) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean keyTyped(char character) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
float distanceX = (float) Math.sqrt(Math.pow((spot.getSpots()[0].getCircle().x - (screenX)), 2));
float distanceY = (float) Math.sqrt(Math.pow((spot.getSpots()[0].getCircle().y - (screenY)), 2));
//Below is for testing purposes not to do with touching a circle but touching a particular
//region on the screen
//float distance = distanceX + distanceY;
// System.out.println(distanceX);
// System.out.println(distanceY);
// System.out.println(spot.getSpots()[0].isVisible() + "" + "Color: " + spot.getSpots()[0].getColor());
System.out.println("CLICKED: " + " x: " + screenX + " y: " + screenY);
if (screenX > 45 && screenY < 431)
{
spot.getSpots()[0].setColor(Color.RED);
System.out.println("This works!");
}
return true;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean scrolled(int amount) {
// TODO Auto-generated method stub
return false;
}
}
The Spot class:
public class Spot {
private float x;
private float y;
private float radius;
//Use CircleMapObject instead?
private CircleMapObject spot_1, spot_2, spot_3, spot_4;
private CircleMapObject spotList[];
private float elapsedTime;
private int index;
public Spot(float x, float y, float radius){
this.x = x;
this.y = y;
this.radius = radius;
//each spot initialised
spot_1 = new CircleMapObject(x,y,radius);
spot_2 = new CircleMapObject(x, y-228,radius);
spot_3 = new CircleMapObject(x+440,y,radius);
spot_4 = new CircleMapObject(x+440,y-228,radius);
//spot list initialised
spotList = new CircleMapObject[4];
//adding the spots to the spotlist
spotList[0] = spot_1;
spotList[1] = spot_2;
spotList[2] = spot_3;
spotList[3] = spot_4;
elapsedTime = 0.0f;
index = 0;
}
public void update(float delta){
}
}
The Gamescreen where the inputHandler is set:
public class GameScreen implements Screen {
private GameWorld world;
private GameRenderer render;
private Spot spot;
public GameScreen(){
float screenWidth = Gdx.graphics.getWidth();
float screenHeight = Gdx.graphics.getHeight();
System.out.println("Screen width: " + screenWidth + ", " + "Screen height: " + screenHeight);
float gameWidth = 136;
float gameHeight = screenHeight / (screenWidth / gameWidth);
world = new GameWorld();
render = new GameRenderer(world);
Gdx.input.setInputProcessor(new InputHandler(world.getSpot()));
}
... (there's more code in the GameScreen class but this is the only reference of the InputHandler used in this class. This also applies to the rest of the code.)
To simplify this you only need to check if the distance of the circle vs touch location is less then the radius of the circle to detect a hit. Simple vector math does the trick, we calculate the distance horizontally and vertically and with Pythagoras (A² + B² = C²) we can calculate the final distance.
distance = √(circleX - (touchX))² + ((circleY) - touchY)²
if (Math.abs(distance) < circleRadius)
{
//Touch within circle...
}
Libgdx has some vector functions for calculating the distance.
if (touchVector.dst(circleOrigin) < circleRadius)
{
//Touch within circle...
}
When i am talking about touchVector you need to use Vector2 and put your coordinates in. When working with positions, directions, velocities, etc you should be using the Vector2 class or Vector3 for 3D environments.
Vector2 touchVector = new Vector2(x,y);
float distance = touchVector.dst(new Vector2(circleOriginX, circleOriginY));
I have an image(ball) its a BitmapDrawable and it moves in all directions on the screen for that i am taking onDraw(canvas) in AnimatedView.java its working fine.
My Requirement:
I want to click on the image, but unfortunately the whole screen takes the event.
Please help me.
How to get click event only on the image?
So that i can proceed with further development.
Here is the code:
MainActivity
public class MainActivity extends Activity {
Context context;
int count=0;
EditText text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AnimatedView animatedView=(AnimatedView) findViewById(R.id.anim_view);
animatedView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText text=(EditText) findViewById(R.id.editText1);
ImageView imageView=(ImageView) findViewById(R.drawable.ball);
v.startAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.animation));
}
});
animatedview.java:
public class AnimatedView extends ImageView{
private Context mContext;
int x = -1;
int y = -1;
private int xVelocity = 10;
private int yVelocity = 5;
private Handler h;
private final int FRAME_RATE = 30;
BitmapDrawable ball;
public AnimatedView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
h = new Handler();
}
private Runnable r = new Runnable() {
#Override
public void run() {
invalidate();
}
};
#Override
protected void onDraw(Canvas c) {
BitmapDrawable ball = (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.ball);
if (x<0 && y <0) {
x = this.getWidth()/2;
y = this.getHeight()/2;
} else {
x += xVelocity;
y += yVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
xVelocity = xVelocity*-1;
}
if ((y > this.getHeight() - ball.getBitmap().getHeight()) || (y < 0)) {
yVelocity = yVelocity*-1;
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
h.postDelayed(r, FRAME_RATE);
}
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#000000">
<com.example.example.AnimatedView
android:id="#+id/anim_view"
android:layout_width="fill_parent"
android:layout_height="420dp"
android:onClick="imageClicked" />
</LinearLayout>
**I hope This codes Works for you, what you are looking for.....**
This is the complete code representing both ball movement and touch on ball.
public class MainActivity extends Activity implements OnTouchListener
{
BubbleGraphic bubbleGraphic;
int bm_x = 0, bm_y = 0, bm_offsetx, bm_offsety, bm_w, bm_h, subBM_w, subBM_h;
int f = 0;
private float x, y;
Canvas canvas;
boolean dm_touched = false;
static int random_x;
static int random_y;
boolean touching;
// These variable for position in x_axis direction
int w1;
// These variable for position in y_axis direction
int h1;
// These variable for velocity in x_axis direction
private int w1_V;
// These variable for velocity in y_axis direction
private int h1_V;
Bitmap mainBG;
Bitmap bmpBall;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bubbleGraphic = new BubbleGraphic(this);
bubbleGraphic.setOnTouchListener(this);
// Your Background Image of Canvas
mainBG = BitmapFactory.decodeResource(getResources(), R.drawable.bg1);
w1 = -1;
h1 = -1;
w1_V = 1;
h1_V = 1;
// This is used to create bitmap object and decode the input stream into bitmap
bmpBall = BitmapFactory.decodeResource(getResources(),
R.drawable.myBall);
bm_w = bmpBall.getWidth();
bm_h = bmpBall.getHeight();
setContentView(bubbleGraphic);
}
protected void onPause() {
super.onPause();
bubbleGraphic.pause();
}
protected void onResume() {
super.onResume();
bubbleGraphic.resume();
}
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
touching = true;
if (dm_touched) {
w1 = (int) x - bm_offsetx;
h1 = (int) y - bm_offsety;
}
break;
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
touching = true;
// checking if Ball is touched
if ((x > w1) && (x < bm_w + w1) && (y > h1) && (y < bm_h + h1)) {
bm_offsetx = (int) x - w1;
bm_offsety = (int) y - h1;
}
dm_touched = true;
break;
case MotionEvent.ACTION_UP:
default:
dm_touched = false;
touching = false;
}
return true;
}
// **************************************************************************************//
// Creating Canvas graphic Class and running thread //
// *************************************************************************************//
class BubbleGraphic extends SurfaceView implements Runnable {
SurfaceHolder holder;
Thread thread = null;
boolean isRunning = false;
boolean holdingBubble = true;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public BubbleGraphic(Context context) {
super(context);
holder = getHolder();
}
public void pause() {
isRunning = false;
while (true) {
try {
thread.join();
} catch (Exception e) {
}
break;
}
thread = null;
}
public void resume() {
isRunning = true;
thread = new Thread(this);
thread.start();
}
public void run() {
while (isRunning) {
if (!holder.getSurface().isValid())
continue;
canvas = holder.lockCanvas();`enter code here`
canvas.drawBitmap(mainBG, 0, 0, null);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setColor(Color.TRANSPARENT);
if (touching) {
canvas.drawRect(x, y, x + bm_w, y + bm_h, paint);
}
canvas.drawBitmap(bmpBall, w1, h1, null);
// Method Responsible for ball movement and its position
checkBallMovementAndPosition();
holder.unlockCanvasAndPost(canvas);
}
}
private void checkBallMovementAndPosition() {
if (w1 < 0 && h1 < 0) {
w1 = this.getWidth() / 2;
h1 = this.getHeight() / 2;
} else {
w1 += w1_V;
h1 += h1_V;
if ((w1 > this.getWidth() - bm_w) || (w1 < 0)) {
w1_V = w1_V * -1;
w1 += w1_V;
}
if ((h1 > this.getHeight() - bm_h) || (h1 < 0)) {
h1_V = h1_V * -1;
h1 += h1_V;
}
}
}
}
}
Note: Replace the images with Your images.
I have created custom drawable marker which uses canvas to draw
bounds. Everything works great excepts one thing: onItemSingleTapUp
not called when any marker on the screen taped.
Here is overlay creation code:
ItemizedIconOverlay<OverlayItem> groupsOverlay = new ItemizedIconOverlay<OverlayItem>(
new ArrayList<OverlayItem>(),
new OnItemGestureListener<OverlayItem>() {
#Override
public boolean onItemLongPress(int arg0, OverlayItem arg1) {
return false;
}
#Override
public boolean onItemSingleTapUp(int arg0, OverlayItem arg1) {
if(arg1 == null){
return false;
}
if(m_prevView != null){
m_mapView.removeView(m_prevView);
m_prevView = null;
}
View popUp = getLayoutInflater().inflate(R.layout.map_popup, m_mapView, false);
TextView tv = (TextView)popUp.findViewById(R.id.popupTextView);
tv.setText(arg1.getTitle());
MapView.LayoutParams mapParams = new MapView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
arg1.getPoint(),
MapView.LayoutParams.BOTTOM_CENTER, 0, 0);
m_mapView.addView(popUp, mapParams);
m_prevView = popUp;
return true;
}
}, new DefaultResourceProxyImpl(getApplicationContext()));
This is custom drawable marker:
package com.testapp.data;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
public class GroupMarkerDrawable extends Drawable {
private final static int DELTA_BOX = 4;
private final Paint m_paint;
private String m_text;
private double m_pxRadius;
public GroupMarkerDrawable(double pxRadius, String text) {
m_text = text;
m_paint = new Paint();
m_pxRadius = pxRadius;
m_paint.setAntiAlias(true);
}
#Override
public void draw(Canvas c) {
// Set the correct values in the Paint
m_paint.setARGB(190, 0, 0, 0);
m_paint.setStrokeWidth(2);
m_paint.setStyle(Style.STROKE);
m_paint.setTextAlign(Align.CENTER);
Rect bounds = new Rect();
m_paint.getTextBounds(m_text, 0, m_text.length(), bounds);
int centerX = getBounds().centerX();
int centerY = getBounds().centerY();
int w2 = bounds.width() / 2;
int h2 = bounds.height() / 2;
Rect rect = new Rect(centerX - w2 - DELTA_BOX, centerY - h2 -
DELTA_BOX, centerX + w2 + DELTA_BOX, centerY + h2 + DELTA_BOX);
// Draw it
c.drawCircle(centerX, centerY, (float) m_pxRadius, m_paint);
m_paint.setStyle(Style.FILL);
m_paint.setARGB(190, 0, 128, 0);
c.drawRect(rect, m_paint);
m_paint.setStyle(Style.STROKE);
m_paint.setARGB(190, 0, 0, 0);
c.drawRect(rect, m_paint);
c.drawText(m_text, centerX, centerY + h2, m_paint);
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
#Override
public void setAlpha(int arg0) {
}
#Override
public void setColorFilter(ColorFilter arg0) {
}
}
Same code using static drawable from resources, instead of
GroupMarkerDrawable, works.
I found how to solve this. Just need to return proper dimensions for Drawable. This requires overriding of two methods in GroupMarkerDrawable. FOr example like this:
#Override
public int getIntrinsicHeight() {
return m_pxRadius * 2;
};
#Override
public int getIntrinsicWidth() {
return m_pxRadius * 2;
};
An additional solution for a polygon.
The solutions of ydanila put me on the good track but I had to override hitTest method of ItemizedOverlayWithFocus class in order to get my polygon hit.
Drawable:
Drawable drawable = new Drawable() {
private int mIntrinsicHeight = 0;
private int mIntrinsicWidth = 0;
#Override
public void draw(Canvas canvas) {
// used to determine limit coordinates of the drawable
int yTop, yBottom, xLeft, xRight;
if (points != null && points.size() > 1) {
//we have to make a projection to convert from postions on the map in
//gradiant to a position on the view in pixels
final Projection pj = mapView.getProjection();
Path path = new Path();
Point centerMapPixelPoint = new Point();
Point tmpMapPixelPoint = new Point();
pj.toMapPixels(points.get(0), centerMapPixelPoint);
// init limit coordinates
xLeft = centerMapPixelPoint.x;
xRight = centerMapPixelPoint.x;
yTop = centerMapPixelPoint.y;
yBottom = centerMapPixelPoint.y;
path.moveTo(centerMapPixelPoint.x, centerMapPixelPoint.y);
for (int i = 1; i < points.size(); i++) {
pj.toMapPixels(points.get(i), tmpMapPixelPoint);
// update limit coordinates if necessary
if (xLeft > tmpMapPixelPoint.x) {
xLeft = tmpMapPixelPoint.x;
}
if (xRight < tmpMapPixelPoint.x) {
xRight = tmpMapPixelPoint.x;
}
if (yBottom < tmpMapPixelPoint.y) {
yBottom = tmpMapPixelPoint.y;
}
if (yTop > tmpMapPixelPoint.y) {
yTop = tmpMapPixelPoint.y;
}
path.lineTo(tmpMapPixelPoint.x, tmpMapPixelPoint.y);
}
// close polygon returning to first point
path.close();
canvas.drawPath(path, linePaint);
canvas.drawPath(path, innerPaint);
// calculate drawable height and width
mIntrinsicHeight = yTop -yBottom;
mIntrinsicWidth = xRight - xLeft;
}
}
#Override
public int getIntrinsicHeight() {
return mIntrinsicHeight;
};
#Override
public int getIntrinsicWidth() {
return mIntrinsicWidth;
};
};
Overlay:
public class MyItemizedIconOverlay<Item extends OverlayItem> extends ItemizedOverlayWithFocus<Item> {
#Override
protected boolean hitTest(Item item, Drawable marker, int hitX, int hitY) {
boolean hit = false;
Rect bounds = marker.getBounds();
if (hitX < bounds.right && hitX > bounds.left && hitY < bounds.top && hitY > bounds.bottom) {
hit = true;
} else {
hit = false;
}
return hit;
};