Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a triangle image whose one edge is always in the same direction as the circle.
This image has to be moved around the circle based on user swipe/drag. So, it has to both rotate (so that it's edge is in same direction as the circle) and at the same time revolve around the circle.
How to implement this feature?
UPDATE: My custom View is as follows:
public class ThermoView extends FrameLayout{
private ImageView mThermoBgrd;
private ImageView mCurTempArrow;
public static final int THEMROSTAT_BACKGROUND = 0;
public static final int THEMROSTAT_CURR_TEMP = 1;
public ThermostatView(Context context, AttributeSet attrs) {
super(context, attrs);
mThermoBgrd = new ImageView(context);
mThermoBgrd.setImageResource(R.drawable.circle_icon);
addView(mThermoBgrd, ThermostatView.THEMROSTAT_BACKGROUND);
mCurTempArrow = new ImageView(context);
mCurTempArrow.setImageResource(R.drawable.ruler_triangle_icon);
mCurTempArrow.setScaleType(ImageView.ScaleType.MATRIX);
addView(mCurTempArrow, ThermostatView.THEMROSTAT_CURR_TEMP, new LayoutParams(50, 50));
}
#Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
int currTempHeight = mCurTempArrow.getMeasuredHeight();
int currTempWidth = mCurTempArrow.getMeasuredWidth();
int parentWidth = right - left;
int parentHeight = bottom - top;
int padding = currTempHeight;
//We need square container for the circle.
int containerLeft = padding;
int containerTop = parentHeight - parentWidth + padding;
int containerRight = parentWidth - padding;
int containerBottom = parentHeight - padding;
int containerWidth = containerRight - containerLeft;
int containerHeight = containerBottom - containerTop;
//place the arrow indicating current temperature
int curTempLeft = containerRight - ((containerWidth/2) + currTempWidth/2);
int curTempTop = containerTop - (currTempHeight/2);
int curTempRight = curTempLeft + currTempWidth;
int curTempBottom = curTempTop + currTempHeight;
mCurTempArrow.layout(curTempLeft, curTempTop, curTempRight, curTempBottom);
}
try this (it uses Paths instead of Bitmaps but the idea is the same):
public class MyView extends View {
private Paint mPaint;
private Path mTriangle;
private Path mCircle;
private Matrix mMatrix;
private float mAngle;
public MyView(Context context) {
super(context);
mPaint = new Paint();
mPaint.setStrokeWidth(10);
mPaint.setStyle(Style.STROKE);
mTriangle = new Path();
mTriangle.moveTo(0, -21);
mTriangle.lineTo(0, 21);
mTriangle.lineTo(36, 0);
mTriangle.close();
mCircle = new Path();
mCircle.addCircle(0, 0, 50, Direction.CW);
mMatrix = new Matrix();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float w2 = getWidth() / 2f;
float h2 = getHeight() / 2f;
mAngle = (float) (180 * Math.atan2(event.getY() - h2, event.getX() - w2) / Math.PI);
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
float w2 = getWidth() / 2f;
float h2 = getHeight() / 2f;
mMatrix.reset();
mMatrix.postTranslate(w2, h2);
canvas.concat(mMatrix);
mPaint.setColor(0xaaff0000);
canvas.drawPath(mCircle, mPaint);
mMatrix.reset();
mMatrix.postTranslate(60, 0);
mMatrix.postRotate(mAngle);
canvas.concat(mMatrix);
mPaint.setColor(0xaa00ff00);
canvas.drawPath(mTriangle, mPaint);
}
}
Since I don't know if you are using open GL or a standard canevas, I can't really give you some working code. But the general idea is (assuming you have the current position of the triangle (x, y) stored, and the center of your circle (cx, cy) stored.
Do the following:
v = (cx-x, cy-y) // v is the normal vector of your triangle: it faces the center of the circle
triangle.translate(v) // translate to the center of the circle
triangle.rotate(angle) // rotate the triangle on itself
v.rotate(angle) // apply the same rotation on the normal vector
triangle.translate(-v) // translate back on the circle, but since v is rotated, the position will be updated
I hope it is clear enough, good luck
EDIT:
First, you should really try to be more accurate: in your first post, you didn't say that the triangle was an image (that's important). Then you don't say what is your current rendering, what works, what doesn't. I would be easier to help you to know what your program currently display.
From your code, I assume you place the triangle image properly , but it is not rotated. So first, try to add
//place the arrow indicating current temperature
int curTempLeft = containerRight - ((containerWidth/2) + currTempWidth/2);
int curTempTop = containerTop - (currTempHeight/2);
int curTempRight = curTempLeft + currTempWidth;
int curTempBottom = curTempTop + currTempHeight;
mCurTempArrow.setRotate(angle); // rotate the image. angle is in degrees
mCurTempArrow.layout(curTempLeft, curTempTop, curTempRight, curTempBottom);
If you don't know the angle, you might have to compute it from the previous position of the triangle
Related
Requirement is to keep a ball moving on the Grid path generated in Canvas. I have generated a Grid in canvas but not able to understand how to move the ball randomly means starting point show be different on the path. I am sharing what I have done. I have also plotted the ball in the screen but not getting the point how to put the ball exactly on the grid line randomly and start moving it
public class PixelGridView extends View {
//number of row and column
int horizontalGridCount = 11;
private Drawable horiz;
private Drawable vert;
private final float width;
long mInterpolateTime;
PointF mImageSource = new PointF();
public PixelGridView(#NonNull Context context) {
this(context, null);
}
public PixelGridView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
horiz = new ColorDrawable(Color.WHITE);
horiz.setAlpha(160);
vert = new ColorDrawable(Color.WHITE);
vert.setAlpha(160);
width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, .9f, context.getResources().getDisplayMetrics());
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
horiz.setBounds(left, 0, right, (int) width);
vert.setBounds(0, top, (int) width, bottom);
}
private float getLinePosition(int lineNumber) {
int lineCount = horizontalGridCount;
return (1f / (lineCount + 1)) * (lineNumber + 1f);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//drawTask.start();
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
canvas.drawCircle(120, 110, 10, paint);
int count = horizontalGridCount;
for (int n = 0; n < count; n++) {
float pos = getLinePosition(n);
// Draw horizontal line
canvas.translate(0, pos * getHeight());
Log.e("Position1", "" + pos * getHeight());
horiz.draw(canvas);
canvas.translate(0, -pos * getHeight());
// Draw vertical line
canvas.translate(pos * getHeight(), 0);
Log.e("Position2", "" + pos * getHeight());
vert.draw(canvas);
canvas.translate(-pos * getHeight(), 0);
}
}
}[![Canvas Image][1]][1]
//MainActivity
public class PathAnimationActivity extends AppCompatActivity {
LinearLayout rlLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_path);
rlLayout=findViewById(R.id.rlLayout);
PixelGridView pixelGrid = new PixelGridView(this);
rlLayout.addView(pixelGrid);
}
}
First thing i have noticed it that you haven't used invalidate (); at the end because thats critical in order to animate the canvas ( redraw the frames ) so please include that .
there may be several ways to achieve what you want follows is my idea
this canvas need to be divided into multiple x , y divided planes as follows and save them in array of points which you can randomize and give those points to ball to move ,
Step 1, get the canvas size
step 2, divide is in x and y coordinate depending on size of each device varies so you need to control that factor via Screen size
step 3, save the coordinates in matrix or array
step 4, set position of balls from those arrays values ( randomly can you define the random limits as per the max and min values of x and y from the coordinates division .
example , function move will take ball object and x, y are the positions move (ball, x, y ); and you can randomize the x and y based on max and min limits of your coordinates division example total y lines and total x lines values
in order to get an idea about how to move the ball on canvas you can see this code here : https://github.com/pintspin/ball_animation
Based on my previous question of "How to create a BottomBar as StickyBottomCaptureLayout in camera2 Android api?", I created a layout with a StickyBar (SB) which is always locked above/near the system bar. I set the default positions and coordinates of the SB and the other layout in onLayout() (exactly as my answer).
The upper layout is a simple custom DrawView which has an ArrayList of Paths drew by the user. When the device rotates, it recalls onDraw() and calls several times canvas.drawPath(). However, the Paths are redrew with the same coordinates as before but on a different position and layout size. These screenshots demonstrate the actual behavior:
left: portrait - right: landscape
But I want to keep the same coordinates and positions when the orientation changed, like this:
left: same portrait as above - right: landscape with "portrait" coordinates
Locking my activity with android:orientation="portrait" is not the expected solution. I use android:configChanges="orientation" and an OrientationListener to detect the rotation and prevent the total recreation of the Activity.
I tried to set other different positions in onLayout() but obviously, this is not the right way.
I previously tried to transform the multiple Paths like this:
for (Path path : mPathList) {
Matrix matrix = new Matrix();
RectF bounds = new RectF();
path.computeBounds(bounds, true);
// center points to rotate
final float px = bounds.centerX();
final float py = bounds.centerY();
// distance points to move
final float dx; // ?
final float dy; // ?
/** I tried many calculations without success, it's
not worth to paste these dumb calculations here... **/
matrix.postRotate(rotation, px, py); // rotation is 90°, -90° or 0
matrix.postTranslate(dx, dy); // ?
path.transform(matrix);
}
I also tried to rotate the canvas as follows:
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.rotate(rotation); // rotation is 90°, -90° or 0
canvas.drawColor(mDrawHelper.getBackgroundViewColor());
for (int i=0; i < mPathList.size(); i++) {
canvas.drawPath(mPathList.get(i), mPaintList.get(i));
}
if (mPath != null && mPaint != null)
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
Anyway, I tried many manipulations but nothing seems to work in this specific case. Does someone have a bright and fabulous idea to share which can lead me in the right direction?
Thanks in advance for the help.
Update: Methodology has been simplified and made easier to follow. The sample app has been updated.
I think I understand what you are trying to do. You want the graphic to maintain its relationship with the StickyCaptureLayout that you have defined. I like the approach using Path and Matrix transformations.
After determining the rotation that the device has undergone, create a Matrix to do the appropriate rotation and rotate about the center of the graphic.
mMatrix.postRotate(rotationDegrees, oldBounds.centerX(), oldBounds.centerY());
Here oldBounds is the bounds of the graphic before location. We will use this to determine margins on the rotated graphic. Go ahead and do the rotation
mPath.transform(mMatrix)
The graphic has been rotated but its position is not correct. It is in the old position but rotated. Create a translation Matrix to move the Path to the appropriate location. The actual computation is dependent upon the rotation. For a 90 degree rotation the computation is
transY = -newBounds.bottom; // move bottom of graphic to top of View
transY += getHeight(); // move to bottom of rotated view
transY -= (getHeight() - oldBounds.right); // finally move up by old right margin
transX = -newBounds.left; // Pull graphic to left of container
transX += getWidth() - oldBounds.bottom; // and pull right for margin
where transY is the Y-translation and transX is the X-translation. oldBounds is the pre-rotation bounds and newBounds is the post-rotation bounds. Important to note here is that getWidth() will give you the "old" View height and getHeight() will give you the old View width.
Here is a sample program that accomplishes what I have described above. A couple of graphics follow showing a 90 degree rotation using this sample app.
Demo app
package com.example.rotatetranslatedemo;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
public class MainActivity extends Activity {
private DrawingView dv;
private Paint mPaint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new DrawingView(this);
setContentView(dv);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
public class DrawingView extends View {
private Bitmap mBitmap;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint paint;
Matrix mMatrix = new Matrix();
RectF oldBounds = new RectF();
RectF newBounds = new RectF();
public DrawingView(Context c) {
super(c);
context = c;
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.MITER);
paint.setStrokeWidth(4f);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
#Override
protected void onDraw(Canvas canvas) {
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
int rotationDegrees = 0;
float transX = 0;
float transY = 0;
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
// Determine the rotation of the screen.
switch (display.getRotation()) {
case Surface.ROTATION_0:
break;
case Surface.ROTATION_90:
rotationDegrees = 270;
break;
case Surface.ROTATION_180:
rotationDegrees = 180;
break;
case Surface.ROTATION_270:
rotationDegrees = 90;
break;
default:
rotationDegrees = 0;
break;
}
if (mPath == null) { // Just define what we are drawing/moving
mPath = setupGraphic();
}
// Reposition the graphic taking into account the current rotation.
if (rotationDegrees != 0) {
mMatrix.reset();
// Rotate the graphic by its center and in place.
mPath.computeBounds(oldBounds, true);
mMatrix.postRotate(rotationDegrees, oldBounds.centerX(), oldBounds.centerY());
mPath.transform(mMatrix);
// Get the bounds of the rotated graphic
mPath.computeBounds(newBounds, true);
mMatrix.reset();
if (rotationDegrees == 90) {
transY = -newBounds.bottom; // move bottom of graphic to top of View
transY += getHeight(); // move to bottom of rotated view
transY -= (getHeight() - oldBounds.right); // finally move up by old right margin
transX = -newBounds.left; // Pull graphic to left of container
transX += getWidth() - oldBounds.bottom; // and pull right for margin
} else if (rotationDegrees == 270) {
transY = -newBounds.top; // pull top of graphic to the top of View
transY += getHeight() - oldBounds.right; // move down for old right margin
transX = getWidth() - newBounds.right; // Pull to right side of View
transX -= getHeight() - oldBounds.right; // Reestablish right margin
}
mMatrix.postTranslate(transX, transY);
mPath.transform(mMatrix);
}
canvas.drawPath(mPath, mPaint);
}
// Define the graphix that we will draw and move.
private Path setupGraphic() {
int startX;
int startY;
final int border = 20;
Path path;
if (getHeight() > getWidth()) {
startX = getWidth() - border - 1;
startY = getHeight() - border - 1;
} else {
startX = getHeight() - border - 1;
startY = getWidth() - border - 1;
}
startX = startX - 200;
Pt[] myLines = {
new Pt(startX, startY),
new Pt(startX, startY - 500),
new Pt(startX, startY),
new Pt(startX - 100, startY),
new Pt(startX, startY - 500),
new Pt(startX - 50, startY - 400),
new Pt(startX, startY - 500),
new Pt(startX + 50, startY - 400),
new Pt(startX + 200, startY),
new Pt(startX + 200, startY - 500)
};
// Create the final Path
path = new Path();
for (int i = 0; i < myLines.length; i = i + 2) {
path.moveTo(myLines[i].x, myLines[i].y);
path.lineTo(myLines[i + 1].x, myLines[i + 1].y);
}
return path;
}
private static final String TAG = "DrawingView";
}
// Class to hold ordered pair
private class Pt {
float x, y;
Pt(float _x, float _y) {
x = _x;
y = _y;
}
}
}
Portrait
Landscape
Your solution #2 is almost correct. All you need to do is translate your canvas appropriately.
Assuming that rotation is declared as int and may be only 90, -90 or 0, you need to replace this line:
canvas.rotate(rotation); // rotation is 90°, -90° or 0
by the following code:
if (rotation == 90) {
canvas.translate(canvas.getWidth(), 0);
canvas.rotate(90);
} else if (rotation == -90) {
canvas.translate(0, canvas.getHeight());
canvas.rotate(-90);
}
This will work. I can set up a demo project if needed.
Instead of implementing a solution that is very specific to your problem, you can just implement a more generic one. For example a layout that will rotate everything what is inside, which in my opinion is much more elegant.
public class RotatedLayout extends FrameLayout {
public RotatedLayout(Context context) {
super(context);
}
...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int rotation = 0;
boolean swapDimensions = false;
int translationX = 0;
int translationY = 0;
final Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
switch (display.getRotation()) {
case Surface.ROTATION_0:
rotation = 0;
break;
case Surface.ROTATION_90:
rotation = -90;
swapDimensions = true;
break;
case Surface.ROTATION_180:
rotation = 180;
break;
case Surface.ROTATION_270:
rotation = 90;
swapDimensions = true;
break;
}
if (swapDimensions) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
translationX = (width - height) / 2;
translationY = (height - width) / 2;
final int tmpMeasureSpec = heightMeasureSpec;
heightMeasureSpec = widthMeasureSpec;
widthMeasureSpec = tmpMeasureSpec;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setTranslationX(translationX);
setTranslationY(translationY);
setRotation(rotation);
}
}
This layout is rather straightforward. It forces itself to be measured with swapped dimensions if displayed in landscape mode. It doesn't care what is inside, so you can put everything there, also a regular interface. After measuring itself (and children) with swapped MeasureSpecs it rotates itself and translates, using view properties, to fit the new position. As a bonus of using view properties - touch events works just fine and this button can be pressed as usual.
In portrait orientation:
Rotated to the left:
Problem with onConfigurationChanged
Although this layout will always draw itself in correct orientation, there must be some event that will cause it to be re-drawn. This may be a problem if you rely only on onConfigurationChanged. In your case Activity can react on changes from landscape to portrait and portrait to landscape. But there is no event sent when switching directly from:
portrait orientation to reverted portrait (if you have the reversed portrait enabled in your AndroidManifest) - marked in blue.
landscape orientation to reversed landscape - marked in red
Please keep in mind that such direct orientation swapping to the reversed orientation is a normal interaction on regular device, it is not something artificial that you can do on emulator only.
There is no standard event sent that will cause views to redraw themselves - no onConfigurationChanged, onMeasure, onLayout, onDraw etc. is invoked.
System just rotates everything for you (without even redrawing it) and it will result in wrong rotation of the view RotatedLayout had no changes to correct it. So be aware that you have to handle this case.
You can see it here in an answered by Dianne Hackborn.
This is simply not a configuration change. There is no notification the
platform provides for when it does this, because it is invisible to the
environment the app is in.
To solve this problem you would have to use SensorManager and register OrientationEventListener to determine when to refresh your view instead of relying on onConfigurationChanged method.
I'd like to be able to create an arbitrary input for time into a standard Android animation. Instead of an animation running for 1 second, I want for instance the input to be a coordinate from user touch input. This way I could for instance create a circle motion of object A when the position in the circular motion is defined by a linear input on slide A.
Crude illustration:
Now I'm thinking this could be achieved with defining the translation animation in XML just as with regular animations under /res/anim, but overriding the time input to come from a user input control instead. It minght also be done with a custom interpolator, I'm not sure. I don't what a set start and end time of the animation, in any case.
Anyone have any suggestions on how to achieve this?
edit: To further answer a couple of the comments: Think if it as the user slides/drags the blue dot. No interpolation between the input occurs. As soon as the user lifts the finger, the "animation" stops.
If I understand correctly you need some sort of 'rigging' - Defining a movement of one element as a function of another. In your case this function needs to transform the the linear position into a circular position.
There is no animation involved - When the user moves the blue circle, the red one is moved accordingly.
You should register for callbacks for the blue circle movement (i.e. onTouchEvent, or a seekBar's on change, depending on how you implement your 'bar'). Then you calculate the new position of the red circle and then you put it there.
Here's a simple working example of a custom view that draws two circles according to a given percentValue. I tested using s simple SeekBar and it works:
public class CanvasView extends View {
private int centerX = 0;
private int centerY = 0;
private int radius = 0;
private final int handleRadius = 25;
private final Paint circlePaint = new Paint();
private final Paint handlePaint = new Paint();
private float percentValue = 0f;
public CanvasView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CanvasView(Context context) {
super(context);
init();
}
private void init() {
circlePaint.setColor(Color.BLACK);
handlePaint.setColor(Color.RED);
}
// Call this whenever the value of that linear bar is changed - so when the user moves his finger etc.
public void setValue(float percentage) {
this.percentValue = percentage;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// this is the main circle
canvas.drawCircle(centerX, centerY, radius, circlePaint);
// calculate the angle based on the percentage of the linear movement (substracting (pi/2) so the zero value is on top)
double angle = (percentValue / 100) * (2 * Math.PI) - Math.PI / 2;
// sin and cos to calculate the position of the smaller circle - the 'handle'
float handleX = centerX + (float) (radius * Math.cos(angle));
float handleY = centerY + (float) (radius * Math.sin(angle));
// drawing the circle
canvas.drawCircle(handleX, handleY, handleRadius, handlePaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// choose whatever values you want here, based on the view's size:
centerX = w / 2;
centerY = h / 2;
radius = w / 3;
}
}
What's up guys, I need a little help with this one. I'm trying to achieve a simple(but not really) folding animation on a listview that is being scrolled. Basically, I'm attempting to fold the listview's first visible child backward as if a sheet of paper is being folded downward along the X axis. This goes on on continuously as the user scrolls up and down the list. This is my first time playing around with Matrix animations and Android's camera from the graphics api, so I'm definitely off the mark here.
This is the effect I'm trying to achieve
And this is the effect I'm getting.
I want the animation to begin at the origin(0,0) but both the left and right side, animating from the top of the list item instead of the upper left corner. I'm not very familiar with matrix translations or animations so If anyone much more experience with these techniques than myself can shed some knowledge, it'll be greatly appreciated.
Basically I'm overriding the onDrawChild method of ListView, grabbing the child's bitmap from a drawing cache, and using a matrix to perform the animation. The lighting and camera implementation is code that I took from another sample app in order to get the animation to look as 3D as possible.
I tried playing around with the ListView animations library, but without much luck. I also tried to hack together a solution using code from the developer guides here that uses object animators to achieve a nice little card flip animation, but it started feeling a bit hacky and I couldn't quite get it the way I wanted.
Here's my current implementation. If anyone can shed some light or direction on this one, or maybe if anyone wrote an awesome library that I didn't come across on my searches, please feel free to share. Thanks
#Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
View first = getChildAt(0);
if (child == first) {
if (child.getTop() < 0) {
Bitmap bitmap = getChildDrawingCache(child);
final int top = child.getTop();
child.getRight();
child.getBottom();
child.getLeft();
final int childCenterY = child.getHeight() / 2;
// final int childCenterX = child.getWidth() / 2;
final int parentCenterY = getHeight() / 2; // center point of
// child relative to list
final int absChildCenterY = child.getTop() + childCenterY;
// final int bottom = child.getBottom();
// distance of child center to the list center final int
int distanceY = parentCenterY - absChildCenterY;
final int r = getHeight() / 2;
if (mAnimate) {
prepareMatrix(mMatrix, distanceY, r);
mMatrix.preTranslate(0, top);
mMatrix.postTranslate(0, -top);
}
canvas.drawBitmap(bitmap, mMatrix, mPaint);
}
else {
super.drawChild(canvas, child, drawingTime);
}
} else {
super.drawChild(canvas, child, drawingTime);
}
return false;
}
private void prepareMatrix(final Matrix outMatrix, int distanceY, int r) { // clip
// the
// distance
final int d = Math.min(r, Math.abs(distanceY)); //
// circle formula
final float translateZ = (float) Math.sqrt((r * r) - (d * d));
double radians = Math.acos((float) d / r);
double degree = 45 - (180 / Math.PI) * radians;
// double degree = -180;
mCamera.save();
mCamera.translate(0, 0, r - translateZ);
mCamera.rotateX((float) degree);
if (distanceY < 0) {
degree = 360 - degree;
}
mCamera.rotateY((float) degree);
mCamera.getMatrix(outMatrix);
mCamera.restore();
// highlight elements in the middle
mPaint.setColorFilter(calculateLight((float) degree));
}
private Bitmap getChildDrawingCache(final View child) {
Bitmap bitmap = child.getDrawingCache();
if (bitmap == null) {
child.setDrawingCacheEnabled(true);
child.buildDrawingCache();
bitmap = child.getDrawingCache();
}
return bitmap;
}
private LightingColorFilter calculateLight(final float rotation) {
final double cosRotation = Math.cos(Math.PI * rotation / 180);
int intensity = AMBIENT_LIGHT + (int) (DIFFUSE_LIGHT * cosRotation);
int highlightIntensity = (int) (SPECULAR_LIGHT * Math.pow(cosRotation,
SHININESS));
if (intensity > MAX_INTENSITY) {
intensity = MAX_INTENSITY;
}
if (highlightIntensity > MAX_INTENSITY) {
highlightIntensity = MAX_INTENSITY;
}
final int light = Color.rgb(intensity, intensity, intensity);
final int highlight = Color.rgb(highlightIntensity, highlightIntensity,
highlightIntensity);
return new LightingColorFilter(light, highlight);
}
JazzyListView
has a lot of stuff that's similar to what you want if not exactly what you want. Take a look at how they're defined under jazzy effect and mix and match. I think reverse fly or maybe flip is close to what you want.
The following is supposed to draw an axis in the middle of the screen. However, nothing appears. I am positive that is has to do with my Paths.
#Override
protected void onDraw(Canvas canvas) {
//Variables declared here temporarily for testing purposes
int canterX = getWidth() /2;
int centerY = getHeight() /2;
int radius = 150;
Path verticalAxis = new Path();
Path horizontalAxis = new Path();
drawAxis();
}
private void drawAxis(Canvas canvas) {
int axisLineThickness = 1;
int verticalEndX;
int verticalEndY;
int horizontalEndX;
int horizontalEndY;
Paint axisPaint = new Paint();
axisPaint.setColor(Color.WHITE);
axisPaint.setStrokeWidth(axisLineThickness);
double theta;
for(int i = 90; i < 360; i += 180) {
theta = toRadians(i);
verticalEndX = centerX + (int) ((cos(theta)) * radius);
verticalEndY = centerY + (int) ((sin(theta)) * radius);
verticalAxis.moveTo(centerX, centerY);
verticalAxis.lineTo(verticalEndX, verticalEndY);
}
canvas.drawPath(verticalAxis, axisColor);
for(int i = 90; i < 360; i += 180) {
theta = toRadians(i);
horizontalEndX = centerX + (int) ((cos(theta)) * radius);
horizontalEndY = centerY + (int) ((sin(theta)) * radius);
horizontalAxis.moveTo(centerX, centerY);
horizontalAxis.lineTo(verticalEndX, verticalEndY);
}
canvas.drawPath(horizontalAxis, axisColor);
}
I know I can make the axis draw if I add the following to the vertical and horizontal for loops respectively:
Vertical For Loop:
canvas.drawLine(centerX, centerY, verticalEndX, verticalEndY, paint);
Horizontal For Loop:
canvas.drawLine(centerX, centerY, horizontalEndX, horizontalEndY, paint);
But I don't want to solve the issue this way, I want to correct what is wrong with my paths. Can anyone tell me why the points aren't adding to my path correctly? The loop should only go through twice which creates a line for each side of the axis. Ie. One loop creates the top of the vertical axis and the second loop creates the bottom part.
How do I get my paths create that full line and then draw it outside of the loop?
Paint's default style appears to be FILL, so maybe just having a line in your path is confusing things. Try setting it to STROKE:
axisPaint.setStyle(Paint.Style.STROKE);
See Paint.Style