Sorry I am a noob I've read countless tutorials about making a simple timer and was wondering why it doesn't work until I noticed it is the while-loop causing the issue o.O I have removed it and then it works but only 1 time I need to use the loop though so the movement finishes :C
Heres the code:
old_x is the coordinate from an ImageView and new_x from the onTouch Event, maybe the problem because I am casting them as an int? I don't know what I need to do so it works please help O:
while(old_x != new_x)
{
timedMoveIV(100);
old_x = (int)img.getX();
}
It calls this method which works if I do it without the loop.
public void timedMoveIV(int time_ms)
{
//sleep for time_ms milliseconds
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
if(new_x > img.getX())
{
img.setX(img.getX() + 1);
}
else
{
img.setX(img.getX() - 1);
}
}
}, time_ms);
}
Your main problem is that your loop doesn't take a break, so it's constantly running the function, posting a gazillion runnables.
What you want to do is make the runnable call itself after another 100 ms. Take a look at this example:
if(old_x != new_x)
timedMoveIV(100);
Here you simply call the function once. After that, you let the posted runnable decide whether or not it needs to move again:
public void timedMoveIV(final int time_ms)
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run()
{
if(new_x > img.getX())
img.setX(img.getX() + 1);
else
img.setX(img.getX() - 1);
// if not in position, call again
if((int)img.getX() != new_x)
timedMoveIV(time_ms);
}
}, time_ms);
}
It should stop once img.getX() == new_x. Notice the cast to int, though, because if you leave it out, you might get some oscillation as it gets within a pixel of the destination.
This assumes that new_x is an int. If it's a float as well, you should either cast both to int to compare, or compare them to a minimum threshold. For instance, if there's less than 0.5 difference, treat it as "done".
Related
I have a problem with the progress bar, I have created several progress bars and I want to fill the first one with a specific time interval then after it done the second one start filling, I've done the first one like this
final ProgressBar pb_first = findViewById(R.id.Progressbar_first);
*
*
*
pb_first.setMax(10);
pb_first.setProgress(0);
final int[] currentProgress1 = {0};
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
currentProgress1[0] += 1;
pb_first.setProgress(currentProgress1[0]);
if(currentProgress1[0] != 10){
new Handler().postDelayed(this,1000);
}
else{
Toast.makeText(MainActivity.this, "The first step is OVER!!", Toast.LENGTH_LONG).show();
}
}
}, 1000);
but when I try to make the second one inside the else statement it won't work, I even tried to create an integer that changes when the above code is finished "in the else statement", and create an if-statement if(index == 2){} but it did not work as well, the app just stucks when starting it. Anyway to fix this?
I am a newbie to android development so bear with me. I have scoured the site and implemented several suggested answers but I am still running into a problem. I want to iterate through an object array and display the contents of the array with delay to allow user interaction(The user gets to say if the object and the text displayed is what they were looking for by clicking on a yes or no button). My objects however display last first and then start zooming real fast in a seemingly endless loop. Here is the method I call to load the images:
private void displayInstructions() {
for (Emergency_Instructions instruction : instructions) {
final Emergency_Instructions instruction2 = instruction;
final Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
public void run() {
displayimages(instruction2.getStep(), instruction2.getStepImage());
handler.postDelayed(this, 5000);
}
}, 5000);
}
void displayimages(String text, Bitmap image)
{
instructiontext.setText(text);
instructionbmp.setImageBitmap(image);
}
Any help will be greatly appreciated
You create multiple Handlers where you need only one. You tell each one to post a Runnable to the current thread's message queue, to be run after the same delay. That delay expires at pretty much the same time for all the tasks, so they are then executed one right after another, as fast as the queue can go. Each of those tasks also posts a message via the handler, to be run after the same delay. Once that delay expires, that will produce a second group of messages posted rapidly one right after the other, as fast as the device can go.
If you just want to schedule what is effectively a slide show, you might do it like this:
private void displayInstructions() {
final Handler handler = new Handler();
int delay = 0;
int step = 5000;
for (Emergency_Instructions instruction : instructions) {
final Emergency_Instructions instruction2 = instruction;
handler.postDelayed(new Runnable() {
public void run() {
displayimages(instruction2.getStep(), instruction2.getStepImage());
// handler.postDelayed(this, 5000);
}
}, delay);
delay += step;
}
}
Note there that each task is posted with a larger delay than the last -- this is a delay with respect to when the message it posted, not with respect to when the preceding message was displayed.
With that said, I'm doubtful that this is really what you want to do, or at least that it is all you want to do. As it stands, this approach will cause all the messages to be displayed (eventually) regardless of any user interaction. At minimum, you will probably want to provide for subsequent messages to be canceled in the event that the user accepts one, or cancels the overall operation.
I am making an android studio app and am trying to get my screen to loop through the digits 0-9 until the user clicks and stops the loop on a particular number. The code I have written so far is:
public void loop () {
for (int a = 0; a<10; a = ++a % 10) {
textView2.setText("" + a);
}
Relevant xml section
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:id="#+id/textView2"
.../>
This code doesn't output anything onscreen. I have tried a few different variations of the code and if it does display a number, it only displays the last digit, '9'. This makes me think that it has worked but it goes through the numbers too quickly for me to see and stops on the last one instead of looping indefinitely. Is anyone able to point out where I may be going wrong? I'm still learning so apologies for the relatively simple query.
Also when I do get it to work, and I want to make it clickable, do I put the onclicklistener in the for-loop?
Many thanks.
You were right, when it displays 9, it just looped through the numbers very quickly, you couldn't even see them changing.
You are not doing anything wrong, you just have to take pauses between changing the numbers. The very easy and ugly fix would be adding: wait(1000) after the setText(...) method.
However, this is not recommended, because Android draws and receives events on the main thread. When you block the thread for 1000 milliseconds with the wait, you make the thread unable to accept any events for that one second, which makes your app freeze.
To fix the freezing, you need to update your TextView, and then post a message on the main thread, that it should update again over 1 second. Because of the separation, it won't really look like a loop.
For posting messages on the main thread, I will use a Handler (android.os.Handler), which executes messages on the thread it was created, unless you use a different Looper.
Here's a rough example:
private int a = 0;
private Handler handler = new Handler();
private TextView textView2;
public void loop() {
handler.post(new Runnable() {
#Override
public void run() {
updateText();
a = a++ % 10;
if (a < 10) {
handler.postDelayed(this, 1000);
}
}
});
}
public void updateText() {
textView2.setText("" + a);
}
Just call loop() when the button was clicked, the rest will be handled automatically.
No errors, compiles, no run time error.
Hey, I have a simple question about Android sum. I'm doing a simple summation, the code compiles, it does technically do what it suppose to do, sum the number until its greater than 120. However, I wanted the output to show each sum as it goes through the loop. Unfortunately, it just jumps immediately to the last sum on the textview. I tried using a sleep function, wait function, but what ended up happening was it would wait the amount of time for all the loops until the sum was greater than 120, then output the final sum. In this case 4950. Any suggestions for showing each sum on the text view?
I also tried using this line of code, which worked in showing each sum individually, however, it started the timer immediately. Which it should not do.
Toast.makeText(MainActivity.this, "SUM:"+sum1, Toast.LENGTH_SHORT).show();
Basic goal of my simple application
1) Show each sum
2) When the sum is greater than 120 start timer
Question: How do I get it to show each sum individually on the text-view instead of jumping to the final summation?
public void onClick(View v)
for(i=0;i<100;i++)
{
sum1 = sum1 + i;
sum.setText("Sum:"+sum1);
if(sum1 > 120)
{
timer.start();
}
}
}
Thank you for your time.
The reason it "Jumps" to the last one is because the act of clicking runs the for loop while blocking the UI thread. You are in effect not able to see the text changing, but its changing. You can accomplish what you need by using a Timer or TimerTask, which basically creates a separate thread for you. It also handles the delays which you need. Simply do the math in the Timers execution method.
//make sure these are global
int i = 0;
int sum1 = 2;//not really sure where your sum starts so you choose
TimerTask leTask = new TimerTask() {
public void run() {
sum1 = sum1+i;
i++;
/*I don't actually remember if you must go back to the main thread
* when in the tasks run method. Incase you get an error because
* of that then just do that quickly. I have included a link for
* you on how to do that if necessary.*/
sum.setText("Sum:"+sum1);
if(sum1 > 120){
/*Start your actually timer here
* and stop execution of leTask by calling the cancel method*/
}
}
int delay = 0;//you don't really want a delay since you want it immediately after click
int everySecond = 1000;
timer.scheduleAtFixedRate(leTask, 0, everySecond);
Keep in mind the way I set ti up is just an example. You should make sure that you have access to leTask (or whatever you call it) when its cancelled. i and sum1 should also be global. delay and everySecond don't have to be in variables. I did that to kind of explain what they do so it makes a bit more sense. I like this method better because you aren't worried about UI thread stuff with the exception of updating the TextView.
Edit: You can also access the UI from the timer using the post method from TextView. It would look something like this:
sum.post(new Runnable() {
public void run() {
sum.setText("Sum:"+sum1);
});
}
EDIT2:
I have to include this edit to be thorough for future user who come across this as I just found out that Timer and TimerTask is no longer becoming the preferred method for scheduling commands, etc. It should be ScheduledThreadPoolExecutor now. Although this has been the case for some time it hasn't really been reported much in questions on here that i've come across. In either case, android has it in the docs here. It's literally like the second sentence.
This is my proposed solution :D
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="#+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/startButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start counting" />
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
private static final long SUM_DELAY = 100;
private TextView mResultView;
private Button mStartButton;
private Handler mHandler = new Handler();
private int mSum = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mResultView = (TextView) findViewById(R.id.resultView);
mStartButton = (Button) findViewById(R.id.startButton);
mStartButton.setOnClickListener(this);
}
private Runnable sumTask = new Runnable() {
#Override
public void run() {
mSum += 1;
if (mSum > 120) {
mSum = 0;
mStartButton.setEnabled(true);
return;
}
mResultView.setText(String.valueOf(mSum));
mHandler.postDelayed(this, SUM_DELAY);
}
};
#Override
public void onClick(View view) {
if (view.getId() == mStartButton.getId()) {
mStartButton.setEnabled(false);
sumTask.run();
}
}
}
The thing is that onClick() runs on the UI thread. That means that the whole method has to be finished before the UI can update again, hence the sudden jump to the last number. That is also the reason why nothing happens when you use Thread.Sleep(). sleep() applies in that case to the UI thread, so the whole app will freeze.
If you want the TextView to count up, you have to use an AsyncTask. Add the loop and a Thread.sleep() to doInBackground(), publish the progress and set the text in onProgressUpdate(). The link actually contains a nice example that should work for you.
Every time my timer is run, the code executed inside the timer is run faster. Why is this happening?
This code essentially moves a label (holding an image) across the frame.
Every run, the images moves faster.
if (Player.direction == "west") {
timerWest.start();
isCasting = true;
new Magic("westmagic.gif");
timerWest.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int x = Frame.fireMagicLabel.getX();
int y = Frame.fireMagicLabel.getY();
Frame.fireMagicLabel.setLocation(x - 1, y);
LiveUpdating.updateSpell();
CheckHits.spellHit();
Frame.frame.repaint();
if (Frame.fireMagicLabel.getX() <= tempWest) {
timerWest.stop();
new Magic("");
Frame.frame.repaint();
Frame.fireMagicLabel.setLocation(
Frame.redHealthLabel.getX(),
Frame.redHealthLabel.getX());
isCasting = false;
}
}
});
Frame.frame.repaint();
}
On first run, the timer performs x speed. Then the the next run it performs faster, and faster, and faster.
i think you should add the action listener only once.
Ther actionlistener is only needed to be added once.
Also if this does not fix it, somewhere inside the code you haven't posted you apply the speed, this might not be reset and be added on it, which makes it double speed.