I'm working on a simple memory game where you basically match the same cards. I want the cards to be able to flip back if both didn't match. The flipping codes are the following:
mT1.unselect();
mT2.unselect();
When i put it in the else statement the cards flip back immediately so i used a handler to slow it down.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mT1.unselect();
mT2.unselect();
playSound( SOUND_FAILED );
}
}, 2000);
But when i test it doesn't flip back, but the sound is played after the given time. What's the problem?
Full code:
public void onPosition(int position)
{
if (position == mLastPosition)
{
return;
}
mLastPosition = position;
Tile tile = mList.get(position);
tile.select();
int sound = tile.mResId % mSounds.length;
playSound(sound);
switch (mSelectedCount)
{
case 0:
mT1 = tile;
break;
case 1:
mT2 = tile;
if (mT1.getResId() == mT2.getResId())
{
mT1.setFound(true);
mT2.setFound(true);
mFoundCount += 2;
playSound(SOUND_SUCCEED);
}
else
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mT1.unselect();
mT2.unselect();
playSound( SOUND_FAILED );
}
}, 2000);
}
break;
case 2:
if (mT1.getResId() != mT2.getResId())
{
}
mSelectedCount = 0;
mT1 = tile;
break;
}
mSelectedCount++;
mMoveCount++;
updateView();
checkComplete();
}
As with your previous question, you've still not given us enough context, but I'll have a wild stab in the dark - maybe you need to call:
updateView();
in your Runnable's run() method.
Maybe if you post your updateView() method and some more context of how it works, we might be able to help more if this guess doesn't fix it.
Related
I have been looking all over the Android Studio Docs and StackOverflow, but I can not seem to find a way to do the equivalent of what is done in IOS here:
Adding Delay In A For Loop Without Blocking UI
in Android.
I have tried to use a Handler, but instead of running the code like:
// Iteration 1
// Delay of 500ms
// Iteration 2
// Delay of 500ms
// ...
The code seems to be run as follows:
// Iteration 1
// Iteration 2
// Delay of 500ms
// next state
The code I am using is stuctured like this:
Handler myHandler = new Handler();
while (someCondition) {
myHandler.postDelayed(new Runnable() {
public void run() {
myFunction();
}
}, 500);
}
The behavior I see when running this Activity is that is skips it, and after 500ms it goes to the next Activity (with expected result) but without showing the user what's happening.
How would I delay the loop so the user can see what's happening?
To clarivy:
The current state (after moveCard()) needs to be shown for x ms before going though the logic of my while loop again.
This is done untill the final state is has been reached.
Then the next Activity is started.
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.horserace_game);
this.cardDeck = findViewById(R.id.deck);
this.game_ended = false;
this.deck = new Deck();
this.aces = new ArrayList<>();
this.acesPos = new ArrayList<>();
this.hidden = new ArrayList<>();
// Remove all Aces from deck and put them in aces
//resetDeck creates a deck ordered on Face (so Ace SDHS, Two SDHS etc, which is handy for this purpose.
this.deck.resetDeck(1);
for (int i = 0; i < 4; i++) {
this.aces.add(this.deck.removeCard(0));
// Add a new pos for card
this.acesPos.add(0);
}
// Shuffle the deck
this.deck.shuffleDeck();
// Get 7 cards from the top of the deck to put on the
// side of the track and place them face down on the screen
for (int i = 0; i < 7; i++) {
this.hidden.add(this.deck.removeCard(0));
}
// Whilst the game is still running, pick a card from the deck and move the ace with the same suit
while (!this.game_ended) {
// Pick the next card from the deck
this.next = this.deck.removeCard(0);
cardDeck.setImageResource(getCardImage(next));
// Find the ace with the same suit as this.next and set it's 'score'++
for (Card ace : this.aces) {
if (ace.getSuit().equals(next.getSuit())) {
this.acesPos.set(ace.getSuit().ordinal(), this.acesPos.get(ace.getSuit().ordinal()) + 1);
break;
}
}
// Final state: the last ace now has a pos >7, this ace is the result. start new Activity
if (this.acesPos.get(next.getSuit().ordinal()) > 7) {
this.game_ended = true;
Intent winner = new Intent();
winner.putExtra("winner",next.getSuit().ordinal());
setResult(RESULT_OK, winner);
finish();
// If the ace is not at it's final position, move it
// and pick a new card in the next iteration.
} else {
// Move card
myHandler.postDelayed(new Runnable() {
public void run() {
Log.e("run: ", "moveCard");
moveCard();
}
}, 500);
}
}
}
Maybe Creating Something like this should work,
protected static void startTimer() {
timer.schedule(new TimerTask() {
public void run() {
mHandler.obtainMessage(1).sendToTarget();
}
}, 500);
}
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
myFunction();
}
};
Consider using a Timer, a polling model where the timer will run independently and execute the code in its associated TimerTask based on the frequency you define.
Timer timer = new Timer();
timer.schedule (new TimerTask()
{
#Override public void run()
{
// your program logic here
if (game_ended)
return;
// Pick the next card from the deck
next = deck.removeCard(0);
.
.
.
}
}, 0, 500);
This will start a timer immediately, with 500ms delay between triggers. Each time the timer fires the run() method in TimerTask will execute.
Create the timer in onCreate, start/schedule it in onResume, stop in onPause. Inside the TimerTask run() method you'd move your program logic.
moveCard() and wait to move to new intent:
while (!this.game_ended) {
// Pick the next card from the deck
this.next = this.deck.removeCard(0);
cardDeck.setImageResource(getCardImage(next));
// Find the ace with the same suit as this.next and set it's 'score'++
for (Card ace : this.aces) {
if (ace.getSuit().equals(next.getSuit())) {
this.acesPos.set(ace.getSuit().ordinal(), this.acesPos.get(ace.getSuit().ordinal()) + 1);
break;
}
}
// Final state: the last ace now has a pos >7, this ace is the result. start new Activity
if (this.acesPos.get(next.getSuit().ordinal()) > 7) {
this.game_ended = true;
myHandler.postDelayed(new Runnable() {
public void run() {
Intent winner = new Intent();
winner.putExtra("winner",next.getSuit().ordinal());
setResult(RESULT_OK, winner);
finish();
}
}, 500);
// If the ace is not at it's final position, move it
// and pick a new card in the next iteration.
} else {
// Move card
Log.e("run: ", "moveCard");
moveCard();
}
}
I'm trying to create an update function for multiple ImageView's but kept running into issues. I'm currently passing an individual ImageView to the updateBlobPosition function inside a timer, which works fine for one ImageView. But as soon I pass a second ImageView (blob2), now both ImageViews mimic each other as they have the same trajectory and velocity.
In addition, when one ImageView hits a wall and bounces back, the other one does the same. What I want is for each ImageView to be independent of each other. Any help would be appreciated.
...
blob1.setX(generateRandomNumber(0, 930));
blob1.setY(generateRandomNumber(0, 1750));
blob2.setX(generateRandomNumber(0, 930));
blob2.setY(generateRandomNumber(0, 1750));
...
//start the timer
timer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
playerMovement();
updateBlobPosition(blob1);
updateBlobPosition(blob2);
collision(blob1);
collision(blob2);
}
});
}
}, 0, 20);
...
protected ImageView updateBlobPosition(ImageView blob) {
blobX = blob.getX();
blobY = blob.getY();
blobX += blobVelocityX;
blobY += blobVelocityY;
//left
if ((blob.getX() + blobVelocityX < 0)) {
blobVelocityX = -blobVelocityX;
}
//right
else if (blob.getX() - blobVelocityX > 930) {
blobVelocityX = -Math.abs(blobVelocityX);
}
//top
if ((blob.getY() + blobVelocityY < -20)) {
blobVelocityY = -blobVelocityY;
}
//bottom
else if (blob.getY() - blobVelocityY > 1750) {
blobVelocityY = -Math.abs(blobVelocityY);
}
blob.setX(blobX);
blob.setY(blobY);
return blob;
}
You're using the same velocity variables for both. So of course they be updated in the same way. If you want them to move independently, give them independent speeds.
I have one problem. First - I HAVE AND I WILL HAVE JUST ONE BUTTON.
My problem is: How it can be for each button click different effect-animation.
So, when I first time click on button image goes down, then I second time click image goes from left to right, then I third time click image gone and then it repeat for fourth click image goes down(I can make animation left to right, animation down and then image gone.So I know how to do animation (effects), but I just do not know how to create for every click different effect on image)...
first read about ActioListeners
then simply create a listener that uses a counter!
Each time you click the button the listener increases that counter. And you use different animations based on the current value of the counter. When the "last" animation took place - simply reset the counter to start from scratch.
Here you go
public class ButtonCycle extends JPanel {
private int counter = 0;
public ButtonCycle() {
JButton btn = new JButton("Next");
btn.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseClicked(MouseEvent e) {
switch(counter) {
case 0:
// "Go down"-animation code here
System.out.println("Go down");
counter++;
break;
case 1:
// "Left->right"-animation code here
System.out.println("Left->right");
counter++;
break;
case 2:
// "Disappearing"-animation code here
System.out.println("*poof*, now I'm gone");
counter = 0;
break;
}
}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
});
add(btn);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Button cycling through animations");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setPreferredSize(new Dimension(250,250));
f.setContentPane(new ButtonCycle());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
You could utilize the java.util.Random class along with the Random.nextInt() method against values from 1 to 4 within the ActionPerformed event of your Button. Then, depending upon which value is randomly produced run a specific animation method, perhaps through a switch/case or if statements...whatever. Here is an example:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Random rnd = new Random();
int value = (rnd.nextInt(4) + 1);
switch (value) {
case 1:
imageDown();
break;
case 2:
imageLeftRight();
break;
case 3:
imageGone();
break;
case 4:
imageUp();
break;
}
}
Or if you prefer, you could use the Math.random() method., for example:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int value = 4 + (int)(Math.random() * (((1 - 2) - 4) + 1));
switch (value) {
case 1:
imageDown();
break;
case 2:
imageLeftRight();
break;
case 3:
imageGone();
break;
case 4:
imageUp();
break;
}
}
Of course there will be times when the random number generated will be the very much the same as the number generated previously but then again you could use a class field to hold the previous random value and if the currently generated value is the same as the previously generated value then you can generate another within perhaps a do/while loop, for example:
private int previousValue = 0;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int value = 0;
Random rnd = new Random();
do {
value = (rnd.nextInt(4) + 1);
} while (previousValue == value);
previousValue = value;
switch (value) {
case 1:
imageDown();
break;
case 2:
imageLeftRight();
break;
case 3:
imageGone();
break;
case 4:
imageUp();
break;
}
}
I am having trouble delaying the method assign_backgrounds() within a for loop. I am trying to create a Simon says game, but instead of delaying and showing the next button that "Simon" presses, it shows all the buttons at once. Any help here would be greatly appreciated. Thanks.
boolean simonsTurn = true;
int x = 4;
int s;
int delay = 1000;
int array_values[] = new int[]{1,2,3,4};
public void simonSays() {
// running = true;
if (simonsTurn == true) {
go();
for (int i = 0; i < x; i++) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
go();
}
}, 1000);
}
}
}
public void go(){
s = random_int_between(0,3);
assign_backgrounds(array_values[s]);
}
public void assign_backgrounds( int array_values ){
Handler handler = new Handler();
if( array_values == 1){
button1_.invalidate();
button1_.setBackgroundResource(R.drawable.goatclicked);
button1_.refreshDrawableState();
handler.postDelayed(new Runnable(){
public void run(){
button1_.invalidate();
button1_.setBackgroundResource(R.drawable.goat);
button1_.refreshDrawableState();}}, 1000);
}
else if( array_values == 2){
button2_.invalidate();
button2_.setBackgroundResource(R.drawable.pigclicked);
button2_.refreshDrawableState();
handler.postDelayed(new Runnable(){
public void run(){
button2_.invalidate();
button2_.setBackgroundResource(R.drawable.pig);
button2_.refreshDrawableState();}}, 1000);
}
else if( array_values == 3){
button3_.invalidate();
button3_.setBackgroundResource(R.drawable.chickenclicked);
button3_.refreshDrawableState();
handler.postDelayed(new Runnable() {
public void run() {
button3_.invalidate();
button3_.setBackgroundResource(R.drawable.chicken);
button3_.refreshDrawableState();}}, 1000);
}
if( array_values == 4) {
button4_.invalidate();
button4_.setBackgroundResource(R.drawable.cowclicked);
button4_.refreshDrawableState();
handler.postDelayed(new Runnable(){
public void run(){
button4_.invalidate();
button4_.setBackgroundResource(R.drawable.cow);
button4_.refreshDrawableState();}}, 1000);
}
}
It's because you are creating handlers very fast and then they are all starting at the same time. You should look into how Handler's work and about Asyncronous/Background tasks.
Now back to your problem, you are calling the a loop and it is creating handlers all in a row and they are being created very fast (nanoseconds). They will then all launch 1 second from that creation time because of your postDelayed() call. This is why everything is popping up at the same time! All of these delay posts are being executed at almost the same time on concurrent background threads.
Instead of a for(int i,...) loop you want to have a global int i, just add it to the top of the file.
At the end of any of Simon's turn you'll want, inside of the if, else if statement inside assign_background (at the end of the Runnables, then you'll want to call go().
This might cause problems because you are trying to access the main thread from all these background threads. so you might have to call the function runOnMainUIThread() as a quick hack when you call the go function.
All in all, you are going to have some problems until you understand Handlers, Background Processes, and Threads. Definitely great knowledge to learn about and Android has solid documentation on it just FYI.
Can you guys tell me how can I execute function updateTiles() ONLY after all nodes' actions in function slideTiles(String s) are finished? I tried adding some while loop but it freezes whole program. After running functions one after another the animations just don't show up.
public boolean ccTouchesEnded(MotionEvent event)
{
CGPoint location = CCDirector.sharedDirector().convertToGL(CGPoint.ccp(event.getX(), event.getY()));
x2=location.x; y2=location.y;
float diffY = y1-y2;
float diffX = x1-x2;
if (Math.abs(diffX)>Math.abs(diffY)&&diffX<0)
move="right";
else if (Math.abs(diffX)>Math.abs(diffY)&&diffX>=0)
move="left";
else if (Math.abs(diffX)<=Math.abs(diffY)&&diffY<0)
move="up";
else if (Math.abs(diffX)<=Math.abs(diffY)&&diffY>=0)
move="down";
int row=(int)(y1/TILE_SQUARE_SIZE);
int column=(int)(x1/TILE_SQUARE_SIZE);
slideTiles(move);
//wait for animations to finish
int sum=0;
boolean actionDone=false;
while (!actionDone)
{
for (CCNode c : tilesNode.getChildren())
{
sum+=c.numberOfRunningActions();
}
//String s=sum+"";
//statusLabel.setString(s);
if (sum==0){
actionDone=true;
break;
}
sum=0;
}
updateTiles();
return true;
}
//////
public void slideTiles(String move)
{
// Increment the moves label and animate the tile
CCBitmapFontAtlas moveslabel = (CCBitmapFontAtlas) getChildByTag(MOVES_LABEL_TAG);
moves++;
moveslabel.runAction(CCSequence.actions(
//CCDelayTime.action(0.25f),
CCScaleTo.action(0.2f, 6/5f),
//CCDelayTime.action(0.25f),
CCScaleTo.action(0.2f, 5/6f)
));
moveslabel.setString("Moves:\n " + CCFormatter.format("%03d", moves ));
if (move.equals("up"))
{
for (int start=NUM_COLUMNS; start<2*NUM_COLUMNS; start++)
for (int y=start; y<NUM_COLUMNS*NUM_ROWS; y+=NUM_COLUMNS)
{
if (tileNumbers[y]!=EMPTY)
{
for (int f=start-NUM_COLUMNS; f<y; f+=NUM_COLUMNS)
{
if (tileNumbers[f]==EMPTY)
{
//y->f
CGPoint moveTo = tilesNode.getChildByTag(f).getPosition();
CCMoveTo movetile = CCMoveTo.action(0.25f, moveTo);
CCSequence movetileSeq = CCSequence.actions(movetile);//CCCallFunc.action(this,"stopAction"));
tilesNode.getChildByTag(y).runAction(movetileSeq);
tileNumbers[f]=tileNumbers[y];
tileNumbers[y]=EMPTY;
break;
}
}
}
}
}
Edit: Is this kind of solution correct? It works surprisingly well.
public boolean ccTouchesEnded(MotionEvent event)
{
...
...
slideTiles(move);
float time = 0.25f+0.05f; //0.25f is time of moveTo action for each tile
CCDelayTime delay = CCDelayTime.action(time);
CCCallFunc cA = CCCallFunc.action(this, "updateTiles");
CCSequence se = CCSequence.actions(delay, cA);
runAction(se);
return true;
}
Generally, you shouldn't use while loop to wait in UI thread , it will cause big problem you have saw it.
You should use Thread to do this waiting job.
new Thread(new Runnable() {
#Override
public void run() {
while(true){
//here you wait forever doesn't matter the UI thread.
if(everythingDone) {//here to add your condition
new Handler().post(new Runable() {
#Override
public void run() {
//Here you can do what you want and will be done in UI Thread
}
});
}
}
}
}).start();