Android Wear Picture ticker - android

I am using the sample second, hour, and minute ticker/clock hands for a sample watch face on Android Studio. There, draw line method of Canvas class is being used with a startX, startY, stopX, and stopY. My question is how do I implement this similarly using a picture? I thought of using drawBitmap which only has a left and top and so it rotates but way too widely and not in place as the hands do.
Is there any way to use an image instead of a line like in the drawLine method? The bottom line is that I need to use an image instead of a line as a clock hand.
Thanks!

You should use Canvas.save, Canvas.rotate and Canvas.restore. Imagine, that you always draw things in the same place, but the underlying canvas is being rotated, so you will draw in the right place. Consider the example from the Santa Tracker:
for (int i = 0; i < mCloudBitmaps.length; i++) {
canvas.save();
canvas.rotate(mCloudDegrees[i], centerX, centerY);
float r = centerX - (timeElapsed / (mCloudSpeeds[i])) % centerX;
mCloudFilterPaints[i].setAlpha((int) (r / centerX * 255));
canvas.drawBitmap(mCloudBitmaps[i], centerX, centerY - r,
mCloudFilterPaints[i]);
canvas.restore();
}
It doesn't draw hands, but instead the clouds on the watch face, but it should give you an example of what you need to try to do.

Related

how to find the biggest possible Y point given X in Region in Android?

I have a virtual ruler being drawn on the screen. I want to be able to draw a straight line like the blue line above when touch event happen within the grown rectangle. but because touch can't be 100% straight, the movement might be like the red line. that's why I set a rectangle to listen to all the nearby touch events then draw a blue line.
I currently have
mRulerRect.set(mRulerCenter.x - mRulerBitmap.getWidth() / 2,
mRulerCenter.y - mRulerBitmap.getHeight()),
mRulerCenter.x + mRulerBitmap.getWidth() / 2,
mRulerCenter.y);
mPath.addRect(mRulerRect, Path.Direction.CCW);
mRulerMatrix.setRotate(mRulerAngle, mRulerCenter.x, mRulerCenter.y);
mPath.transform(mRulerMatrix);
mRegions.setPath(mPath, new Region(mRulerRect));
then I check if the touch even happen within brown rectangle by mRegions.contains(x,y). works perfect so far for touch detection, but the problem I have now is how to draw a straight line. I tried to fix X point then calculate Y. it works fine when ruler is horizontal then starts to behave very weird when turning from horizontal to vertical. I'm out of idea how to accomplish this. Please help! thank you.
Things that you know:
The center of that brown rect is mRulerCenter.x, mRulerCenter.y
The line that you want to draw pass through that point
The angle of the line is mRulerAngle
We're missing just one element, which is, the length of the line we want to draw. That's probably going to be a portion of the ruler's width and it should be very easy to compute mRulerRect.width() * someFactor
Now, we want to know what are the start and the end of the line, we can compute that with trigonometry functions
float halfLineLength = mRulerRect.width() * someFactor;
float startAngle = (float) Math.toRadians(mRulerAngle);
float endAngle = (float) Math.toRadians(mRulerAngle + 180);
float startX = mRulerCenter.x + (float) Math.cos(startAngle) * halfLineLength;
float startY = mRulerCenter.y + (float) Math.sin(startAngle) * halfLineLength;
float endX = mRulerCenter.x + (float) Math.cos(endAngle) * halfLineLength;
float endY = mRulerCenter.y + (float) Math.sin(endAngle) * halfLineLength;
and then draw your line from (startX, startY) to (endX, endY), actually doesn't really matter which is start and which is end

Problems Finding Center of Android Wear Watchface

I have looked all over, but nothing has worked. Every time I try finding the center of the screen for my watch face, it always end up slightly higher than the center, and to the far right of the screen (This is different than the problem that the original template from Android Studio has, where the Watch face is slightly up and all the way to the far left of the screen). What could I be doing wrong?
(First screenshot on Left: My problem. Second screenshot: Original form of the Android Wear template from Android Studio)
#Override
public void onDraw(Canvas canvas, Rect bounds) {
if (isInAmbientMode()) {canvas.drawColor(Color.BLACK);
}
else {
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
}
// Draw H:MM in ambient mode or H:MM:SS in interactive mode.
long now = System.currentTimeMillis();
mCalendar.setTimeInMillis(now);
int width = bounds.width();
int height = bounds.height();
float centerX = width / 2f;
float centerY = height / 2f;
String text = mAmbient
? String.format("%d:%02d", mCalendar.get(Calendar.HOUR),
mCalendar.get(Calendar.MINUTE))
: String.format("%d:%02d", mCalendar.get(Calendar.HOUR),
mCalendar.get(Calendar.MINUTE));
canvas.drawText(text, centerX, centerY, mTextPaint);
}
Full code can be found here
As far as i can tell from a quick look on your sources you already calculate mTextXOffset and mTextYOffset but don't use it for your draw call, hence the texts origin is centered in your problematic screenshot instead of the texts center.
adjusting your centerX and centerY by mTextXOffset and mTextYOffset should do the trick.
If you're drawing text using a center point you can use
mTextPaint.setTextAlign(Paint.Align.CENTER);
which will draw from the center. Otherwise the x coordinte represents the left hand side of the text you are drawing.

Android rotating one bitmap around another

Im making a 2d game where my character is controlled by a joystick and I rotate him using canvas.rotate(angel, x, y). My character shoots in the same direction he is heading, now I want to add fire coming out of his rifle when he shoots. The problem is, how do I rotate this fire-image around my character?
How I rotate My character:
canvas.save();
canvas.rotate((float) controls.getAngle(), controls.charPosition.x + charBitmap.getWidth() / 2, controls.charPosition.y + charBitmap.getHeight() / 2);
character.draw(canvas, controls.charPosition.x,controls.charPosition.y);
canvas.restore();
How I rotate the Fire:
canvas.save();
canvas.rotate((float) controls.getAngle(), controls.charPosition.x + (fireBitmap.getWidth()) / 2, controls.charPosition.y + (fireBitmap.getHeight() / 2));
fire.draw(canvas, controls.charPosition.x, controls.charPosition.y);
canvas.restore();
As you probably know, drawing my fire like this will make it rotate around its own center, just like with my character. But I want it to rotate around my character so it looks like the fire is coming out of his gun.
Any ideas? If anything is unclear please let me know in a comment. Thanks!
I will try to tailor this answer so that it's more specific, but if I understand you correctly, you may not want to rotate the canvas to create the rotation of the fire. Instead you may want to describe it with a trig function relative to the character's location. So I think it will look something like your drawing of the character.
canvas.save();
canvas.rotate((float) controls.getAngle(), controls.charPosition.x + charBitmap.getWidth() / 2, controls.charPosition.y + charBitmap.getHeight() / 2);
fire.draw(canvas, controls.charPosition.x+(fireRadius*cos(fireAngle)),controls.charPosition.y+(fireRadius*sin(fireAngle));
canvas.restore();
Where in this code, fireRadius is how far from the center of the character drawing you want the fire drawing to be and fireAngle is the angle of the fire from the center of the character. This won't be quite right because it will track the corner of the fire image, not the center. I think that can be fixed somewhat easily. If not, I will change soon.
You could use Canvas.translate(dx, dy). Idea is to move the fire a certain distance away from the gun, which can be directly along x -axis, and then rotate Canvas around gun center. This results fire to rotate given angle at given distance from the gun.
canvas.save();
canvas.rotate((float) controls.getAngle(), controls.charPosition.x + (fireBitmap.getWidth()) / 2, controls.charPosition.y + (fireBitmap.getHeight() / 2));
canvas.translate(distanceFromGun, 0);
fire.draw(canvas, controls.charPosition.x, controls.charPosition.y);
canvas.restore();
Please do note the order. Canvas matrix operations are pre multiplying and though translation is made later in the code.

centered zoom in custom view - calculating canvas coordinates

I am working on my first "real" Android application, a graphical workflow editor. The drawing is done in a custom class, that is a subclass of View.At the moment my elements are rectangles, which are drawn on a canvas. To detect actions on elements I compare the coordinates and check for elements on the touch location.
To implement a zoom gesture I tried http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
With the 4 argument canvas.scale(...) function the centered zooming works well, but I lose the ability to calculate the canvas coordinates using the offset with mPosX and mPosY to detect if the touch after a zoom is on an element.
I tried to change the example in the blogpost above to center the canvas on the zoom gesture with:
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor, mScalePivotX, mScalePivotY);
//drawing ....
canvas.restore();
I did not find any examples on how this could be done without losing the reference offset to calculate the coordinates. Is there an easy workaround? I tried to calculate the offset with the gesture center and the scaling factor, but failed :/
I have seen that other examples which use an ImageView often use a Matrix to transform the image. Could this be done with a custom View and a Canvas? If yes, how can I get the x and y offset to check the coordinates?
Also, if my ideas are completely wrong, I would be very happy to see some examples on how this is done properly.
Thx! ;)
Perhaps the following code will help you to calculate coordinates with the gesture center and the scaling factor. I use this method in my class representing opengl-sprite.
void zoom(float scale, PointF midPoint) {
if (zoomFactor == MAX_ZOOM_FACTOR && scale > 1) return;
if (zoomFactor == MIN_ZOOM_FACTOR && scale < 1) return;
zoomFactor *= scale;
x = (x - midPoint.x) * scale + midPoint.x;
y = (y - height + midPoint.y) * scale + height - midPoint.y;
if (zoomFactor >= MAX_ZOOM_FACTOR) {
zoomFactor = MAX_ZOOM_FACTOR;
} else if (zoomFactor < MIN_ZOOM_FACTOR) {
zoomFactor = MIN_ZOOM_FACTOR;
x = 0;
y = 0;
}
}
X and Y coordinates are processed in different ways, because of distinction between directions of opengl coordinate system (right and up) and midPoint's coordinate system (right and down). midPoint is taken from MotionEvents coordinates.
All other operations are understandable, i think.
Hope it will help you.

Skewing a bitmap only in the vertical direction

I want to skew (correct me if this is not the correct word) a bitmap so that it appears to have depth. A good way to visualize what I am asking for is how the credits of Star Wars are angled to show depth.
I have tried the following:
canvas.getMatrix().postSkew(kx,ky,px,py);
and
canvas.skew(sx,sy);
But I have not had much success. The above methods seem to always transform the bitmap into a parallelogram. Is there a way to transform the bitmap into a trapezoid instead?
Here is a snippet of code that I took from the examples that Romain pointed me to.
canvas.rotate(-mOrientation[0] + mHeading, mCenterX, mCenterY);
camera.save();
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
camera.rotateX(mOrientation[1]);
camera.applyToCanvas(canvas);
canvas.drawPath(mPath, mPaint);
canvas.drawCircle(mCenterX, mCenterY, mRadius - 37, mPaint);
camera.restore();
I spent a lot of time working on this today (ran into the same problem) and came up with the code below.
Key thing to note, you need to set preTranslate() and postTranslate() to the center (or somewhere else) of your Canvas area. It seems to mean that it uses the center of the image to apply the transformation from, instead of the upper left corner (x=0,y=0) by default. This is why you would get a parallelogram instead of what you would expect, a trapezoid (Thanks for teaching me the names of those).
The other important thing that I picked up is the Save/Restore functions on the Canvas/Camera. Basically, if you call the Rotate functions consecutively three times without restoring the state back each time, you would keep rotating your object around and around each time you draw. That might be what you want, but I certainly didn't in my case. Same applies to the canvas as you are basically applying the Matrix from the Camera object to the Canvas and it needs to be reset otherwise the same thing occurs.
Hope this helps someone, this is not well documented for beginners. Tip to anyone reading this, check out the APIDemos folder in the SDK Samples. There is a Rotate3dAnimation.java file which demonstrates this as well.
//Snippet from a function used to handle a draw
mCanvas.save(); //save a 'clean' matrix that doesn't have any camera rotation in it's matrix
ApplyMatrix(); //apply rotated matrix to canvas
Draw(); //Does drawing
mCanvas.restore(); //restore clean matrix
//
public void ApplyMatrix() {
mCamera.save();
mCamera.rotateX(-66);
mCamera.rotateY(0);
mCamera.rotateZ(0);
mCamera.getMatrix(mMatrix);
int CenterX = mWidth / 2;
int CenterY = mHeight / 2;
mMatrix.preTranslate(-CenterX, -CenterY); //This is the key to getting the correct viewing perspective
mMatrix.postTranslate(CenterX, CenterY);
mCanvas.concat(mMatrix);
mCamera.restore();
}
You cannot achieve the effect you want with skew(). However, you can use a Camera object and 3D rotations to achieve this effect. The Camera will generate a Matrix for you that you can then apply on the Canvas. Note that the result will not be perspective correct, but good enough for your purpose. This how 3D rotations are done in Honeycomb's Launcher for instance (and many other apps.)
I don't think the "Star Wars effect" is an affine transformation, which I think are the only operations supported by Matrix.

Categories

Resources