7 package com.google.appinventor.components.runtime;
21 import android.os.Handler;
23 import java.util.HashSet;
39 private static final String LOG_TAG =
"Sprite";
40 private static final boolean DEFAULT_ENABLED =
true;
41 private static final int DEFAULT_HEADING = 0;
42 private static final int DEFAULT_INTERVAL = 100;
43 private static final float DEFAULT_SPEED = 0.0f;
44 private static final boolean DEFAULT_VISIBLE =
true;
45 private static final double DEFAULT_Z = 1.0;
50 private final Handler androidUIHandler;
56 private final Set<Sprite> registeredCollisions;
105 androidUIHandler = handler;
108 if (!(container instanceof
Canvas)) {
111 this.canvas = (
Canvas) container;
112 this.canvas.addSprite(
this);
115 registeredCollisions =
new HashSet<Sprite>();
118 timerInternal =
new TimerInternal(
this, DEFAULT_ENABLED, DEFAULT_INTERVAL, handler);
120 this.form = container.
$form();
127 Speed(DEFAULT_SPEED);
144 this(container,
new Handler());
149 canvas.registerChange(
this);
164 description =
"Controls whether the %type% moves and can be interacted with " +
165 "through collisions, dragging, touching, and flinging.")
167 return timerInternal.
Enabled();
178 defaultValue = DEFAULT_ENABLED ?
"True" :
"False")
181 timerInternal.
Enabled(enabled);
191 description =
"Returns the %type%'s heading in degrees above the positive " +
192 "x-axis. Zero degrees is toward the right of the screen; 90 degrees is toward the " +
193 "top of the screen.")
209 defaultValue = DEFAULT_HEADING +
"")
229 description =
"The interval in milliseconds at which the %type%'s " +
230 "position is updated. For example, if the interval is 50 and the speed is 10, " +
231 "then every 50 milliseconds the sprite will move 10 pixels in the heading direction.")
244 defaultValue = DEFAULT_INTERVAL +
"")
258 description =
"The number of pixels that the %type% should move every interval, if enabled.")
261 defaultValue = DEFAULT_SPEED +
"")
274 description =
"The speed at which the %type% moves. The %type% moves " +
275 "this many pixels every interval if enabled.")
299 defaultValue = DEFAULT_VISIBLE ?
"True" :
"False")
310 private double xLeftToCenter(
double xLeft) {
314 private double xCenterToLeft(
double xCenter) {
320 private void updateX(
double x) {
323 xLeft = xCenterToLeft(x);
330 @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT,
331 defaultValue =
"0.0")
333 category = PropertyCategory.APPEARANCE)
334 public
void X(
double x) {
339 private double yTopToCenter(
double yTop) {
343 private double yCenterToTop(
double yCenter) {
349 private void updateY(
double y) {
352 yTop = yCenterToTop(y);
359 @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_FLOAT,
360 defaultValue =
"0.0")
362 public void Y(
double y) {
381 defaultValue = DEFAULT_Z +
"")
382 public
void Z(
double layer) {
384 canvas.changeSpriteLayer(
this);
388 description =
"How the %type% should be layered relative to other Balls and ImageSprites, " +
389 "with higher-numbered layers in front of lower-numbered layers.")
416 final String eventName,
417 final Object... args) {
418 androidUIHandler.post(
new Runnable() {
436 if (!registeredCollisions.contains(other)) {
437 registeredCollisions.add(other);
460 description =
"Event handler called when a %type% is dragged. " +
461 "On all calls, the starting coordinates " +
462 "are where the screen was first touched, and the \"current\" coordinates " +
463 "describe the endpoint of the current line segment. On the first call " +
464 "within a given drag, the \"previous\" coordinates are the same as the " +
465 "starting coordinates; subsequently, they are the \"current\" coordinates " +
466 "from the prior call. Note that the %type% won't actually move " +
467 "anywhere in response to the Dragged event unless MoveTo is explicitly called. " +
468 "For smooth movement, each of its coordinates should be set to the sum of its " +
469 "initial value and the difference between its current and previous values.")
470 public
void Dragged(
float startX,
float startY,
471 float prevX,
float prevY,
472 float currentX,
float currentY) {
473 postEvent(
this,
"Dragged", startX, startY, prevX, prevY, currentX, currentY);
484 description =
"Event handler called when the %type% reaches an edge of the screen. " +
485 "If Bounce is then called with that edge, the %type% will appear to " +
486 "bounce off of the edge it reached. Edge here is represented as an integer that " +
487 "indicates one of eight directions north (1), northeast (2), east (3), southeast (4), " +
488 "south (-1), southwest (-2), west (-3), and northwest (-4).")
511 description =
"Event handler called when a pair of sprites (Balls and ImageSprites) are no " +
514 registeredCollisions.remove(other);
515 postEvent(
this,
"NoLongerCollidingWith", other);
526 description =
"Event handler called when the user touches an enabled " +
527 "%type% and then immediately lifts their finger. The provided x and y coordinates " +
528 "are relative to the upper left of the canvas.")
548 description =
"Event handler called when a fling gesture (quick swipe) is made on " +
549 "an enabled %type%. This provides the x and y coordinates of the start of the " +
550 "fling (relative to the upper left of the canvas), the speed (pixels per millisecond), " +
551 "the heading (0-360 degrees), and the x and y velocity components of " +
552 "the fling's vector.")
566 description =
"Event handler called when the user stops touching an enabled %type% " +
567 "(lifting their finger after a TouchDown event). This provides the " +
568 "x and y coordinates of the touch, relative to the upper left of the canvas.")
582 description =
"Event handler called when the user begins touching an enabled %type% " +
583 "(placing their finger on a %type% and leaving it there). This provides the " +
584 "x and y coordinates of the touch, relative to the upper left of the canvas.")
608 description =
"Makes the %type% bounce, as if off a wall. " +
609 "For normal bouncing, the edge argument should be the one returned by EdgeReached.")
617 if (normalizedAngle < 0) {
618 normalizedAngle += 360;
624 && (normalizedAngle < 90 || normalizedAngle > 270))
626 && (normalizedAngle > 90 && normalizedAngle < 270))) {
627 Heading(180 - normalizedAngle);
629 && normalizedAngle > 0 && normalizedAngle < 180)
631 Heading(360 - normalizedAngle);
633 && normalizedAngle > 0 && normalizedAngle < 90)
635 && normalizedAngle > 90 && normalizedAngle < 180)
637 && normalizedAngle > 180 && normalizedAngle < 270)
639 Heading(180 + normalizedAngle);
655 description =
"Indicates whether a collision has been registered between this %type% " +
656 "and the passed sprite (Ball or ImageSprite).")
658 return registeredCollisions.contains(other);
669 description =
"Moves the %type% back in bounds if part of it extends out of bounds, " +
670 "having no effect otherwise. If the %type% is too wide to fit on the " +
671 "canvas, this aligns the left side of the %type% with the left side of the " +
672 "canvas. If the %type% is too tall to fit on the canvas, this aligns the " +
673 "top side of the %type% with the top side of the canvas.")
698 description =
"Turns the %type% to point towards a designated " +
699 "target sprite (Ball or ImageSprite). The new heading will be parallel to the line joining " +
700 "the centerpoints of the two sprites.")
712 description =
"Sets the heading of the %type% toward the point " +
713 "with the coordinates (x, y).")
740 canvas.registerChange(
this);
767 boolean moved =
false;
773 if (
Width() > canvasWidth) {
782 }
else if (overWestEdge()) {
786 }
else if (overEastEdge(canvasWidth)) {
794 if (
Height() > canvasHeight) {
803 }
else if (overNorthEdge()) {
807 }
else if (overSouthEdge(canvasHeight)) {
833 private final boolean overWestEdge() {
837 private final boolean overEastEdge(
int canvasWidth) {
841 private final boolean overNorthEdge() {
845 private final boolean overSouthEdge(
int canvasHeight) {
849 protected int hitEdge(
int canvasWidth,
int canvasHeight) {
853 boolean west = overWestEdge();
854 boolean north = overNorthEdge();
855 boolean east = overEastEdge(canvasWidth);
856 boolean south = overSouthEdge(canvasHeight);
859 if (!(north || south || east || west)) {
1018 canvas.removeSprite(
this);
1028 protected abstract void onDraw(android.graphics.Canvas
canvas);