Detach child, update, and attach? AndEngine, Android - android

engine.registerUpdateHandler(new TimerHandler(0.2f,
new ITimerCallback() {
public void onTimePassed(final TimerHandler pTimerHandler) {
pTimerHandler.reset();
Rectangle xpRect = new Rectangle(30, 200,
(float) (((player.getXp()) / (player
.getNextLevelxp())) * 800), 40, vbom);
HUD.attachChild(xpRect);
}
}));
I have this so far in my createHUD method. It's pretty simple, it creates a rectangle showing the player's xp in relation to the xp needed for the next level and attaches it to the HUD. The only problem is that the old rectangle is never deleted. How can I have a rectangle like that one that updates itself and removes old ones?

If you use the detachChild() or any other detach method too regularly, you might run into problems sooner or later. Especially because the detaching can only be made on an update thread. You'll never know when exactly your rectangle will be detached again. So, to save you a lot of attaching and detaching, reuse the rectangle:
i) Save a reference of the Rectangle somewhere (as a global variable for example in your Playerclass).
ii) At the beginning when you load your stuff also initialize the rectangle:
Rectangle xpRect = new Rectangle(30, 200, 0, 40, vbom); // initialize it
HUD.attachChild(xpRect); // attach it where it belongs
xpRect.setVisible(false); // hide it from the player
xpRect.setIgnoreUpdate(true); // hide it from the update thread, because you don't use it.
At this point it doesn't matter where you put your rectangle or how big it is. It's only important that it is there.
iii) Now when you want to show the player his XP you only have to make it visible
public void showXP(int playerXP, int nextXP){
float width= (float) ((playerXP / nextXP) * 800); // calculate your new width
xpRect.setIgnoreUpdate(false); // make the update thread aware of your rectangle
xpRect.setWidth(width); // now change the width of your rectangle
xpRect.setVisible(true); // make the rectangle visible again
}
iv) When you no longer need it: In order to make it invisible again just call
xpRect.setVisible(false); // hide it from the player
xpRect.setIgnoreUpdate(true); // hide it from the update thread, because you don't
Of course you can now use the showXP() method in anyway you like and use it in your TimerHandler. If you want a more effect full appearance you do something like this instead:
public void showXP(int playerXP, int nextXP){
float width= (float) ((playerXP / nextXP) * 800); // calculate your new width
xpRect.setIgnoreUpdate(false); // make the update thread aware of your rectangle
xpRect.setWidth(width); // now change the width of your rectangle
xpRect.setVisible(true);
xpRect.registerEntityModifier(new FadeInModifier(1f)); // only this line is new
}
It's actually the same as the above method with just a little change in the last line, that makes the rectangle appear a little more smoothly...

To detach a child from a HUD ,you can write
aHUD.detachChild(rectangle);
To clear all children from HUD
aHUD.detachChildren();
To to clear all HUD from camera , you can write
cCamera.getHUD().setCamera(null);
After using one of above, You can create a HUD & also attach as usual.

Related

How to display a larger image in a smaller ImageView with a sliding effect in Android?

i am having a image of size 1000X400px. Assuming the screen width of the target android device to be 320px. i want to slide that image from right to left. actually i want to do this for a sliding 2d scrolling platformer that i was trying to make. so please help as i am just a beginner(only 2 weeks into android dev.)
i was thinking if i could somehow make the program sleep for some time and i did this:
int START_X = 0;
int START_Y = 0;
final int WIDTH_PX = 320;
final int HEIGHT_PX = 400;
Bitmap SOURCE_BITMAP = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.background);
Bitmap img = Bitmap.createBitmap(SOURCE_BITMAP, START_X, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg=(ImageView)findViewById(R.id.imageView1);
bg.setImageBitmap(img);
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
img = Bitmap.createBitmap(SOURCE_BITMAP, START_X+100, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg.setImageBitmap(img);
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
img = Bitmap.createBitmap(SOURCE_BITMAP, START_X+200, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg.setImageBitmap(img);
but it directly executes the last statements after waiting for 4000ms i.e.
img = Bitmap.createBitmap(SOURCE_BITMAP, START_X+200, START_Y, WIDTH_PX, HEIGHT_PX, null, false);
bg.setImageBitmap(img);
First of all: the reason why you only see the effect of the last setImageBitmap(img) is because the method doesn't actually stop everything and draw the bitmap. It just sets the "drawing process" in a queue and it takes care of it a little bit later.
The reason why it didn't render the bitmap even with the Thread.sleep call, is because you we're freezing the thread which renders the screen. :) The code you write (unless you write it in a separate thread) runs on the same thread as the code that handles the screen drawing. If you freeze that ... you basically block the entire app.
Second: As mentioned, Thread.currentThread().sleep(2000) is not a good approach to stop the thread. The main thread of an Android app is called the UI thread. If you freeze that, your ENTIRE app will freeze. So don't do it, whatever the case. :) Use AsyncTasks instead ... or the postDelayed method of the Handler or some other way. There are a couple more.
Now, to get back to the matter at hand:
If you want to create an Android game, I suggest looking over the libGDX game development framework.
But if you don't want to go right into that, the easiest way of doing what you've described above would be to use an Animation. See this guy's answer.
Another approach could be to use a ValueAnimator, see this . What it does is ... you set a start and end value, i.e. screenWidth and 0 and you set a duration in miliseconds. Some "magic" happens in the back and a method onAnimationUpdate is called periodically with values between the start and end values you entered . You can take that value and set it as the left position of the ImageView. This might move the View from right to left and obtain the effect you want.
A more "advanced" method of doing it
Now ... a good way of doing it, which comes to mind, could be to:
create a class that overrides the ImageView class.
Set that in the .xml file of your layout and make it match_parent.
In its constructor you could load the Bitmap value into memory and keep it in a Bitmap local attribute.
Now .. the next thing you could do is override the onDraw method of the ImageView.
In it, you could use the drawBitmap method of the Canvas object. It accepts a srcRect and destRect which basically describesc what chunk from the original image goes where on the ImageView's canvas.
You could have a separate thread to keep the duration by which the image moves along the screen and that thread could call the invalidate method of your ImageView thus forcing the onDraw method to be called and thus redrawing the bitmap a little bit more to the left.

refreshing of canvas is happening only after touch

I am using ViewPagerparallax class,(for parallax view ) and I am setting the background using pager.setbackgroundAsset(....);
everything works fine....but I have a situation,what i want to achieve is that whenever the fragments go in a pause state and whenever it is resumed,the background should change and hence i am changing the background in the onResume method but seems like change is only visible whenever i try to swipe between fragments ...
this is my code
public void onResume()
{
Activity_Fragments ab = (Activity_Fragments) this.getActivity();
ViewPagerParallax pager=ab.hi();
int arr[]={R.raw.a1,R.raw.a2,R.raw.a3,R.raw.a4,R.raw.a5,R.raw.a6,R.raw.a7,R.raw.a8,
R.raw.a9,R.raw.a10,R.raw.a11,R.raw.a12,R.raw.a13,R.raw.a14,R.raw.a15,R.raw.a16,
R.raw.a17,R.raw.a18,R.raw.a19,R.raw.a20,R.raw.a21,R.raw.a22,R.raw.a23,
R.raw.a24,R.raw.a25,R.raw.a26,R.raw.a27,R.raw.a28,R.raw.a29};
int x=(int)(Math.random() * ((28 ) + 1));
pager.setBackgroundAsset(arr[x]);
A few notes.
First of, I am assuming you are using this: https://github.com/MatthieuLJ/ViewPagerParallax/blob/master/src/com/matthieu/ViewPagerParallax.java
When looking at the source it appears to be that you are doing things correctly.
I few things I can see that could be happening
The max number of pages set is 0, which is the default
The width or height is 0
The background hasn't changed from last time
The application has run out of memory (They say they don't want to use more then 1/5th of the memory
An exception was thrown while creating the bitmap of the image
A few things you could try
For point number 1, if you haven't already, call the set_max_pages with a number you see fit.
Point 2, Is the ViewPager in the activity with width and height set?
Point 3, confirm that you generator is working. (Already done, logic works perfect)
int arr[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
18,19,20,21,22,23,24,25,26,27,28,29};
for(int i = 0; i < 100; i++){
Log.d("TEST", String.valueOf(arr[(int)(Math.random() * ((28 ) + 1))]));
}
This returns the desired output, no problem there.
Point 4 & 5, Check logcat, it could say some errors, the tag should say ViewPagerParallax
I would say point 1 could be it, it will draw the background if it hasn't already once you set it.
I'm not really sure how setBackgroundAsset works, but try a call to invalidate(); at the end of onResume(). That will insure that your onDraw() method will be called ASAP and the new background will be redrawn.

AndroidPlot: How to render a chart in an AppWidget?

I'm trying to use AndroidPlot to draw a chart in a homescreen widget.
I know that in a normal app, it uses a custom view and from what I've seen (Android: AppWidget with custom view not working), the workaround is to render this as a bitmap in an imageView.
Now I've taken the quickstart code for AndroidPlot and put it into the provider class but it doesn't seem to render anything when I drop it on the homescreen.
The difference between this code and the original quickstart code is that in the quickstart, it leverages the Activity.findViewById but obviously it can't be used here.
Can anyone see something here that I'm doing wrong that may be causing the empty rendering?
Appreciate any help you could provide!
private Bitmap getChartImage(Context context)
{
// initialize our XYPlot reference:
mySimpleXYPlot = new XYPlot(context, "My Simple XYPlot");
mySimpleXYPlot.setDrawingCacheEnabled(true);
// add a new series
mySimpleXYPlot.addSeries(new SimpleXYSeries(), LineAndPointRenderer.class, new LineAndPointFormatter(Color.rgb(0, 200, 0), Color.rgb(200, 0, 0)));
// reduce the number of range labels
mySimpleXYPlot.getGraphWidget().setRangeTicksPerLabel(4);
// reposition the domain label to look a little cleaner:
Widget domainLabelWidget = mySimpleXYPlot.getDomainLabelWidget();
mySimpleXYPlot.position(domainLabelWidget, // the widget to position
45, // x position value, in this case 45 pixels
XLayoutStyle.ABSOLUTE_FROM_LEFT, // how the x position value is applied, in this case from the left
0, // y position value
YLayoutStyle.ABSOLUTE_FROM_BOTTOM, // how the y position is applied, in this case from the bottom
AnchorPosition.LEFT_BOTTOM); // point to use as the origin of the widget being positioned
// get rid of the visual aids for positioning:
mySimpleXYPlot.disableAllMarkup();
//mySimpleXYPlot.measure(150, 150);
//mySimpleXYPlot.layout(0, 0, 150, 150);
Bitmap bmp = mySimpleXYPlot.getDrawingCache();
return bmp;
}
Have you stepped through to see if the bitmap is actually empty? My guess is that the Bitmap is fine and the problem exists outside of this chunk of code. Take a look at this example usage of a widget with AndroidPlot - it's super minimal and it definitely works. Hopefully there's a solution in there for you :)
Nick

android - set starting point to top left on libgdx

i want to use libgdx as a solution to scaling of apps based on screens aspect ratio.
i've found this link , and i find it really useful:
http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-different-resolutions-using-libgdx/
i'm quite rusty with opengl (haven't written for it in years) and i wish to use the example on this link so that it would be easy to put images and shapes .
sadly , the starting point is in the middle . i want to use it like on many other platforms - top left corner should be (0,0) , and bottom right corner should be (targetWidth-1,targetHeight-1) ,
from what i remember, i need to move(translate) and rotate the camera in order to achieve it , but i'm not sure .
here's my modified code of the link's example for the onCreate method:
#Override
public void create()
{
camera=new OrthographicCamera(VIRTUAL_WIDTH,VIRTUAL_HEIGHT);
// camera.translate(VIRTUAL_WIDTH/2,VIRTUAL_HEIGHT/2,0);
// camera.rotate(90,0,0,1);
camera.update();
//
font=new BitmapFont(Gdx.files.internal("data/fonts.fnt"),false);
font.setColor(Color.RED);
//
screenQuad=new Mesh(true,4,4,new VertexAttribute(Usage.Position,3,"attr_position"),new VertexAttribute(Usage.ColorPacked,4,"attr_color"));
Point bottomLeft=new Point(0,0);
Point topRight=new Point(VIRTUAL_WIDTH,VIRTUAL_HEIGHT);
screenQuad.setVertices(new float[] {//
bottomLeft.x,bottomLeft.y,0f,Color.toFloatBits(255,0,0,255),//
topRight.x,bottomLeft.y,0f,Color.toFloatBits(255,255,0,255),//
bottomLeft.x,topRight.y,0f,Color.toFloatBits(0,255,0,255),//
topRight.x,topRight.y,0f,Color.toFloatBits(0,0,255,255)});
screenQuad.setIndices(new short[] {0,1,2,3});
//
bottomLeft=new Point(VIRTUAL_WIDTH/2-50,VIRTUAL_HEIGHT/2-50);
topRight=new Point(VIRTUAL_WIDTH/2+50,VIRTUAL_HEIGHT/2+50);
quad=new Mesh(true,4,4,new VertexAttribute(Usage.Position,3,"attr_position"),new VertexAttribute(Usage.ColorPacked,4,"attr_color"));
quad.setVertices(new float[] {//
bottomLeft.x,bottomLeft.y,0f,Color.toFloatBits(255,0,0,255),//
topRight.x,bottomLeft.y,0f,Color.toFloatBits(255,255,0,255),//
bottomLeft.x,topRight.y,0f,Color.toFloatBits(0,255,0,255),//
topRight.x,topRight.y,0f,Color.toFloatBits(0,0,255,255)});
quad.setIndices(new short[] {0,1,2,3});
//
texture=new Texture(Gdx.files.internal(IMAGE_FILE));
spriteBatch=new SpriteBatch();
spriteBatch.getProjectionMatrix().setToOrtho2D(0,0,VIRTUAL_WIDTH,VIRTUAL_HEIGHT);
}
so far , i've succeeded to use this code in order to use scaled coordinates , and still keep aspect ratio (which is great) , but i didn't succeed in moving the starting point (0,0) to the top left corner .
please help me .
EDIT: ok , after some testing , i've found out that the reason for it not working is that i use the spriteBatch . i think it ignores the camera . this code occurs in the render part. no matter what i do to the camera , it will still show the same results.
#Override
public void render()
{
if(Gdx.input.isKeyPressed(Keys.ESCAPE)||Gdx.input.justTouched())
Gdx.app.exit();
// update camera
// camera.update();
// camera.apply(Gdx.gl10);
// 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);
//
final String msg="test";
final TextBounds textBounds=font.getBounds(msg);
spriteBatch.begin();
screenQuad.render(GL10.GL_TRIANGLE_STRIP,0,4);
quad.render(GL10.GL_TRIANGLE_STRIP,0,4);
Gdx.graphics.getGL10().glEnable(GL10.GL_TEXTURE_2D);
spriteBatch.draw(texture,0,0,texture.getWidth(),texture.getHeight(),0,0,texture.getWidth(),texture.getHeight(),false,false);
spriteBatch.draw(texture,0,VIRTUAL_HEIGHT-texture.getHeight(),texture.getWidth(),texture.getHeight(),0,0,texture.getWidth(),texture.getHeight(),false,false);
font.draw(spriteBatch,msg,VIRTUAL_WIDTH-textBounds.width,VIRTUAL_HEIGHT);
spriteBatch.end();
}
These lines:
camera.translate(VIRTUAL_WIDTH/2,VIRTUAL_HEIGHT/2,0);
camera.rotate(90,0,0,1);
should move the camera such that it does what you want. However, my code has an additional:
camera.update()
call after it calls translate, and it looks like you're missing that. The doc for Camera.update says:
update
public abstract void update()
Recalculates the projection and view matrix of this camera and the Frustum planes. Use this after you've manipulated any of the attributes of the camera.

view.invalidate() not working to redraw imageview

Ok guys, this may sound dumb, but I have been banging my head against the keyboard for some time now trying to figure out why this will not refresh. the basics: I have a little sample app that i am testing to see if i can rotate an image around a point a X amount of degrees, and show it one degree at a time to make a smooth animation. So I have a great sample i found that works great with a slider bar, basically setting the images rotation to a point on the slider bar, great! but.... when i try and create a for loop with a random number and use my for variable updating the image along the way every degree... it does nothing... and all i get is the updated image at the end... but when i drag my finger on he slider bar the graphic is updated instant as me spinning it... I cant figure out what i am doing wrong here... here is the code with the slider... i don't have my piece that creates the random number and draws it but essentially i did it behind a button click
essentially if you look at this piece i did the same behind a button again but it doesnt do it "real time". i called view.invalidate() and view.postinvalidate() to try to force it but no go...
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
curRotate = (float)progress;
drawMatrix();
}
private void drawMatrix(){
Matrix matrix = new Matrix();
matrix.postScale(curScale, curScale);
matrix.postRotate(curRotate);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
myImageView.setImageBitmap(resizedBitmap);
}
I think what you did was something like:
for (int degrees = 0 ; degrees < maxRotation ; i++) {
// perform the rotation by matrix
myImageView.invalidate();
}
This wouldn't work because invalidate() only schedules a redraw in the main thread event queue. This means that the redraw will be performed only when the current code has all been executed (in this case, the for cycle).
For a simple rotation a Tween Animation would be better suited. For more advanced stuff (like game animations) you might need to create a custom view or use SurfaceView.
Sounds like you're blocking the UI thread with your code to rotate the image.
I don't have any code to show you right now (reply back and when I'm home tonight I can post something that should help), but yuo will probably get better results placing your rotate code in an AsyncTask, see the Painless Threading area of the dev site for more info.
I was having the same problem, i used:
runOnUiThread(new Runnable() {
public void run() {
myImageView.setImageBitmap(image);
imageView.invalidate();
}
});

Categories

Resources