Android Progress bars losing their value when updated in a Handler - java

I am working on an Android app that displays cell phone usage information in a progress bar. The bar changes color based on the amount of usage from green to yellow to red. When my TimerTask executes the update though (via a Handler so it goes through the UI thread and not the Timer thread), the progress bars empty, even though the text labels are updated correctly. The code updating the progress bar is:
private void SetBarColor(ProgressBar bar, int progress, int secondaryAdditive){
int setTo = R.drawable.greenbar;
if(progress < 60)
setTo = R.drawable.greenbar;
else if (progress < 90)
setTo = R.drawable.yellowbar;
else
setTo = setTo = R.drawable.redbar;
bar.setProgress(progress);
bar.setSecondaryProgress(progress + secondaryAdditive); //Mocking up phone usage
Rect bounds = bar.getProgressDrawable().getBounds();
bar.setProgressDrawable(this.getResources().getDrawable(setTo));
bar.getProgressDrawable().setBounds(bounds);
bar.setVisibility(View.VISIBLE);
}
This method works fine when called the first time in the onCreate method, but when called from the TimerTask, the bars simply hide themselves, showing only the grey background (as if their progress == 0). I've used the debugger and confirmed that the right values are going into the setProgress and setSecondaryProgress() calls. I have also tried setting the progress both before (as in the snippet above) and after the setProgressDrawable call, to no avail.
Anyone run into something like this?
EDIT: By request, some additional code. Here's the Runnable:
private class MyTime extends TimerTask {
#Override
public void run() {
ReQueryCount--;
if(ReQueryCount <= 0){
ReQueryCount = ReQueryCountStarter;
HeartbeatHandler.post(new Runnable() {
public void run() {
GetDataFromServer();
}
});
}
}
}
The HeartbeatHandler is created in onCreate.
GetDataFromServer gets some data from server, but the part that consumes my SetBarColor above is:
private void UpdateProgressBars(ServiceHelper.UsageResult result) {
int voiceBarProg = (int)((double)result.VoiceUsage / (double)result.MaxVoice * 100);
int dataBarProg = (int)((double)result.DataUsage / (double)result.MaxData * 100);
int msgBarProg = (int)((double)result.TextUsage / (double)result.MaxText * 100);
SetBarColor(voiceBar, voiceBarProg, PhoneVoice);
SetBarColor(dataBar, dataBarProg, PhoneData);
SetBarColor(msgBar, msgBarProg, PhoneText);
}
Short of posting the layouts, manifest and the rest I'm not sure what other code would be helpful.

After searching for hours for a solution of this problem I have found this article that has an answer that has helped me.
But in addition to the answer there I have also get/set the drawable bounds too. So I have a custom ProgressBar which has the following method to set progress color and amount:
public void setProgressColorAndFill(int color, int fill){
Drawable bgDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, new int[]{0xFFdadada, 0xFFdadada});
Drawable proDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, new int[]{color, color});
ClipDrawable clip = new ClipDrawable(proDrawable, Gravity.LEFT,ClipDrawable.HORIZONTAL);
Drawable[] layers = new Drawable[2];
layers[0] = bgDrawable;
layers[1] = clip;
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.setId(0, android.R.id.background);
layerDrawable.setId(1, android.R.id.progress);
Rect bounds = ((LayerDrawable)getProgressDrawable()).getBounds();
setProgressDrawable(layerDrawable);
getProgressDrawable().setBounds(bounds);
setProgress(1);
setMax(100);
setProgress(fill);
}
Hope this will be useful to someone.

Related

lwjgl3 window resize final event?

I know that you can track the window resize operation using:
glfwSetWindowSizeCallback(window, wsCallback = new GLFWWindowSizeCallback() {
#Override
public void invoke(long window, int w, int h) {
LOG.info("window resized");
if (w > 0 && h > 0) {
width = w;
height = h;
}
}
});
However, this way the invoke method gets invoked potentially Hundreds of times, and I only want the final event to store the new size in the configuration. How do I do this without using some sort of delay mechanism like a one second timer that gets refreshed on further invoke calls?
The way GLFW callbacks are set up is so they are refreshed upon each call of glfwPollEvents(). If you only want to set the configuration variables on the final update, this in and of itself is not feasible. I would have a void dispose() method in which you can call this:
public void dispose() {
try (MemoryStack stack = stackPush()) {
IntBuffer width = stack.ints(1);
IntBuffer height = stack.ints(1);
glfwGetWindowSize(windowID, width, height);
configuration.width = width.get();
configuration.height = height.get();
}
}
This allows for you to set the configuration data once when you want to close the window. The draw back to this technique is if the application crashes or the dispose() method isn't called, the configuration data isn't saved.

Libgdx: How can I load assets after screen has already rendered?

I have implemented the loading screen from this example on my Loading Screen that is a child of Game. This is how my asset init method and Screen class looks like. My init method in assets loads classes that contain my AtlasRegion. I have tested that this method is what makes my screen load a black screen as it loads a lot of resources.
public void init (AssetManager assetManager) {
this.assetManager = assetManager;
// set asset manager error handler
assetManager.setErrorListener(this);
assetManager.load(Constants.TEXTURE_ATLAS_OBJECTS, TextureAtlas.class);
assetManager.load(Constants.TEXTURE_ATLAS_UI, TextureAtlas.class);
assetManager.finishLoading();
TextureAtlas atlas = assetManager.get(Constants.TEXTURE_ATLAS_OBJECTS);
TextureAtlas atlasUi = assetManager.get(Constants.TEXTURE_ATLAS_UI);
//font = new BitmapFont(Gdx.files.internal("data/font.fnt"), Gdx.files.internal("data/font.png"), false);
clickSound = Gdx.audio.newSound(Gdx.files.internal("data/sounds/click.wav"));
// create game resource objects
fonts = new AssetFonts(assetManager);
skins = new AssetSkins();
background = new AssetBackgroundImage();
cards = new AssetCards(atlas);
cardimages = new AssetImages(atlas);
cardsjson = new AssetList();
suitimages = new AssetSuitImages(atlas);
}
This is my Loading Screen class:
public class LoadingScreen extends AbstractGameScreen implements Disposable {
private Stage stage;
private Image logo;
private Image loadingFrame;
private Image loadingBarHidden;
private Image screenBg;
private Image loadingBg;
private float startX, endX;
private float percent;
private Actor loadingBar;
public LoadingScreen(CardGame game) {
super(game);
}
public InputProcessor getInputProcessor () {
return (null);
}
#Override
public void show() {
// Tell the manager to load assets for the loading screen
game.manager.load("data/images-ui/loading/loading.pack", TextureAtlas.class);
// Wait until they are finished loading
game.manager.finishLoading();
// Initialize the stage where we will place everything
stage = new Stage();
// Get our textureatlas from the manager
TextureAtlas atlas = game.manager.get("data/images-ui/loading/loading.pack", TextureAtlas.class);
// Grab the regions from the atlas and create some images
logo = new Image(atlas.findRegion("libgdx-logo"));
loadingFrame = new Image(atlas.findRegion("loading-frame"));
loadingBarHidden = new Image(atlas.findRegion("loading-bar-hidden"));
screenBg = new Image(atlas.findRegion("screen-bg"));
loadingBg = new Image(atlas.findRegion("loading-frame-bg"));
// Add the loading bar animation
Animation anim = new Animation(0.05f, atlas.findRegions("loading-bar-anim") );
anim.setPlayMode(Animation.PlayMode.LOOP_REVERSED);
loadingBar = new LoadingBar(anim);
// Or if you only need a static bar, you can do
// loadingBar = new Image(atlas.findRegion("loading-bar1"));
// Add all the actors to the stage
stage.addActor(screenBg);
stage.addActor(loadingBar);
stage.addActor(loadingBg);
stage.addActor(loadingBarHidden);
stage.addActor(loadingFrame);
stage.addActor(logo);
// Add everything to be loaded, for instance:
Assets.instance.init(game.manager);
}
#Override
public void resize(int width, int height) {
// Set our screen to always be XXX x 480 in size
//width = 480 * width / height;
//height = 480;
stage.getViewport().update(width, height, false);
// Make the background fill the screen
screenBg.setSize(width, height);
// Place the logo in the middle of the screen and 100 px up
logo.setX((width - logo.getWidth()) / 2);
logo.setY((height - logo.getHeight()) / 2 + 100);
// Place the loading frame in the middle of the screen
loadingFrame.setX((stage.getWidth() - loadingFrame.getWidth()) / 2);
loadingFrame.setY((stage.getHeight() - loadingFrame.getHeight()) / 2);
// Place the loading bar at the same spot as the frame, adjusted a few px
loadingBar.setX(loadingFrame.getX() + 15);
loadingBar.setY(loadingFrame.getY() + 5);
// Place the image that will hide the bar on top of the bar, adjusted a few px
loadingBarHidden.setX(loadingBar.getX() + 35);
loadingBarHidden.setY(loadingBar.getY() - 3);
// The start position and how far to move the hidden loading bar
startX = loadingBarHidden.getX();
endX = 440;
// The rest of the hidden bar
loadingBg.setSize(450, 50);
loadingBg.setX(loadingBarHidden.getX() + 30);
loadingBg.setY(loadingBarHidden.getY() + 3);
}
#Override
public void render(float delta) {
// Clear the screen
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (game.manager.update()) { // Load some, will return true if done loading
if (Gdx.input.isTouched()) { // If the screen is touched after the game is done loading, go to the main menu screen
game.setScreen(new MainMenuScreen(game));
}
}
// Interpolate the percentage to make it more smooth
percent = Interpolation.linear.apply(percent, game.manager.getProgress(), 0.1f);
// Update positions (and size) to match the percentage
loadingBarHidden.setX(startX + endX * percent);
loadingBg.setX(loadingBarHidden.getX() + 30);
loadingBg.setWidth(450 - 450 * percent);
loadingBg.invalidate();
// Show the loading screen
stage.act();
stage.draw();
}
#Override
public void pause () {}
#Override
public void resume () {}
#Override
public void hide() {
// Dispose the loading assets as we no longer need them
game.manager.unload("data/images-ui/loading/loading.pack");
}
}
I need to load the screen then load my assets on this Assets.instance.init(new AssetManager()) since it is what is causing a black screen. The problem is my screen loads after loading the assets hence this makes my loading screen to be of no use. How can I load this method after the screen has rendered?
Remove the finishLoading call from your init method. That should do it. finishLoading forces the assets to finish loading immediately which is not what you want here. Calling update on it repeatedly, which you are already doing is the way to load assets asynchronously.
Here's a general structure for how it could be done. Call all the load methods on the asset manager before it ever gets to the render method. Then call update repeatedly until everything is loaded. Then get references to the assets before continuing.
private boolean loadComplete;
public void show(){
//...
loadComplete = false;
}
private void init(AssetManager assetManager){
this.assetManager = assetManager;
assetManager.load(/*whatever*/);
assetManager.load(/*whatever*/);
assetManager.load(/*whatever*/);
}
private void onAssetsLoaded(){
loadComplete = true;
myAtlas = assetManager.get("myAtlas.json", TextureAtlas.class);
//and so on
}
public void render(float delta){
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//load one asset at a time per frame until loading is complete
if (assetManager == null || !assetManager.update()){
//maybe draw a load screen image that's not a texture that's being managed
//by the assetManager. You could even play an animation. Otherwise,
//you can leave the screen blank.
return;
}
//will only get here when asset manager is done. The first time, you still
//need to get references to its assets
if (!loadComplete)
onAssetsLoaded();
//the rest of you render method. Everything below here only happens after assets are loaded and asset references have been aquired
}
After trying so many recommended solutions, I finally managed to fix this problem thanks to this blog post
I used a callback which displays an android framelayout with an image as the assets load asynchronously.

Move image up they y-axis in eclipse

I am making a game for androids using eclipse and I had some trouble trying to figure this out. I already have 2 images on my layout and I just want to figure out how to make them move one unit up the y axis with a timer that is repeatedly. Thanks alot for any help appreciate it!
You can do something like
final int interval = 1000; // one second
View myView = ...;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
// Do work
myView.setY(myView.getY() + 1);
handler.postDelayed(this, interval);
}
handler.post(runnable);

Android, I can't update the image src for until another thread is sleeping 600 ms

I want to give the sensation that a button was pressed by the system. The two images are used when the user presses the button and I want to create the same effect using a thread sleeping between the display of both images.
But the system doesn't update the button src as expected. Resuming I want to change the src, wait 600 ms and update again. Can someone give me a direction on it?
switch (color) {
case 1: //green
// first image
Main.imageButtonGreen.setImageResource(R.drawable.light_green);
//this is just to make it sleep to give the sensation that the button was pressed
try {
Blink blink = new Blink();
Thread t = new Thread(blink);
t.run();
t=null;
}
catch (Exception e){if (Main.debug) Log.d("Blink.blink Switch color= ", e.getMessage());}
//scond image
Main.imageButtonGreen.setImageResource(R.drawable.dark_green);
break;
}
I recommend you to use Handler or AsyncTask. This is how I would make:
Create Handler in UI Thread:
Handler UIHandler = new Handler(this);
Now just call
UIhandler.postDelayed(new Runnable(){
#Override
public void run() {
button.setBackgroundColor(Color.GREEN);
}}, 600);
or even better to make changing color smoother
final long startTime = SystemClock.uptimeMillis();
final long duration = 600;
//here specify what interpolator you like
final Interpolator interpolator = new LinearInterpolator();
//here you can pass whatever you want
final int startColor = Color.GREEN;
final int endColor = Color.CYAN;
//take start color components
final int startRed = Color.red(startColor);
final int startGreen = Color.green(startColor);
final int startBlue = Color.blue(startColor);
//take delta color components
final int deltaRed = Color.red(endColor)-startRed;
final int deltaGreen = Color.green(endColor)-startGreen;
final int deltaBlue = Color.blue(endColor)-startBlue;
UIhandler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - startTime;
float t = interpolator.getInterpolation((float) elapsed / duration);
if (t < 1.0) {
button.setBackgroundColor(Color.argb(255, (int)(startRed+t*deltaRed), (int)(startGreen+t*deltaGreen), (int)(startBlue+t*deltaBlue)));
// Post again 16ms later.
UIhandler.postDelayed(this, 16);
} else{
//to restore default values
button.setBackgroundColor(endColor);
}
}
});
Also you can change alpha parameter by passing value from 1 to 255 in Color.argb() as first parameter

Using a single EventHandler for entire array in Java

I've looked everywhere online and have been stumped for a few hours now. I have a program where I have an array of 40 imageViews on a screen. What I want to happen is when I click on a particular imageView in the array, I want the image to change within the imageView.
Here is what I have:
public void initBubbles(){
Image image = new Image("file:src/bubbles/images/bubble.png");
for (int i = 0; i < bubbles.length; i++) {
//Creates a new bubble
bubbles[i] = new Bubble(image, 'A', 1);
//Creates a new image view
ivs[i] = new ImageView(image);
//Various lines of codes that put the imageView in the scene..... (Not relevant)
ivs[i].addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent> () {
#Override
public void handle(MouseEvent event) {
System.out.println("Bubble Popped!");
ivs[i].setImage(new Image("popped.png":); //Will result in arrayOutOfBounds
}
});
}
}
The problem I am running into is getting the selected element from the MouseEvent. I understand that the line right after the "Bubble Popped!" message will cause an ArrayOutOfBounds exception. But I hope you understand what I am trying to. How can I get the imageView that was clicked based on the MouseEvent?
Any help is greatly appreciated as I've been stuck for a few hours.
I cannot check it in compiler now but based on the documentation I guess you will refer to the correct ImageView by the getTarget() method which MouseEvent inherits from javafx.event.Event.
So the correct code would be something like this:
#Override
public void handle(MouseEvent event) {
final ImageView iv = (ImageView) (event.getTarget());
iv.setImage(new Image("popped.png"));
}
And if you really wish to have a single EventHandler for entire array, you should define it outside of the for loop and assign it to a variable - otherwise you are creating a new instance of the handler for each ImageView.
public void initBubbles(){
final Image image = new Image("file:src/bubbles/images/bubble.png");
// Create an event handler to be re-used for all the ImageView's
final EventHandler eventHandler = new EventHandler<MouseEvent> () {
#Override
public void handle(MouseEvent event) {
final ImageView iv = (ImageView) (event.getTarget());
iv.setImage(new Image("popped.png"));
}
}
for (int i = 0; i < bubbles.length; i++) {
//Creates a new bubble
bubbles[i] = new Bubble(image, 'A', 1);
//Creates a new image view
ivs[i] = new ImageView(image);
// Register the event handler
ivs[i].addEventHandler(MouseEvent.MOUSE_CLICKED, eventHandler);
}
}
The handler code runs in different context than where you register the handler (addEventHandler()), so the value of i does not have the same meaning. You should consider the code of the handle() method as completely separate and independent on its surrounding code. The information of the "run-time context" of the handler is in the event which triggered the handler.
To use a parable, by calling the addEventHandler() you tell the ImageView this:
Dear ImageView, when a mouse event occurs above you, call the code of the handler.
And the handler:
Dear handler, when you are triggered, have a look at the target upon which you have been called, consider it as an instance of ImageView and set its image to something new.

Categories

Resources