I'm new to this so don't blame me. I am trying to develop an android app that would make music. I am trying to make a bar that rotates over a bunch of buttons that are displayed in the form of a circle, and when it does to play the sound represented by every button. However so far I managed to make an image rotate around the middle of the screen by setting x and y coordinates representing the centre of the circle, but when I try to put the formula (x + radius*sin(angle)), (y + radius*cos(angle)), it just moves the image I want to rotate at that point. So basically I am trying to rotate an Image around an circle defined by buttons or coordinates rather then an actual circle image. So I need to rotate an image or imageView around a circle, not just a point.
I have added the code ass well so you could have a look at what I'm doing wrong.
ImageView bara = (ImageView) findViewById(R.id.floating_image);
layoutParams[9] = new RelativeLayout.LayoutParams
(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
toop = Math.round(size.x/2); // + 90*Math.sin(ANGLE));
lefft = Math.round(size.y/2); // + 90*Math.cos(ANGLE));
top = (int) toop;
left = (int) lefft;
layoutParams[9].setMargins(top, left, 0, 0);
bara.setLayoutParams(layoutParams[9]);
RotateAnimation rAnim = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0 , Animation.RELATIVE_TO_SELF, 0);
rAnim.setRepeatCount(Animation.INFINITE);
rAnim.setInterpolator(new LinearInterpolator());
rAnim.setDuration(8000);
bara.startAnimation(rAnim);
Any help would be really appreciated !!
the code looks like :
private float mCalcX;//x-coord of object
private float mCalcY;//y-coord of object
private double mCenterX;//x-coord of center of circle
private double mCenterY;//y-coord of center of circle
private double mRadius;//circle radius
private double mAngleRadians;//angle of your object to draw in RADs
// whenever you draw the object, calculate the new X and Y coords
mCalcX = (float) (mCenterX+(mRadius*Math.cos(mAngleRadians)));
mCalcY = (float) (mCenterY+(mRadius*Math.sin(mAngleRadians)));
public void setRadius(double r)
{
mRadius = r;
}
public void setStartingAngle(double radians)
{
mAngleRadians = radians;
}
public void setRotationSpeed(double radians)
{
mRotationSpeed = radians;
}
public void increaseRotationAngle()
{
mAngleRadians += mRotationSpeed;
}
public void decreaseRotationAngle()
{
mAngleRadians -= mRotationSpeed;
}
x^2 + y^2 = r^2
Reference: http://www.mathwarehouse.com/geometry/circle/equation-of-a-circle.php
You should animate the centre of your object around all the (x,y) that satisfy that equation for your chosen value of r (the radius of the circle).
I'm not a graphics guy, so forgive the terseness of my response.
Related
I am looking for a way to connect pan gesture with percentage of animation completion. Let me show you what I mean.
This image represents an animation that I want to execute, namely a moving Image actor or a sprite. The animation gets executed by pan gesture. Animation is 100% complete and at stage 6 when user slides for a 200px. If user slided only 100px, it would be 50% complete and at stage 3. If the user didn't execute pan gesture the animation stays at 0% and at stage 1. I am looking for tips on how to start building such a model. I believe it is called interactive. Do you have any suggestions?
You can use a GestureDetector to handle the panning input. The gestureListener.pan method can update an animation position parameter.
private int screenPixelsToAnimationPositionRatio = 0.01f; //will need to tweak this and probably adjust it at runtime based on screen dimensions and resolution
private float animationPosition = 0; //from 0 to 1, fraction of animation complete
public void create () {
//...
GestureAdapter gestureAdapter = new GestureAdapter {
#Override
public boolean pan (float x, float y, float deltaX, float deltaY) {
animationPosition += deltaX * screenPixelsToAnimationPositionRatio;
animationPosition = MathUtils.clamp(animationPosition, 0, 1);
return true;
}
};
GestureDetector gestureDetector = new GestureDetector(gestureAdapter);
Gdx.input.setInputProcessor(gestureDetector); //or put the detector in an InputMultiplexer with your other input listeners.
}
Then you would create a method that can update your object's position and rotation based on the current value of animationPosition. You would need to figure out the equations that determine the movement you want. For example, something that looks sort of like what you illustrated above:
private void updateAnimation (){
x = animationPosition * 30;
float y = 0, rotation = 0;
if (animationPosition >= 0.25f) {
float jumpPosition = Math.min(1, (animationPosition - 0.25f) / 0.5f);
y = 30 * (1 - Interpolation.pow2In.apply(Math.abs(2 * jumpPosition - 1)));
rotation = 180 * jumpPosition;
}
mySprite.setPosition(x, y);
mySprite.setRotation(rotation);
}
Then call this update method somewhere in render.
I am going through one problem even i searched lot of blogs and tutorial but nothing is working for me.
I have this above view where bold border shape is text-view without any text and i have two button UP and DOWN(not shown in image). Now i want to move this bold border view with some angle. Suppose i click UP button then it should be move with angle 10 degree UP but other end should remain constant. Some thing like hands of clock. One end XY coordinate constant and other end XY coordinate will change with some angle every time when we click on UP button. I have tried this
float x1=txtv_legside.getLeft();
float y1=txtv_legside.getTop();
float x2=txtv_legside.getRight();
float y2=txtv_legside.getBottom();
System.out.println("starting co-ordinates ("+x1+","+y1+")");
System.out.println("end co-ordinates ("+x2+","+y2+")");
float lenght = lengthOfLine(x1, y1, x2, y2);
txtv_legside.setRight((int) (x1+lenght* Math.cos(10*3.14/180)));
txtv_legside.setBottom((int) (y1+lenght* Math.sin(40*3.14/180)));
and
public float lengthOfLine(float x1, float y1, float x2 , float y2){
float length = (float) Math.sqrt(((x2-x1)*(x2-x1))+((y2-y1)*(y2-y1)));
System.out.println("length of line "+length);
return length;
}
But not working for me
Create a angle variable in your activity class.
private int angle = 0;
create a method getAngle in your activity:
public int getRotateAngle(){
angle = angle+10;
return angle*(-1);
}
on up button click listener :
upBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Animation an = new RotateAnimation(angle*(-1), getRotateAngle(), 0, 25);
an.setDuration(1000); // duration in ms
an.setRepeatCount(0); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE); // reverses each repeat
an.setFillAfter(true);
an.setFillEnabled(true);
tvView.startAnimation(an);
}
});
its out put is :
If what you want to know are the new coordinates of the moving edge, it is as simple as this:
given the constant length of the shape L (in pixels), a rotation D (in degrees, counterclockwise and 0 in the horizontal position of your draw), and the coordinates of the fixed edge (X0,Y0),
the coordinates of the moving edge (XY) are
X=X0+L* cos(D*PI/180)
Y=Y0+L* sin(D*PI/180)
(D*PI/180) is to express the angle in radians
I would like to have a bubble with a precentage value in my app, I can't use 9 patches as i want it to be customizable and its background color should be changeble.
It should look something like this
How can I do it? This bubble will have views inflated inside of it, like this percentage or some larger layouts.
Also depending on the layout(phone or tablet) it might have one side larger than the other (arrow not at the center) so that's another reason i prefer doing it programmatically
Create a custom Drawable and use it for the background of whatever container you are putting your text or other views into.
You will need to modify the padding of the background to take the bubble's pointer into account.
The code below allows you to set the alignment of the pointer as LEFT, CENTER or RIGHT.
This is just a basic version to give you an idea. You could easily add a setter for the bubble color, or add stroke properties to 'mPaint' for additional flexibility.
public class BubbleDrawable extends Drawable {
// Public Class Constants
////////////////////////////////////////////////////////////
public static final int LEFT = 0;
public static final int CENTER = 1;
public static final int RIGHT = 2;
// Private Instance Variables
////////////////////////////////////////////////////////////
private Paint mPaint;
private int mColor;
private RectF mBoxRect;
private int mBoxWidth;
private int mBoxHeight;
private float mCornerRad;
private Rect mBoxPadding = new Rect();
private Path mPointer;
private int mPointerWidth;
private int mPointerHeight;
private int mPointerAlignment;
// Constructors
////////////////////////////////////////////////////////////
public BubbleDrawable(int pointerAlignment) {
setPointerAlignment(pointerAlignment);
initBubble();
}
// Setters
////////////////////////////////////////////////////////////
public void setPadding(int left, int top, int right, int bottom) {
mBoxPadding.left = left;
mBoxPadding.top = top;
mBoxPadding.right = right;
mBoxPadding.bottom = bottom;
}
public void setCornerRadius(float cornerRad) {
mCornerRad = cornerRad;
}
public void setPointerAlignment(int pointerAlignment) {
if (pointerAlignment < 0 || pointerAlignment > 3) {
Log.e("BubbleDrawable", "Invalid pointerAlignment argument");
} else {
mPointerAlignment = pointerAlignment;
}
}
public void setPointerWidth(int pointerWidth) {
mPointerWidth = pointerWidth;
}
public void setPointerHeight(int pointerHeight) {
mPointerHeight = pointerHeight;
}
// Private Methods
////////////////////////////////////////////////////////////
private void initBubble() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mColor = Color.RED;
mPaint.setColor(mColor);
mCornerRad = 0;
setPointerWidth(40);
setPointerHeight(40);
}
private void updatePointerPath() {
mPointer = new Path();
mPointer.setFillType(Path.FillType.EVEN_ODD);
// Set the starting point
mPointer.moveTo(pointerHorizontalStart(), mBoxHeight);
// Define the lines
mPointer.rLineTo(mPointerWidth, 0);
mPointer.rLineTo(-(mPointerWidth / 2), mPointerHeight);
mPointer.rLineTo(-(mPointerWidth / 2), -mPointerHeight);
mPointer.close();
}
private float pointerHorizontalStart() {
float x = 0;
switch (mPointerAlignment) {
case LEFT:
x = mCornerRad;
break;
case CENTER:
x = (mBoxWidth / 2) - (mPointerWidth / 2);
break;
case RIGHT:
x = mBoxWidth - mCornerRad - mPointerWidth;
}
return x;
}
// Superclass Override Methods
////////////////////////////////////////////////////////////
#Override
public void draw(Canvas canvas) {
mBoxRect = new RectF(0.0f, 0.0f, mBoxWidth, mBoxHeight);
canvas.drawRoundRect(mBoxRect, mCornerRad, mCornerRad, mPaint);
updatePointerPath();
canvas.drawPath(mPointer, mPaint);
}
#Override
public int getOpacity() {
return 255;
}
#Override
public void setAlpha(int alpha) {
// TODO Auto-generated method stub
}
#Override
public void setColorFilter(ColorFilter cf) {
// TODO Auto-generated method stub
}
#Override
public boolean getPadding(Rect padding) {
padding.set(mBoxPadding);
// Adjust the padding to include the height of the pointer
padding.bottom += mPointerHeight;
return true;
}
#Override
protected void onBoundsChange(Rect bounds) {
mBoxWidth = bounds.width();
mBoxHeight = getBounds().height() - mPointerHeight;
super.onBoundsChange(bounds);
}
}
Usage
The example below shows how you might use BubbleDrawable.
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.myLayout);
BubbleDrawable myBubble = new BubbleDrawable(BubbleDrawable.CENTER);
myBubble.setCornerRadius(20);
myBubble.setPointerAlignment(BubbleDrawable.RIGHT);
myBubble.setPadding(25, 25, 25, 25);
linearLayout.setBackgroundDrawable(myBubble);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<LinearLayout
android:id="#+id/myLayout"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Other Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
</RelativeLayout>
Obviously it's never a good idea to have code in your app that you don't understand, so I won't just write out a bunch of equations in java code for you. If however you follow and understand the maths below, then it will be a relatively simple matter for you to use the described equations in your code and draw the arc.
To achieve a rounded tip on the pointer you will need to modify updatePointerPath().
At the moment it just uses rLineTo() to draw three lines forming a triangle.
There is another method in the android Path class called arcTo() which takes the form:
arcTo(RectF oval, float startAngle, float sweepAngle)
You can use this method to draw your arc at the tip of the pointer, but you need to work out a few things first.
You can already calculate the coordinates of the three corners of the pointer triangle. This is what updatePointerPath() does already. Now take a look at the diagram below. To use arcTo(), you will need to calculate the following:
The coordinates of point T which is where your arc will start.
The coordinates of the top left and bottom right corners of the bounding RectF
Your starting angle ()
Your sweep angle (2 * )
The start angle can easily be found with basic trig as shown in the diagram below.
Note: It will be best if you stick to using Radians instead of Degrees for all the angles since this is what all the trig functions in the android 'Math' class require.
With this in mind:
There are 2 Radians in a circle
The three angles in a triangle add up to Radians
A right angle is /2 Radians
So adding the three angles in the triangle formed by points C, T and P you get:
+ + /2 =
Therefore
= /2 -
So we have now calculated and .
Next, d is the distance between the point P and the bottom of the bounding box.
You can get it by calculating the distance from point C to point P, and then subtracting the radius r.
Now:
sin() = r / (distance from C to P)
Therefore:
distance from C to P = r / sin()
And so given that the distance d is the distance from point C to point P minus the radius r, we get:
d = (r / sin()) - r
That gives you all the info you need to calculate the coordinates of the top left and bottom right corners of the bounding RectF.
Now all that's left is to work out the coordinates of point T.
First work out the distance from P to T.
Given that:
tan() = r / (distance from P to T)
We get:
distance from P to T = r / tan()
Finally, adding one more point to the diagram....
We can see that:
sin() = (distance from P to A) / (distance from P to T)
So:
distance from P to A = (distance from P to T) * sin()
Similarly:
cos() = (distance from T to A) / (distance from P to T)
So:
distance from T to A = (distance from P to T) * cos()
With this info you can now calculate the coordinates of point T !!
If you understood all that, then the coding from here is easy. If you're unsure of anything, then just ask.
The following gives an idea how the updated updatePointerPath() might look.
private void updatePointerPath() {
float xDistance;
float yDistance;
mPointer = new Path();
mPointer.setFillType(Path.FillType.EVEN_ODD);
// Set the starting point (top left corner of the pointer)
mPointer.moveTo(pointerHorizontalStart(), mBoxHeight);
// Define the lines
// First draw a line to the top right corner
xDistance= mPointerWidth;
yDistance= 0;
mPointer.rLineTo(xDistance, yDistance);
// Next draw a line down to point T
xDistance= (mPointerWidth / 2) - distancePtoA;
yDistance= mPointerHeight - distanceTtoA;
mPointer.rLineTo(-xDistance, yDistance); //Note: Negative sign because we are moving back to the left
// Next draw the arc
// Note: The RectF used in arcTo() is defined in absolute screen coordinates
float boundingBoxLeft = pointerHorizontalStart() + (mPointerWidth / 2) - (2 * radius);
float boundingBoxTop = mBoxHeight - distanceD - (2 * radius);
float boundingBoxRight = boundingBoxLeft + (2 * radius);
float boundingBoxBottom = boundingBoxTop + (2 * radius);
RectF boundingBox = new RectF(boundingBoxLeft, boundingBoxTop, boundingBoxRight, boundingBoxBottom);
// Note: While the methods in the android Math class like sin() and asin() all use Radians,
// for some reason it was decided that arcTo() in the Path class would use Degrees,
// so we have to convert the angles
float startAngleInDegrees = angleAlpha * (180 / Math.PI);
float sweepAngleInDegrees = 2 * anglePhi * (180 / Math.PI);
mPointer.arcTo(boundingBox, startAngleInDegrees, sweepAngleInDegrees);
// Finally draw the line from the end of the arc back up to the top left corner
// Note: The distances are the same as from the top right corner to point T,
// just the direction is different.
mPointer.rLineTo(-xDistance, -yDistance); // Note: Negative in front of yDistance because we are moving up
// Close off the path
mPointer.close();
}
I am developing a game for Android using LibGDX. I have added pinch zoom and pan. My issue is how to keep from going outside of the play area. As it is, you can pan outside of the play area into blackness. When zoomed out fully I know how to deal with it, I just said:
if(camera.zoom == 1.0f) ;
else {
}
But, if zoomed in, how do I accomplish this. I know this is not that complicated, I just can't seem to figure it out. Upon creation I set the camera to the middle of the screen. I know how to pan, I am using camera.translate(-input.deltaX, -input.deltaY, 0), I just need to test before this call to see if the position is outside of the play area. When I am zoomed in, how do I test if I am at the edge of the screen?
You can use one of
camera.frustum.boundsInFrustum(BoundingBox box)
camera.frustum.pointInFrustum(Vector3 point)
camera.frustum.sphereInFrustum(Vector3 point, float radius)
to check if a point/box/sphere is within your camera's view.
What I normally do is define 4 boxes around my world where the player should not be allowed to see. If the camera is moved and one of the boxes is in the frustum, I move the camera back to the previous position.
Edit: AAvering has implemented this in code below.
Credit goes to Matsemann for idea, here is the implementation I used.
Make a custom MyCamera class extending OrthographicCamera and add the following code:
BoundingBox left, right, top, bottom = null;
public void setWorldBounds(int left, int bottom, int width, int height) {
int top = bottom + height;
int right = left + width;
this.left = new BoundingBox(new Vector3(left - 2, 0, 0), new Vector3(left -1, top, 0));
this.right = new BoundingBox(new Vector3(right + 1, 0, 0), new Vector3(right + 2, top, 0));
this.top = new BoundingBox(new Vector3(0, top + 1, 0), new Vector3(right, top + 2, 0));
this.bottom = new BoundingBox(new Vector3(0, bottom - 1, 0), new Vector3(right, bottom - 2, 0));
}
Vector3 lastPosition = new Vector3();
#Override
public void translate(float x, float y) {
lastPosition.set(position.x, position.y, 0);
super.translate(x, y);
}
public void translateSafe(float x, float y) {
translate(x, y);
update();
ensureBounds();
update();
}
public void ensureBounds() {
if (frustum.boundsInFrustum(left) || frustum.boundsInFrustum(right) || frustum.boundsInFrustum(top) || frustum.boundsInFrustum(bottom)) {
position.set(lastPosition);
}
}
Now, in you custom sceene or whathever you use (in my case it was a custom Board class) call:
camera.setWorldBounds()
and in your GestureListener.pan method you can call
camera.translateSafe(x, y);
it should keep your camera in bounds
Here's the code I call after the position of the camera is updated due to panning or zooming in my 2D game using an orthographic camera. It corrects the camera position so that it doesn't show anything outside the borders of the play area.
float camX = camera.position.x;
float camY = camera.position.y;
Vector2 camMin = new Vector2(camera.viewportWidth, camera.viewportHeight);
camMin.scl(camera.zoom/2); //bring to center and scale by the zoom level
Vector2 camMax = new Vector2(borderWidth, borderHeight);
camMax.sub(camMin); //bring to center
//keep camera within borders
camX = Math.min(camMax.x, Math.max(camX, camMin.x));
camY = Math.min(camMax.y, Math.max(camY, camMin.y));
camera.position.set(camX, camY, camera.position.z);
camMin is the lowest left corner that the camera can be without showing anything outside of the play area and is also the offset from a corner of the camera to the center.
camMax is the opposite highest right location the camera can be in.
The key part I'm guessing you're missing is scaling the camera size by the zoom level.
Here's my solution:
float minCameraX = camera.zoom * (camera.viewportWidth / 2);
float maxCameraX = worldSize.x - minCameraX;
float minCameraY = camera.zoom * (camera.viewportHeight / 2);
float maxCameraY = worldSize.y - minCameraY;
camera.position.set(Math.min(maxCameraX, Math.max(targetX, minCameraX)),
Math.min(maxCameraY, Math.max(targetY, minCameraY)),
0);
Where:
targetX and targetY are world coordinates of where your target is.
worldSize is a Vector2 of the size of the world.
I don't have enough reputation to write comments, so I'll point to some previous answers.
AAverin's solution with bounding box that's made with Matsemann's idea isn't good because it annoyingly slows when you are near the one edge (boundary) and trying to translate diagonally in which case you are panning to one side out of bounds and other in proper direction.
I strongly suggest that you try solution from the bottom of handleInput method presented at
https://github.com/libgdx/libgdx/wiki/Orthographic-camera
That one works smoothly, and some of the previous answers look like that one but this one uses MathUtils.clamp wihch is a straight forward and much cleaner.
Perfect class for this, (partly thanks to AAverin)
This class not only sticks into the bounds it also snaps into the bounds when you zoom.
Call these for setting bounds and moving the camera.
camera.setWorldBounds()
camera.translateSafe(x, y);
When zooming call
camera.attemptZoom();
And here's the class:
public class CustomCamera extends OrthographicCamera
{
public CustomCamera() {}
public CustomCamera(float viewportWidth, float viewportHeight)
{
super(viewportWidth, viewportHeight);
}
BoundingBox left, right, top, bottom = null;
public void setWorldBounds(int left, int bottom, int width, int height) {
int top = bottom + height;
int right = left + width;
this.left = new BoundingBox(new Vector3(left - 2, 0, 0), new Vector3(left -1, top, 0));
this.right = new BoundingBox(new Vector3(right + 1, 0, 0), new Vector3(right + 2, top, 0));
this.top = new BoundingBox(new Vector3(0, top + 1, 0), new Vector3(right, top + 2, 0));
this.bottom = new BoundingBox(new Vector3(0, bottom - 1, 0), new Vector3(right, bottom - 2, 0));
}
Vector3 lastPosition;
#Override
public void translate(float x, float y) {
lastPosition = new Vector3(position);
super.translate(x, y);
}
public void translateSafe(float x, float y) {
translate(x, y);
update();
ensureBounds();
update();
}
public void ensureBounds()
{
if(isInsideBounds())
{
position.set(lastPosition);
}
}
private boolean isInsideBounds()
{
if(frustum.boundsInFrustum(left) || frustum.boundsInFrustum(right) || frustum.boundsInFrustum(top) || frustum.boundsInFrustum(bottom))
{
return true;
}
return false;
}
public void attemptZoom(float newZoom)
{
this.zoom = newZoom;
this.snapCameraInView();
}
private void snapCameraInView()
{
float halfOfCurrentViewportWidth = ((viewportWidth * zoom) / 2f);
float halfOfCurrentViewportHeight = ((viewportHeight * zoom) / 2f);
//Check the vertical camera.
if(position.x - halfOfCurrentViewportWidth < 0f) //Is going off the left side.
{
//Snap back.
float amountGoneOver = position.x - halfOfCurrentViewportWidth;
position.x += Math.abs(amountGoneOver);
}
else if(position.x + halfOfCurrentViewportWidth > viewportWidth)
{
//Snap back.
float amountGoneOver = (viewportWidth - (position.x + halfOfCurrentViewportWidth));
position.x -= Math.abs(amountGoneOver);
}
//Check the horizontal camera.
if(position.y + halfOfCurrentViewportHeight > viewportHeight)
{
float amountGoneOver = (position.y + halfOfCurrentViewportHeight) - viewportHeight;
position.y -= Math.abs(amountGoneOver);
}
else if(position.y - halfOfCurrentViewportHeight < 0f)
{
float amountGoneOver = (position.y - halfOfCurrentViewportHeight);
position.y += Math.abs(amountGoneOver);
}
}
}
The CustomCamera class given doesn't work very well. I used it to map a pinch gesture to zoomSafe and the camera would bounce/flash from left to right constantly when on the edge of the bounds. The camera also doesnt work properly with panning. If you try to pan along the edge of the bounds it doesnt pan anywhere as if the edges are "sticky". This is because it just translates back to the last position instead of just adjusting the coordinate that it outside the bounds.
I have seen how to draw a shape in Android, but what I want to know is how to rescale the shape when the user touches over the shape.
Imagine a square into a screen corner, so when you touch it, it grows until fitting the whole screen. I'd like to make that with a transition, animated, not instant.
Any idea of how to do that, or any known resource?
Android has built-in support for Animations. You can find many examples by searching the Web. This one is a good start.
In order to make your shapes touchable, you can implement them by overriding the View class (a nice example can be found here). Then you can use View.OnTouchListener.
The built in Animations are nice in Android but they aren't the most efficient by any means. When performance is a must I would recommend creating your own method. What I would do is create a class that extends View and give it a bounding box (Rect/RectF) and a circle. Then you can use the bounding box to detect when the circle is touched.
public class Circle extends View {
public static final float SCALE_AMOUNT = 1.0f;
public RectF boundingBox;
private Paint paint;
private float circleCenterX, circleCenterY, circleRadius;
private float x, y;
public Circle(Context context) {
super(context);
// Create paint
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
// Set circle start radius
circleRadius = 50.0f;
// Set start x and y (this is the upper left hand corner)
x = 100.0f;
y = 100.0f;
// Create boundingBox
boundingBox = new RectF();
boundingBox.left = x;
boundingBox.top = y;
boundingBox.right = x + (circleRadius*2);
boundingBox.bottom = y + (circleRadius*2);
// Set circleCenterX and circleCenterY (the center of the bounding box and circle)
circleCenterX = x + circleRadius;
circleCenterY = y + circleRadius;
}
public void scale(boolean scaleUp) {
float scaleBy = (scaleUp) ? SCALE_AMOUNT : -SCALE_AMOUNT;
// Update circleRadius
circleRadius += scaleBy;
// Update the bounding box
boundingBox.left = x;
boundingBox.top = y;
boundingBox.right = x + (circleRadius*2);
boundingBox.bottom = y + (circleRadius*2);
// Update the circle center positions
circleCenterX = x + circleRadius;
circleCenterY = y + circleRadius;
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, paint);
}
}
... Then in your Activity class override the onTouchEvent() method and check if your Circle is touched.
Circle circle = new Circle(this);
#Override
public void onDraw(Canvas canvas) {
circle.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
float x = event.getX();
float y = event.getY();
// Detect if pointer goes down on screen
if(action == MotionEvent.ACTION_DOWN) {
if(circle.boundingBox.contains(x, y) == true) {
// Circle was touched so scale it
circle.scale(true); // true is scale up, false is scale down
}
}
return true;
}
... This will scale your circle/rectangle every time you touch it. If you wanted to make it continually grow you could have a boolean variable that gets set to true when you touch the shape and grows until you pick your finger up. I haven't tried this code, just typed it up real quick so it may not compile but this is going to be you're best bet. It is really easy to add many shapes and detect touches on all of the shapes. Add different effects to each one... etc. I didn't want to do all of it for you but this should point you in the right direction.
Maybe this github project could help you: https://github.com/markushi/android-circlebutton