Hi everyone This is my first post so please be gentle. Even though my android final is complete and all i still find it hard to not continue to adjust my program and add to it just for the fun of it. what it is is a simple program that when you touch the screen the character on the screen laughs one of two ways with matching vibration. It works nicely but when my teacher (who was in a foul mood at the time) went to test it he pretty much button mashed the thing which made it que up every button press and we had to sit through like 15 laughter fits before we could do anything more with the phone. What i want to do is just have one touch event count until the first event is complete. what it is is a simple on touch event with a couple nested if statements.
public boolean onTouch(final View v, MotionEvent m)
{
v.setBackgroundResource(R.drawable.laughing);//changes the image to the laughing monkey
if (m.getAction() == MotionEvent.ACTION_DOWN)
{
if (timesTouched != I) //checks to see if the touch amount is not equal to the random number
{
if(laughter != 0)
{
sp.play(laughter, 1, 1, 1, 0, 1);//plays the loaded sound when the screen is pressed
//vib.vibrate(900);
}
Time = 900;
timesTouched++;
intDelay = 1;
if(vibon == 1)
{
vib.vibrate(Time);
}
}
else if (timesTouched == I)//checks to see if the touch amount is the same as the random number
{
if(laughter != 0)
{
sp.play(laughFit, 1, 1, 1, 0, 1);//plays the loaded sound when the screen is pressed
}
Time = 6000;
timesTouched = 0;
intDelay = 1;
if(vibon == 1)
{
vib.vibrate(laugh, -1);
}
}
}
else if((m.getAction() == MotionEvent.ACTION_UP) && (intDelay == 1))
{
try {
Thread.sleep(Time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
v.setBackgroundResource(R.drawable.normal) ;//returns the image to the normal looking monkey
intDelay = 0;
}
return true;
}
}
the sleep timer is there to prevent the background image from going back to the default before the laughter is over. I did attempt to get my teacher to help but he is just a speedy replacement and had never even touched an android device before starting to teach at the beginning of this year. please any help you can provide is greatly appreciated because so far i have had to pretty much teach myself this stuff with the help of Google searches.
Thanks a Bunch!
If this were me I would probably move the sound playing to a separate thread then use messages to notify that thread when to start and stop the sounds. In this way the button can send the message immediately and not be blocked by any subsequent actions. The side-effect should be that every button press stops the previous laughter and starts a new one (or just queues up another sound byte over the previous one effectively combining sounds). You could contrarily disable the button until the sound is finished but I do not believe that would be a good user experience - they would expect to be able to mash and mash to their hearts content!
Check out http://developer.android.com/resources/articles/painless-threading.html for more guidance on how to approach such a thing.
Related
I'm working on a memory game for Android and I'm having a problem. When the user taps the second image - if the images are not the same I want the second image to show for 1, 2 seconds.
What I've tried is to sleep for 1-2 sec. the UI thread after the second image is activated - but this doesn't seem to work - the second image doesn't seem to show! (only the first images is showed)
Here's my code:
public void whenTapedImage(View v, int position)
{
i++;
ImageView imgV=(ImageView)v;
if(i%2!=0)
{
firstClick=position;
imgV.setImageResource(im.images.get(firstClick));
}
else
{
secondClick=position;
imgV.setImageResource(im.images.get(secondClick));
try {
Thread.currentThread().sleep(1000);
if(!(im.images.get(firstClick).equals(im.images.get(secondClick))))
{
Toast.makeText(easyGame.this, "Try Again!", Toast.LENGTH_SHORT).show();
im.notifyDataSetChanged();
gridview.setAdapter(im);
gridview.invalidate();
aux=player1Turn;
player1Turn=player2Turn;
player2Turn=aux;
}
else{
done=done+2;
ImageAdapter.keepVisibleViews.add(firstClick);
ImageAdapter.keepVisibleViews.add(secondClick);
if(player1Turn==true)
{
player1Score++;
String score=Integer.toString(player1Score);
score1.setText(score);
}
if(player2Turn==true)
{
player2Score++;
String score=Integer.toString(player2Score);
score2.setText(score);
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
What am I doing wrong?
You must not sleep the UI thread as this would prevent android from delivering any other events to your activity's UI.
Instead, do something such as use a timer and have the timer's method use the run on ui thread facility to make the desired postponed change.
For robustness you may need to implement a state machine (either formally, or in effect) to keep track of what is supposed to be happening - you'll need to decide if the current delay should be aborted or enforced if another button is pushed, and make the state machine treat that appropriately.
This is similar to Waiting in android app
Try following the solution posted there and use the Timer Class
I am trying to calibrate an accelerometer, but I can't obtain the 6 sample values at 6 different acceleration readings required for the calibration. PreliminaryW is a double[6][3] array made to fill those sample values. It is 6 by 3 because each acceleration reading has an x, y, and a z component.
I am planning to sample them by pressing a button at the 6 different acceleration readings. This button makes "calibrate" true.
I ofcourse, first make "calibrating" true to start this thread.
For some unfathomable reason, preliminaryW[i] = currentAcc seems to be filling up from 0 to i with the same value instead of just i. I made sure that the currentAcc is different every time I press the "calibrate" button.
What is wrong with my code?
public synchronized void run() {
Log.d(TAG, "+ in Calibrator thread +");
int i = -1;
while (calibrating) {
if (calibrate) {
i = i + 1;
calibrate = false;
preliminaryW[i] = currentAcc;
if (i == 5) {
calibrating = false;
}
}
}
}
I'm not very familiar with the inner-workings of the accelerometer, and it's hard to decide why it's not working without seeing more of the code. For example, can you be sure that there's only one instance of the Thread, or are you creating multiple instances?
Why does this need to be in a Thread?
Looping like that is not good practice either, you should use wait/notify if you absolutely need a Thread. (more info at http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html)
I've drafted a class that does approximately what you want, but doesn't use threading. You could create a Calibrator and then call performCalibration() with each new value:
class Calibrator{
int count = 0;
double[][] preliminaryW = new double[6][3];
public void performCalibration(double[] currentAcc){
preliminaryW[count] = currentAcc;
count++;
}
}
without the Thread and "busy loop", you might be able to omit those flags for 'calibrate' and 'calibrating' which would certainly help debugging as well.
Good Luck!
Im writing an educational mathematics game were you can choose many operations at the same time, now im trying to generate 5 questions, however it runs all the five questions soo fast that i cant answer them except last questions because it is stuck there. so i thought about creating a thread that will wait() after creating the first question and wait till it is solved and answered correctly, then proceed to the next question and so on... now i have never used wait and notify before so where should i assign them
here what i got so far, however it gives me an exception:
while (counter < 5) {
Thread pause = new Thread() {
#Override
public void run() {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
// ops array is for knowing what operation he chose,
// 1 for plus and 2 for minus
//generate random number within the range of the operations length.
int gen = Integer.valueOf((int) ((Math.random() * (ops.length))));
Log.d("Testgen", String.valueOf(gen));
//random operation is generated
int TheChosenOperation = ops[gen];
Log.d("Test The chosen", String.valueOf(TheChosenOperation));
switch (TheChosenOperation) {
case 1: // if it is plus, assign the generated numbers to i and j from plus.java
Toast t = Toast.makeText(SecondActivity.this, "Plus", 5000);
t.show();
tv.setText("+");
int i = plus.getBorder();
int j = plus.getBorder2();
tv2.setText(String.valueOf(i));
tv3.setText(String.valueOf(j));
answer = plus.getAnswer();
break;
case 2: //if it is minus, assign the generated numbers to i and j from minus.java
Toast t2 = Toast.makeText(SecondActivity.this, "minus", 5000);
t2.show();
tv.setText("-");
int i2 = minus.getBorder();
int j2 = minus.getBorder2();
tv2.setText(String.valueOf(i2));
tv3.setText(String.valueOf(j2));
answer = minus.getAnswer();
break;
}
b.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// TODO Auto-generated method stub
if (answer > 0 && answer == Integer.parseInt(ed.getText().toString())) {
Toast t = Toast.makeText(SecondActivity.this, "true",
5000);
t.show();
this.notify(); // if the answer is true then notify()
} else {
Toast t = Toast.makeText(SecondActivity.this, "false",
5000);
t.show();
}
}
}); // end of clicklistner
counter++; // counting for 5 questions
}//finally
}//run
}; // end of thread
pause.start();
} //end of while loop
im still new with android and threads so please be patient with me.
Thanks in advance and sorry for my horrible english.
The usage of Thread in this context is not really correct.
There's a main thread or the UI thread responsible for presenting and managing the UI state. If you want to make any changes to the UI elements like TextView, Button, etc., you'll have to do it in this thread. If you keep long duration tasks in this thread or make this thread wait, your application will become unresponsive and the Android OS will show an ANR dialog. (Read more about it in Keeping Your App Responsive)
Threads are usually used when there are concurrent tasks to be executed. In this case, the tasks are not really concurrent, but rather sequential.
You should think more in terms of an event-driven approach where the application takes decision based on events (like question answered, which can be used to check how many questions have been answered to decide whether you need to generate more questions).
I think this is not an easy question.
I'll be brief and give a little example of what is happening.
Let's say we have a source of data in file Byron.txt:
SHE walks in beauty, like the night
Of cloudless climes and starry skies;
And all that 's best of dark and bright
Meet in her aspect and her eyes:
Thus mellow'd to that tender light
Which heaven to gaudy day denies.
And this code execute inside an AsyncTask:
final ArrayList<Record> poem = new ArrayList<Record>();
final Object objectLock = new Object();
private Record rec = new Record();
#Override
protected Void doInBackground(Void... args) {
String line = null;
int i;
int last;
try {
process = Runtime.getRuntime().exec("cat Byron.txt");
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()), 8192);
synchronized (objectLock) {
poem.clear();
last = i = poem.size() - 1;
}
while(line = bufferedReader.readLine()) != null) {
rec.setString(line);
synchronized (objectLock) {
last++;
poem.add(last, rec);
}
while(!bPause && i < last) {
i++;
publishProgress(poem.get(i));
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected synchronized void onProgressUpdate(Record... m) {
if(m.length > 0) {
mContext.mTable.appendRow(m[0]);
}
}
where there is a TableLayout in the UI and each time we get a new line we add a new TableRow to it.
And this is the output we see in the UI:
SHE walks in beauty, like the night
Of cloudless climes and starry skies;
Of cloudless climes and starry skies;
Of cloudless climes and starry skies;
Thus mellow'd to that tender light
Thus mellow'd to that tender light
And we go into the debugger and we see why it happens.
Sometimes the synchronized (objectLock) is skipped and the loop continues.
There is no publishing because i already catched last.
Later the block is executed as many times as it was skipped,
, but the original line is lost and the current line is added instead to poem several times
Then, all the new lines are published until i catches last again.
So you see that I followed the code and I can explain what's happening, the question here is: Why the block is skipped?, Why?
I expected the synchronized block to stall until it can be executed.
At least this how I understood the function of synchronized (objectLock)
even without using wait() and notify()
I don't pretend to open a discussion here (although if you want we can open one in the chat area)
If you see some fault in the code, then, answer the question to let me know.
NOTES:
synchronized is needed because somewhere else in the app, the user may want to email the lines he got so far.
The user may pause the publishing (bpause); that's the while loop and i follows last only when bPause is false.
I decided to publish the answer. Even though I feel very embarrassed by its simplicity.
I discovered it only after I had already dug deep into AsyncTask class and message handling and whatnot.
I publish it in hope it will help people to check the basic things before jumping to
conclusions, and that someone out there will save himself half a day debugging because of
this post.
The Record rec was the same one each time. The poem ArrayList had the same element id for each entry. And the content changed on all of them at once, since they were all the same.
When the progress was published immediately it printed the right string, the last one. But if some delay cause the progress to publish later, then retrieving the poem.get(i) records retrieved a different entry but with the same pointer, thus, the same content.
The solution was to create a new Record each loop.
Do the synchronization for last object.
synchronized (last) {
last++;
poem.add(last, line);
}
I have a:
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (event)
{
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(event.getAction() == MotionEvent.ACTION_DOWN){
isDown = true;
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
isDown = true;
}else if(event.getAction() == MotionEvent.ACTION_UP){
isDown = false;
}
return true;
}
}
Then in my MainGame thread I use setCharacterPosition();
public void setCharacterPosition(){
if(isDown){
CharacterX += 32;
}
}
but this make my Character to run toooo quickly so i tried to add:
Thread.sleep(500);
Because i only want my character to increase with 32 every half a sencond.
IT works but bring my FPS down to 2-3.
How do i do it right?
//Simon
I know little about Java or Android, but this seems like a wrong way to do it.
What you're after is setting up a Game Loop (a loop that continuously runs and updates the game logic, frame by frame).
This loop is executed on timed intervals (usually 60 frames per second, or maybe less on a mobile platform).
On each frame, you can scale the game object's movements based on the time elapsed since last frame.
This gives a scaled movement, irrelevant to the clock speed of the device you're using.
Read more abot game loops in these fine resources:
Simple Java Android Game Loop
Game Loop
Android Game Loop
Android UI is based on Single Thread Model, so do not block UI Thread. Your onTouchEvent executes in UI Thread...when you call Thread.sleep you are making your UI thread to sleep for that specific period of time.
Hope this help. Ref: Painless Threading