I'm writing a simple custom View that should draw the an RSSI signal shape (two overlapping triangles one filled proportionally with the level of the signal). The components works except for the clearing of canvas.
I tought I need to do something like that:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int mWidth = getMeasuredWidth();
final int mHeight = getMeasuredHeight();
final float _fillw = mWidth * (mLevel) / 100f;
final float _fillh = mHeight * (100f - mLevel) / 100f;
//canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawColor(Color.BLACK);
if (mLevel < mWarnLevel)
mFillPaint.setColor(mWarnFillColor);
else if ((mLevel >= mWarnLevel) && (mLevel < mSafeLevel))
mFillPaint.setColor(mFillColor);
else if (mLevel >= mSafeLevel)
mFillPaint.setColor(mSafeFillColor);
mFramePath.moveTo(0, mHeight);
mFramePath.lineTo(mWidth, mHeight);
mFramePath.lineTo(mWidth, 0);
mFramePath.close();
mFillPath.moveTo(0, mHeight);
mFillPath.lineTo(_fillw, mHeight);
mFillPath.lineTo(_fillw, _fillh);
mFillPath.close();
canvas.drawPath(mFramePath, mFramePaint);
canvas.drawPath(mFillPath, mFillPaint);
}
public void setLevel(int level) {
if (this.mLevel != level) {
this.mLevel = level;
invalidate();
}
}
The level is drawn correctly until it grows and the triangle area also get larger. When it start to decrease the area is not updated anymore probably beacuse the background is not cleared.
I tried also to use the commented .drawColor() line without luck.
P.S. I need to have a transparent background on this View.
You just have to clear the path
public void clear()
{
mFramePath.reset();
mFillPath.reset();
invalidate();
}
Related
hi guys as title,
I want to add multiple images(red fan) that are continuously rotated on an existing image (about 10~40),I have implemented it by using View "onDraw", but the system resource consumption is very serious, the CPU usage is 30%~40%.
Is there any better way? Like using a game framework or surfaceview?
Thanks in advance.
onDraw
#Override
protected void onDraw(Canvas canvas) {
int j=1;
final int viewHeight =610;
for (final Fan fan : mFans) {
j = j+1;
final float fanSize = 8;
// // Save the current canvas state
final int save = canvas.save();
if(XArray.size()>0){
y = (int) YArray.get(j);
x = (int) XArray.get(j);
// Move the canvas to the center of the fan
canvas.translate(x, y);
// Rotate the canvas based on how far the fan has moved
final float progress = (fan.y + fanSize) / viewHeight;
canvas.rotate(360 * progress);
// Prepare the size and alpha of the drawable
final int size = (int) fanSize;
mDrawable.setBounds(-size, -size, size, size);
// Draw the fan to the canvas
mDrawable.draw(canvas);
// Restore the canvas to it's previous position and rotation
canvas.restoreToCount(save);
}}
}
onAttachedToWindow()
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mTimeAnimator = new TimeAnimator();
mTimeAnimator.setTimeListener(new TimeAnimator.TimeListener() {
#Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
if (!isLaidOut()) {
// Ignore all calls before the view has been measured and laid out.
return;
}
updateState(deltaTime);
invalidate();
}
});
mTimeAnimator.start();
}
/**
* Pause the animation if it's running
*/
public void pause() {
if (mTimeAnimator != null && mTimeAnimator.isRunning()) {
// Store the current play time for later.
mCurrentPlayTime = mTimeAnimator.getCurrentPlayTime();
mTimeAnimator.pause();
}
}
/**
* Resume the animation if not already running
*/
public void resume() {
if (mTimeAnimator != null && mTimeAnimator.isPaused()) {
mTimeAnimator.start();
// Why set the current play time?
// TimeAnimator uses timestamps internally to determine the delta given
// in the TimeListener. When resumed, the next delta received will the whole
// pause duration, which might cause a huge jank in the animation.
// By setting the current play time, it will pick of where it left off.
mTimeAnimator.setCurrentPlayTime(mCurrentPlayTime);
}
}
/**
* Progress the animation by moving the Fans based on the elapsed time
* #param deltaMs time delta since the last frame, in millis
*/
private void updateState(final float deltaMs) {
// Converting to seconds since PX/S constants are easier to understand
new Thread(new Runnable(){
#Override
public void run() {
final float deltaSeconds = deltaMs / 1000f;
final int viewWidth = getWidth();
final int viewHeight = getHeight();
for (final Fan fan : mFans) {
// Move the Fan based on the elapsed time and it's speed
fan.y -= fan.speed * deltaSeconds;
// If the Fan is completely outside of the view bounds after
// updating it's position, recycle it.
final float size = fan.scale * mBaseSize;
// if (fan.y + size < 0) {
// initializeFan(fan, viewWidth, viewHeight);
// }
} // long run job
}
}).start();
}
I'm trying to transfer my bitmap from using rectF translation to using matrix rotation and translation. I feel like I've tried every combination of pre,set,post for rotate and translate. This is a sample of my code
Matrix m;
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, m, null);
}
onTouchEvent(MotionEvent event)
{
m.setRotate(rotation, bitmap.getWidth()/2, bitmap.getHeight()/2); //rotation points to your finger
if (fingerx1 > bitmapX) {
xspeed = xspeed + accel;
}
if (fingerx1 < bitmapX) {
xspeed = xspeed - accel;
}
if (fingery1 < bitmapY) {
yspeed = yspeed - accel;
}
if (fingery1 > bitmapY) {
yspeed = yspeed + accel;
}
}
private Runnable runny = new Runnable() {
public void run() {
m.postTranslate(xspeed, yspeed);
handler.postDelay(this, 50);
}
};
Not the final code since I don't know which pre,set,post to use in these situations. I feel like this would be the solution though (setRotate, postTranslate).
With this combination, the bitmap looks like it tries to but can't translate.
When I've tried setTranslate(bitmapX + xspeed, bitmapY + yspeed) and postRotate it gives me the right translation but crazy rotation. Using both setRotate, setTranslate gives me crazy glitches. Adding pretranslate and prerotate to the mix just gave me a headache.
TL;DR: Ugh.
EDIT:
bitmapX = playerValues[Matrix.MTRANS_X];
bitmapY = playerValues[Matrix.MTRANS_Y];
After trying postRotate and postTranslate together, I see my bitmap rotate around it's center at first and then it moves out of the 0,0 position and continue to rotate around the 0,0 center.x,center.y position.
Basically I need to fix my rotate's center x,y to make them dynamically change with my bitmap's location.
Thanks pskink, I was having a very bad case of matricides.
What I want to have is the Bitmap Font to change in size accordingly when changing screen sizes. What I mean is on my computer, the font appears rather large, but on my phone, it is a little font that is harder to read. I could change the size, but I want it to look similar on all screens, instead of having it large on one screen and smaller on another. Here is my code to see what I have to work with:
public void render() {
//score system
scoreFont.setColor(1.0f, 1.0f, 1.0f, 1.0f);
scoreBatch.begin();
scoreFont.draw(scoreBatch, Long.toString(getScore()), 10, Gdx.graphics.getHeight() - 10);
scoreFont.setScale(3, 3);
scoreBatch.end();
}
public void resize(int width, int height) {
camera.viewportWidth = 450;
camera.viewportHeight = 250;
camera.update();
stage.setViewport(450, 250, true);
stage.getCamera().translate(-stage.getGutterWidth(), -stage.getGutterHeight(), 0);
}
Think of it this way, you always want to have the same ratio.
For example:
500/3 = 450/x
x is the new size of your text. So you have to do some cross multiplying.
500x = 1350
1350รท500 = x
So now to do this programmatically.
public void resizeText(float width, float currentSize, float currentWidth){
//currentWidth/currentSize = width/x
a = width * currentSize;//450 * 3 in example above
b = a/currentWidth;
return b;//returns the x or the new size that your text should be
}
Also you said that it needs to change depending on of the size is over a certain amount.
So here's a little thing I've devised
ApplicationType appType = Gdx.app.getType();
if (appType == ApplicationType.Android || appType == ApplicationType.iOS) {
screenFont.setScale(your number)
} else { screen font.setScale(otherNum)} //if its a desktop
Scale by Gdx.graphics.getDensity()
I am trying to fill the resize function that comes with Libgdx to fit all of the android screens.
The best solution I have seen so far is this tutorial: http://www.java-gaming.org/index.php?topic=25685.0 but when I run it on my Galaxy S3 the buttons are really tiny and just overall the whole game is tiny where on the desktop version it is big and the way I want it to be. My resize code right now is:
#Override
public void resize(int width, int height) {
float aspectRatio = (float)width/(float)height;
float scale = 2f;
Vector2 crop = new Vector2(0f, 0f);
if(aspectRatio > ASPECT_RATIO)
{
scale = (float)height/(float)VIRTUAL_HEIGHT;
crop.x = (width - VIRTUAL_WIDTH*scale)/2f;
}
else if(aspectRatio < ASPECT_RATIO)
{
scale = (float)width/(float)VIRTUAL_WIDTH;
crop.y = (height - VIRTUAL_HEIGHT*scale)/2f;
}
else
{
scale = (float)width/(float)VIRTUAL_WIDTH;
}
float w = (float)VIRTUAL_WIDTH*scale;
float h = (float)VIRTUAL_HEIGHT*scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
}
My render code is:
#Override
public void render() {
// update camera
camera.update();
// set viewport
Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
(int) viewport.width, (int) viewport.height);
// clear previous frame
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
ScreenManager.updateScreen();
sb.begin();
ScreenManager.renderScreen(sb);
sb.end();
}
And my variables are:
private static final int VIRTUAL_WIDTH = 600;
private static final int VIRTUAL_HEIGHT = 800;
private static final float ASPECT_RATIO = (float)VIRTUAL_WIDTH/(float)VIRTUAL_HEIGHT;
public static BitmapFont FONT;
private OrthographicCamera camera;
private Rectangle viewport;
private SpriteBatch sb;
Like I said, when I run the desktop version it works fine but when I run it on my Galaxy S3 (Screen Dim: 1,280 x 720), the game is zoomed out a lot, and it crops out the tops and sides of the game which is not what I want at all.
Wonder if anyone could point me in the right direction.
I want to be able to animate an object like a bullet from a static position to any area on the screen.
I have no problem with simple horizontal and vertical movements. I.e. x +/- 1 or y +/- 1.
But when it comes to an object like a bullet could move/animate at any degree I'm not quite sure how to do this by making the animation look smooth. For example at 18, 45, or 33 degree angle an algorithm like y+1, x+1, y+1..... Is not going to make the animation very smooth.
Thanks in advance
P.s maybe there is some documentation out there already?
Update
Thanks to everyone who has replied.
This is the code I have so far based on your comments.
package com.bullet;
//imports go here
public class canvas extends SurfaceView implements OnTouchListener, SurfaceHolder.Callback{
private Bitmap bullet;
private int bulletStartX, bulletStartY;
private int bulletX, bulletY;
private int bulletEndX = -1;
private int bulletEndY = -1;
private int incX = 0;
private int incY = 0;
private SurfaceHolder holder;
private Thread t;
public canvas(Context context) {
super(context);
this.setBackgroundColor(Color.WHITE);
setFocusable(true);
setFocusableInTouchMode(true);
setOnTouchListener(this);
bulletStartX = 0;
bulletStartY = 0;
bulletX = bulletStartX;
bulletY = bulletStartY;
bullet = BitmapFactory.decodeResource(getResources(), R.drawable.bullet);
holder = getHolder();
holder.addCallback(this);
}
public void onDraw(Canvas canvas){
if(bulletEndX != -1 && bulletEndY != -1){
Log.e("here", "drawing bullet");
Log.e("here", "x: " + bulletX + ", y: " + bulletY);
canvas.drawBitmap(bullet, bulletX, bulletY, null);
}
}
public void updateBullet(){
Log.e("here", "inc bullet");
bulletX += incX;
bulletY += incY;
if(bulletX > bulletEndX){
bulletEndX = -1;
}
if(bulletY > bulletEndY){
bulletEndY = -1;
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int[] coordinates = {(int) event.getX(), (int) event.getY()};
int motion = event.getAction();
switch(motion){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
bulletX = bulletStartX;
bulletY = bulletStartY;
bulletEndX = (int) event.getX();
bulletEndY = (int) event.getY();
incX = (int) bulletEndX / 50;
incY = (int) bulletEndY / 50;
Log.e("here", "touch up");
break;
}
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
t = new GameThread(this, holder);
t.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
//Thread class
class GameThread extends Thread{
private canvas canvas;
private SurfaceHolder holder;
private boolean run = false;
long delay = 70;
public GameThread(canvas canvas, SurfaceHolder holder){
this.holder = holder;
this.canvas = canvas;
startThread(true);
}
public boolean isRunning(){
return this.run;
}
private void startThread(boolean run){
this.run = run;
}
public void stopThread(){
this.run = false;
}
#Override
public void run(){
while(run){
Canvas c = null;
try {
//lock canvas so nothing else can use it
c = holder.lockCanvas(null);
synchronized (holder) {
//clear the screen with the black painter.
//This is where we draw the game engine.
//Log.e("drawthread", "running");
if(bulletEndX != -1 && bulletEndY != -1){
updateBullet();
canvas.postInvalidate();
}
canvas.onDraw(c);
//Log.e("drawthread", "ran");
try {
sleep(32);
//Log.e("slept", "sleeping");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
}
}
}
The drawing works pretty well, however there are two problems.
1. the drawing is fairly slow and jumpy... compared to something like this java example http://www.youtube.com/watch?v=-g5CyPQlIo4
2. If the click is on a position to close to Y = 0 then due to the value of ENDY / FRAME being less that 1, means that the round up is to 0 and the bullet travels across the top of the screen, rather than the ocassionaly increment in Y.
#SyntaxT3rr0r your right, that is prob the best way to go about it. But do you know of any documentation for implementing something like this?
Thanks again to everyone who replied
My understanding is that you're asking how to determine how many x&y pixels to move the bullet per frame, as opposed to asking about implementation details specific to animating on Android.
The short answer: Attack it with Math :P
With the example of the bullet:
-You can either chop up the animation as "divide up the animation into 100 frames, play them as fast as we can" or "Play the animation in about 2 seconds, smash as many frames in those 2 seconds as you can." I'm going to explain the former, because that sounds like what you're trying to do.
Start out with a starting X & Y, and an ending X & Y: Let's pretend you want to move from 0,0 to 200,400, and you want to do it in about 100 frames of animation.
Divide up the total distance travelled along the X axis by the number of frames. Do the same with total distance along Y axis. Now you have the distance to travel x & y for each frame. For this example, you want the bullet to move 2 pixels per frame (200 pixels / 100 frames) sideways, and 4 pixels per frame (400 pixels / 100 frames) vertically. So every frame, add x +=2, y+=4.
I suggest you to read the following articles:
View Animation and Property Animation
I don't think this can be answered in it's current form. First how are you animating? are you using the graphics API? GL? AndEngine?
If is graphics API I would rotate the canvas the appropriate degree and move the bullet up the y axis.
For GL you can do the same thing.
For and engine, refer to the tutorials.