In my main activity I have a button with the text "Play". I want the text to gradually grow and then gradually diminish in size. This should loop until the button is clicked. This effect should appear like a gentle glow.
So, I have tried using a Thread to accomplish this:
// Play Button Animation Thread
Thread playAnimation = new Thread() {
public void run() {
try {
int textSize = 25;
while (textSize <= 50) {
playBtn.setTextSize(textSize);
textSize += .10;
sleep(100);
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally {
}
}
};
Then, I called the thread with:
playAnimation.start();
It isn't working as I have it, but now I'm thinking there is probably a better way. Any help is appreciated.
There are a couple things wrong with the code you have written:
You have declared your textSize variable as an int. Thus, your attempt to increment the value by 0.1 each iteration is futile because the value is cast back to an int after each operation, dropping off the value you just added (i.e. 25 += 0.1 -> 25.1, cast back to an int -> 25...lather, rinse repeat). So the value you are passing to setTextSize() never actually changes.
Your code does not repeat. That loop will only run once until the value reaches 50, and then stop. You won't get the back/forth effect you are going for.
Your updates to the UI should ALWAYS occur on the main/rendering thread. You should never call any update methods (like setTextSize()) from any thread you have created. This can be solved by employing a Handler to manage the threading for you.
If you want the entire button to animate, you can look at the animation framework like others have suggested. However, to automate just the text size, you are on the right path...we just need to tweak your code based on the points I mentioned above:
Handler mHandler = new Handler();
boolean mReverse = false;
Runnable mUpdate = new Runnable() {
#Override
public void run() {
float current = playBtn.getTextSize();
if(mReverse) {
current -= 0.1;
playBtn.setTextSize(current);
mReverse = (current <= 25);
} else {
current += 0.1;
playBtn.setTextSize(current);
mReverse = (current >= 50);
}
mHandler.postDelayed(mUpdate, 100);
}
}
The Handler is created on the main thread, and all code inside the Runnable is executed on the main thread...so you may update the UI there. postDelayed() takes care of the wait delays so you don't really need to create another thread at all. To start your animation, just call
mHandler.postDelayed(mUpdate, 100);
anywhere in your code. To stop the animation at any time, simply stop calling postDelayed after each iteration.
HTH!
You can use Animation resources to perform the above task.
You need to create an animation resource file (anim_play.xml) and put it in res/anim folder.
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatCount="infinite"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.2"
android:toYScale="1.2" >
</scale>
Set the animation to the particular view.
play_btn = (Button) findViewById(R.id.btn_play);
playAnim = AnimationUtils.loadAnimation(this, R.anim.anim_play);
play_btn.startAnimation(playAnim);
remove the animation when the user clicked the button.
play_btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
play_btn.clearAnimation();
}
});
Look for animations in android - it is best way to create simple effects.
About you code - changing of user interface are allowed only from main thread, so you can't create another thread for animation.
Related
I am building my first android application and I am trying to make a memory game. Anyhow, I need to make an array of buttons change color for 1 second and then return to its original color in order, for example: button1 changes to yellow, stays like that for 1 second then returns to gray, then button2 changes to yellow for 1 second then returns, and so on. I tried using the handler but it always works only after the last iteration, this is my code:
for (i = 0; i < 9; i++) {
buttonList.get(i).setBackgroundColor(Color.YELLOW);
runnable =new Runnable(){
#Override
public void run() {
buttonList.get(i).setBackgroundColor(Color.GRAY);
}
};
handler.postDelayed(runnable,1000);}
what am I doing wrong?
EDIT
Found How to do it. First I need to make a runnable class that takes paramaters ex MyRunnable implements Runnable (using Runnable interface), then writing a method that uses this paramater, I can't do it with the regular one because it depends on i and i changes with the iteration.
You need to create a new Runnable inside each loop because all 9 delayed posts are running the same runnable that you create on the 9th and final loop since the loop no doubt takes less than a second to complete. So try something like this:
for (i = 0; i < 9; i++) {
buttonList.get(i).setBackgroundColor(Color.YELLOW);
Runnable runnable = new Runnable(){
#Override
public void run() {
buttonList.get(i).setBackgroundColor(Color.GRAY);
}};
handler.postDelayed(runnable,1000);
}
You're synchronously (at the same time) setting all buttons' colors to yellow, and also creating 9 asynchronous tasks (one for each button) to change color to gray after one second. It means all buttons will change colors back to gray after around 1 second, (more or less) at the same time.
Think of the handler as a queue that you add tasks to. The call postDelayed() is scheduling your tasks to be executed in the future, but all of them are scheduled at the same time, so all of them will be executed at the same time in the future.
I haven't run it, but I think this approach is more of what you are looking for:
// Those are fields
private int buttonIndex = 0;
private boolean yellow = false;
private final Handler handler = new Handler(new Handler.Callback() {
#Override
public void handleMessage(Message msg) {
if (!yellow) {
buttonList.get(buttonIndex).setBackgroundColor(Color.YELLOW);
handler.sendEmptyMessageDelayed(0, 1000);
} else {
buttonList.get(buttonIndex).setBackgroundColor(Color.GRAY);
if (++buttonIndex < 9) handler.sendEmptyMessage(0);
}
yellow = !yellow;
}});
// Call this to start the sequence.
handler.sendEmptyMessage(0);
Note that I'm using sendEmptyMessage*() instead of post*(), but either approach could be used. Additionally, handler's messages (tasks) can have input parameters, so it'd be nice to use them.
There are other similar SO questions but none really helped.
I just want to implement a progress bar, that on button click, starts from 0, moves gradually and stops at a point (which is read from sharedPreferences).
However things are working but that progress bar is not gradually updating , instead it appears straight at the end point.
A simplified code I'm pasting here:
public class MainActivity extends ActionBarActivity {
int primaryProgress = 0;
int finalPrimaryProgress = 60;
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setMax(100);
progressBar.setProgress(0);
}
public void click(View view) {
while (primaryProgress < finalPrimaryProgress)
{
progressBar.setProgress(primaryProgress+=1);
Thread.sleep(100); //This is inside Try-catch in actual code.
}
}
}
I know the problem is with Threads, but I am not too friendly with threads so unable to understand the problem.
I'm assuming your "click" method is happening on the UI thread (otherwise, you'd probably get an exception when you try to alter the view on a background thread).
Think about what you're doing here - the moment you click a button, you begin a loop. On each iteration of the loop, you change the value of the progress bar, and then make the (main) thread sleep for 100ms, then repeat.
Problem is, you're never letting the runtime react to what change you made. Once you change a value of a view (for instance, change the text of a TextView), the Android runtime needs to have time to react to your change, adjusting the view's size, position and then redraw. Your code basically blocks anything from happening before you change the value again, because you're telling the main thread to sleep, and then you immediately change the value again.
The expected result here would be seeing the progress bar drawn in its final state once you stopped changing its value without letting the runtime actually do anything with it.
As an alternative to your current click method, try doing something with postDelayed. This will allow the runtime to perform a full measure-layout-draw cycle between every iteration:
private void increaseProgressBar()
{
if (progressBar.getProgress() < progressBar.getMax())
progressBar.postDelayed(new Runnable() {
#Override
public void run() {
progressBar.setProgress(progressBar.getProgress() + 1); // increase the value of the progress bar.
increaseProgressBar(); // call the function again, but after a delay.
}
}, 100);
}
public void click(View v)
{
increaseProgressBar();
}
Note: Keep in mind that currently, clicking the button multiple times will cause erratic behaviour, since your basically adding additional runnables to this sequence, causing the progress bar to grow faster. You could defend against such a case fairly easily with a simple flag.
im trying to make a thread that will run a While-loop and inside the while-loop actions that will change the UI should take place :
public void GameHandler()
{
layout = (RelativeLayout) findViewById(R.id.MyLayout);
params=new RelativeLayout.LayoutParams(30,40);
btn1 = (Button) findViewById(R.id.MovingObj);
xBounds=new int[2];
yBounds=new int[2];
xBounds[0]=0;
xBounds[1]=layout.getWidth();
yBounds[0]=0; //will change once i set up the screen settings !
yBounds[1]=layout.getHeight();
selectFlyingObject();
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (CurrentXLocation <= xBounds[1] + 10) {
CurrentXLocation = CurrentXLocation + 1;
params.leftMargin = CurrentXLocation;
params.topMargin = 50;
btn1.setLayoutParams(params);
SystemClock.sleep(1000 - SpeedRate);
}
}
};
Thread myThread = new Thread(myRunnable);
myThread.start();
}
However my first problem is that the following error occurs :
Only the original thread that created a view hierarchy can touch its views.
and the second problem is that i have heard that using threads in background will consume toooooo much of the device CPU, fact that may lead to my app-crash. Does something like that indeed happens ?
thank you for your answers
First off, I recommend reading this article, as the current construction you're using is kind of .. dirty ..
Anyway, in order to respond to the actual error you're receiving, it's obvious that you can't update the UI thread from a background thread.
Why ? Because if you could, imagine 10 different threads updating the UI at the same time .. That'd give buggy-looking applications, wouldn't it?
Also it would mean that every UI element would need to be locked, synchronised, in order to provide more or less consistent behaviour. This would defeat the purpose of having threads perform 'hard work', they'd become dependant on the UI ..
(If anyone can give a very clear reason / reference to a guideline and / or reason comment it and I'll adjust the answer)
So, in Android you can get a hold of the main Looper, a utility used to perform tasks in a sequential order, performed on a specified thread.
What we hereby do, is actually queueing our operations to be performed on the main thread.
public class GameHandler implements Runnable {
Handler mHandler = new Handler(Looper.getMainLooper());
Layout mLayout;
View mView;
int currentX, mSpeedRate;
public GameHandler(Layout layout, View viewToAnimate, int speedRate) {
mLayout = layout;
mView = viewToAnimate;
mSpeedRate = speedRate;
}
public void handleGame() {
final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(30, 40);
int[] xBounds = new int[2];
int[] yBounds = new int[2];
xBounds[0] = 0;
xBounds[1] = mView.getWidth();
yBounds[0] = 0; //will change once i set up the screen settings !
yBounds[1] = mView.getHeight();
selectFlyingObject();
while ( currentX <= xBounds[1] + 10 ) {
currentX = currentX + 1;
params.leftMargin = currentX;
params.topMargin = 50;
mHandler.post(new Runnable() {
#Override
public void run() {
mView.setLayoutParams(params);
}
});
SystemClock.sleep(1000 - mSpeedRate);
}
}
void selectFlyingObject() {
// No idea what you're doing here, but keep in mind you're still working on the
// background thread if you call this method from somewhere not wrapped in the mHandler
}
#Override
public void run() {
handleGame();
}
}
Disclaimer, this code is untested and from the top of my head. If it does not do what you think it should do, feel free to mention it.
The structure is adapted, what you have to do now is start the thread as you were attempting to, or use an Executor.
Thread myThread = new Thread(new GameHandler());
myThread.start();
In case I'm not clear enough on the subject, these links are for reference :
What is the purpose of Looper and how to use it?
http://developer.android.com/reference/android/os/Handler.html
"I have heard..." is not a particularly credible source of information. Using background threads is absolutely fine in Android. Spawning undying threads every second does lead to an app crash, just like making an infinite loop will - and with the same comments from fellow devs.
Your "problem" is self-explanatory - as each and every tutorial on Java concurrency in a UI toolkit states - DO NOT UPDATE UI FROM BACKGROUND THREADS. Use one of the numerous mechanisms to send a signal to the UI thread to do it. In your case - a Timer will fit nicely (it will wake up every x seconds and execute a runnable).
I'm trying to build an animation where this image:
Becomes this image:
Where the three bars fade in like 1...2...3... then back to none. At this point the animation will repeat. Here is what I tried to make work:
AlphaAnimation anim = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.fade_in);
anim.setRepeatMode(Animation.RESTART);
anim.setRepeatCount(Animation.INFINITE);
anim.setStartOffset(3000);
LayoutAnimationController controller = new LayoutAnimationController(anim, 0.5f);
rl.setLayoutAnimation(controller);
rl.startLayoutAnimation();
This almost works, but the problem is that the first child in the view group of those wifi bars doesn't wait for the last child to complete its animation before it goes off. The result is a beat that looks like 1...2...3..1..2.3...1...3...1..2.3 instead of a nice 1..2..3..1..2..3. I need to find a way to make the controller wait until the cycle is completed before setting the 1st child off again. I haven't been able to find any solutions on this, does anyone have some input? I'm open to changing the way I'm doing this altogether, this LayoutAnimationController class just seemed to be the best way up until this point.
Thanks!
When I had to use an animation for a image, I prepared the frames. I used AnimatioDrawable with this class:
* This class is a hack to simply get an on finish callback for an
* AnimationDrawable.
*
*/
public class CustomAnimationDrawable extends AnimationDrawable {
// We need to keep our own frame count because mCurFrame in AnimationDrawable is private
private int mCurFrameHack = -1;
// This is the Runnable that we will call when the animation finishes
private Runnable mCallback = null;
/*
* We override the run method in order to increment the frame count the same
* way the AnimationDrawable class does. Also, when we are on the last frame we
* schedule our callback to be called after the duration of the last frame.
*/
#Override
public void run() {
super.run();
mCurFrameHack += 1;
if (mCurFrameHack == (getNumberOfFrames() - 1) && mCallback != null) {
scheduleSelf(mCallback, SystemClock.uptimeMillis() + getDuration(mCurFrameHack));
}
}
/*
* We override this method simply to reset the frame count just as is done in
* AnimationDrawable.
*/
#Override
public void unscheduleSelf(Runnable what) {
// TODO Auto-generated method stub
super.unscheduleSelf(what);
mCurFrameHack = -1;
}
public void setOnFinishCallback(Runnable callback) {
mCallback = callback;
}
public Runnable getOnFinishCallback() {
return mCallback;
}
}
And in my code I use it like:
CustomAnimationDrawable staticAnimation = new CustomAnimationDrawable();
//add frames with resources and duration. step is just a class of mine
staticAnimation.addFrame(getResources().getDrawable(step.getStepImageResId()), step.getStepDuration());
staticAnimation.addFrame(getResources().getDrawable(step.getStepImageResId()), step.getStepDuration());
staticAnimation.addFrame(getResources().getDrawable(step.getStepImageResId()), step.getStepDuration());
staticAnimation.setOneShot(false);
imgView.setBackgroundDrawable(staticAnimation);
staticAnimation.start();
Probably not the best solution out there, but it worked for my needs.
Could someone provide a sample implementation for rotating a button on a thread ? As of now I am rotating my button on the UI thread using the following code:
Animation ranim = (Animation) AnimationUtils.loadAnimation(getBaseContext(),
R.anim.rotation);
buttonRotate.setAnimation(ranim);
following should work for you.
Thread thread = new Thread()
{
#Override
public void run() {
try {
Animation ranim = (Animation) AnimationUtils.loadAnimation(getBaseContext(),
R.anim.rotation);
buttonRotate.setAnimation(ranim);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
you might eventually have to create an AsyncTask which can run independently without disturbing UI Thread.
in your UI thread define
Handler mainHandler = new Handler();
then inside your thread, use this:
mainHandler.post(new Runnable()
{
public void run()
{
//your piece of code
}
});
This has served me well so far in several cases, hope it does for you too! :D
EDIT:
mainHandler.post(new Runnable()
{
public void run()
{
while(someBoolean==true)
{
//your piece of code
}
}
});
if you'd define 'someBoolean' inside your class, just like you did with the handler, the thread is supposed to get it, I believe.
this way, after processing your data, simply set someBoolean to false, and the rotating stops.
Unfortunately I don't have access to my IDE at the moment, so I am going to list the steps for you rather that put buggy code up here.
1) Implement "AnimationListener" in the same class that extends Activity.
2) Set a click listener on your button.
3) Create an AsyncTask class.
4) Override doInBackground (of AsyncTask) and place your resource intensive logic there.
5) In onAnimationStart (of AnimationListener), implement the logic to call your AsyncTask, i.e. new MyTask().execute();
6) Define the animation and set it to your button.
This is how it should go: You click the button, onAnimationStart is called, your AsyncTask logic and animation both start. This way you can have your button rotate at the same that that your background thread is performing resource intensive operations - i.e. concurrently.
And just for fun, you may want to disable your button from being clicked again before the background task has finished.
Sorry for the lack of code, but this should be pretty straight forward from here.