I'm trying to make a tictactoe game using Android Studio. I already write a code to set the button visibility when someone is win (and it work). I also want the game to show "play again" button when the board is full but no one win (tie). How do i do that.
Here's what I'm trying to code:
public boolean checkGameState () {
boolean isBoardFull = false;
androidx.gridlayout.widget.GridLayout gridLayout = findViewById(R.id.gridLayout);
for (int i = 0; i < gridLayout.getChildCount(); i++) {
ImageView reset = (ImageView) gridLayout.getChildAt(i);
if(reset.getDrawable() != null) {
isBoardFull = true;
}
}
return isBoardFull;
}
Here's a screenshot of what the game look like:
as you can see in the image, the "Play again" button is visible even though the game hasn't been finished yet. The button will show if the someone is win or tie (the board is full).
There is slight mistake with your condition as once it found drawable it marks as isBoardFull as true, Which is wrong as all child haven't been checked yet so marking isBoardFull as true at this stage is wrong.
You can do like following:
public boolean checkGameState () {
boolean isBoardFull = true; // first mark it as full
androidx.gridlayout.widget.GridLayout gridLayout = findViewById(R.id.gridLayout);
for (int i = 0; i < gridLayout.getChildCount(); i++) {
ImageView reset = (ImageView) gridLayout.getChildAt(i);
if(reset.getDrawable() == null) {
isBoardFull = false; // if drawable not found that means it's not full
}
}
return isBoardFull;
}
So now first it will mark board as full but once any drawable is found as null it will mark board as empty.
I think you should initialize a variable isEveryChildChecked = true before iterating the gridLayout child.
While iterating the children, if a grid is not checked, set the field isEveryChildChecked = false.
Then after iteration, check the field isEveryChildChecked, If it is true, you can display play again else do nothing or hide the Play again button.
Related
So I am facing a weird bug I cannot explain - I cannot even reproduce it sometimes.
Basic context:
I have an application, which lists objects. Every object has a name and a point value. For every object, the addCustomSpinner function creates a "ticket" (a custom view, kind-of-spinner) and shows them in a scrollview so the user can select the one needed. There are four different 'containers' for four different kind of objects - so the layout can be populated with four kind of "ticket" package.
The data for the objects are collected from a database. The addCustomSpinner is called with a for cycle for every object in the database, and - Important - before the for method, the Layout it populates with the tickets is cleared (removeAllViews).
Inside addCustomSpinner, everything is created as "new" - like the button in question.
addCustomSpinner creates this button and adds a new onClickListener. Inside onClickListener, a new boolean is created - this is used to show a different animation when the button is clicked again. On first click (boolean = true), the arrow turns 180 degrees and faces upwards, on second click (boolean = false) the arrow turns 180 degrees and faces downwards. Works like a charm, until...
The bug I am facing:
Sometimes - as I already mentioned, not every time - if I click the button for one "ticket", then leave it 'opened' and click on an another one, and leave it 'opened' also, THEN I choose to populate the layout with a different kind of "ticket" package - The arrow faces upwards by default on every ticket in every package! Sometimes - again, just sometimes - with the same pattern I can turn it back, but it happens just "by accident".
I don't understand how the animation and state of the buttons can be connected, if every created ticket is new, every button is new, every onClickListener is new, and every boolean inside onClickListener is new. And if these are connected somehow, then why can that be that every behavior is "unique" for the buttons, nothing else shows any connection - even this is just a "sometimes" bug, a pretty rare one.
Can anybody help me why this happens?
What I tried:
Well, tried to trace the issue - but since it happens just by accident, I have no clue, I just searched if I can do anything else than the boolean to add different animation for the clicks. Sadly using ObjectAnimator is not a good solution for me - not the same result at least, since my animated arrow not only rotates, but it also changes its color. Shapeshifter seemed like a good idea to create animations easily, but now as I see it, maybe a simple rotation will be my ultimate solution.
Here's the code for the button:
customButton.setOnClickListener(new View.OnClickListener() {
boolean isCustomButtonClicked = true;
#Override
public void onClick(View v) {
if (isCustomButtonClicked) {
customButton.setImageResource(R.drawable.avd_anim_arrow_blue_back);
Drawable d = customButton.getDrawable();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (d instanceof AnimatedVectorDrawable) {
animArrowAnim = (AnimatedVectorDrawable) d;
animArrowAnim.start();
}
}
routeWhoClimbed.setVisibility(View.VISIBLE);
isCustomButtonClicked = false;
} else if (!isCustomButtonClicked) {
customButton.setImageResource(R.drawable.avd_anim_arrow_blue);
Drawable d = customButton.getDrawable();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (d instanceof AnimatedVectorDrawable) {
animArrowAnim = (AnimatedVectorDrawable) d;
animArrowAnim.start();
}
}
routeWhoClimbed.setVisibility(GONE);
isCustomButtonClicked = true;
}
}
});
EDIT:
The full addCustomSpinner():
private void addCustomSpinner(Routes mRouteItemToAdd, String placeName) {
//creating a new View for my custom layout created in xml
View customRoutesView = new View(this);
LinearLayout.LayoutParams customViewParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
customRoutesView.setLayoutParams(customViewParams);
customRoutesView = LayoutInflater.from(this).inflate(
R.layout.custom_view_layout, routeLayout, false
);
//Setting up the views inside the custom view
ImageView imageViewDiffImage = customRoutesView.findViewById(R.id.routeDiffImageView);
TextView textViewRouteName = customRoutesView.findViewById(R.id.routeNameTextView);
TextView textViewRouteDiff = customRoutesView.findViewById(R.id.routeDiffTextView);
ImageButton customButton = customRoutesView.findViewById(R.id.customButton);
RadioButton climberNameOne = customRoutesView.findViewById(R.id.climberNameOne);
RadioButton climberNameTwo = customRoutesView.findViewById(R.id.climberNameTwo);
Button climbedItButton = customRoutesView.findViewById(R.id.climbed_it_button);
RadioGroup climberNameRadioGroup = customRoutesView.findViewById(R.id.climberNameRadioGroup);
RadioGroup climbingStyleRadioGroup = customRoutesView.findViewById(R.id.styleNameRadioGroup);
RelativeLayout routeWhoClimbed = customRoutesView.findViewById(R.id.routeWhoClimbedRelativeLayout);
imageViewDiffImage.setImageResource(R.mipmap.muscle);
textViewRouteName.setText(mRouteItemToAdd.name);
textViewRouteDiff.setText("Difficulty: " + (int) mRouteItemToAdd.difficulty);
climberNameOne.setText(climberName1);
climberNameTwo.setText(climberName2);
routeWhoClimbed.setVisibility(GONE);
//Here comes the button with the animated image
customButton.setOnClickListener(new View.OnClickListener() {
boolean isCustomButtonClicked = true;
#Override
public void onClick(View v) {
if (isCustomButtonClicked) {
customButton.setImageResource(R.drawable.avd_anim_arrow_blue_back);
Drawable d = customButton.getDrawable();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (d instanceof AnimatedVectorDrawable) {
animArrowAnim = (AnimatedVectorDrawable) d;
animArrowAnim.start();
}
}
routeWhoClimbed.setVisibility(View.VISIBLE);
isCustomButtonClicked = false;
} else if (!isCustomButtonClicked) {
customButton.setImageResource(R.drawable.avd_anim_arrow_blue);
Drawable d = customButton.getDrawable();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (d instanceof AnimatedVectorDrawable) {
animArrowAnim = (AnimatedVectorDrawable) d;
animArrowAnim.start();
}
}
routeWhoClimbed.setVisibility(GONE);
isCustomButtonClicked = true;
}
}
});
//Button, works like an 'OK' or something, and I have no
//problem with this
climbedItButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int checkedNameButton = climberNameRadioGroup.getCheckedRadioButtonId();
int checkedStyleButton = climbingStyleRadioGroup.getCheckedRadioButtonId();
RadioButton checkedNameRadioButton = (RadioButton) findViewById(checkedNameButton);
RadioButton checkedStyleRadioButton = (RadioButton) findViewById(checkedStyleButton);
String checkedName = (String) checkedNameRadioButton.getText();
String checkedStyle = (String) checkedStyleRadioButton.getText();
addClimbToDatabase(user.getUid(), checkedName, mRouteItemToAdd, placeName, checkedStyle);
}
});
//And finally, I add this new "ticket" with the custom view to the layout i want to show it. Again, this also works like a charm, no problem here.
routeLayout.addView(customRoutesView);
}
Ultimately, I did not manage to understand the problem throughly, but I was able to eliminate it.
So during my fixing tries I narrowed down the problem to the animated drawable state - credit to #avalerio for his pro tip, but the answer wasn't addig an id to the button. I think somehow and sometime, the state of the first animation (turning the arrow 180 degrees) stuck in the end position - causing the other views using this animatedDrawable showing it in end position on start.
.reset() did not help, since it resets the animatedVectorDrawable object, not the animation xml drawable state. My solution is a kind of workaround, but it is working: when the custom-view 'ticket' is created with the animated-drawable-imagebutton, I set the imageResource of the button to a not-animated xml drawable - this drawable is basically the start position of my animated-drawable. This way, when the 'tickets' are generated, the imagebutton is 'hardcoded' in the start position.
Not elegant, but works. BUT(!) I would really appreciate if someone could explain to me how this weird behavior is possible - just sometimes, randomly, with no pattern I can reproduce intentionally.
I have a horizontal recyclerview which implements ItemTouchHelper callback for dragging and reordering cells. When a cell is being moved i want to shrink all of the cells widths so they all appear on screen. In onItemSelected() i can successfully change the size of the cell currently being moved, and revert back in onItemClear.
However, i want to resize all cells and not just the current cell. What is the best approach for this?
I tried creating a function in my adapter class and calling it to resize via notifyDataSetChanged() however it was removing the current cell being moved.
Is there a way to do this as part of my ItemTouchHelperCallback - creating a similar function as onItemSelected but updating all other cells?
I am not sure if I completely understand what you are trying to do, but I think this might help:
int childs = recyclerView.getChildCount();
for (int i = 0; i < childs; i++) {
View child = recyclerView.getChildAt(i);
if (child != null) {
RecyclerView.ViewHolder vholder = recyclerView.getChildViewHolder(child);
if (vholder != null) {
ViewHolder holder = (ViewHolder) vholder;
if (holder.view != null) {
LayoutParams params = (LayoutParams)holder.view.getLayoutParams();
params.width = <new_width>;
params.height = <new_height>;
holder.view.setLayoutParams(params);
}
}
}
}
Happy coding !!
I have a button that is a type Button that is gonna be clicked. When this is clicked, It is changing color to green.
When I click the button it changes color to green, but when I click it again, it should go back to the standard color.
I have 2 drawable files with names checked_list and not_checked_list.
These two are working good.
But when I click the button, the click has happened. And I can't click it again for some reason.
I have a Button field with a public void sendMessage method that is hooked to the buttons onClick. Is it better to just set an onClickEvent for the button in the code instead.
Here is the code I have so far.
int checked = 0;
Button gotIt;
gotIt = (Button)findViewById(R.id.got_it);
switch(checked) {
case 0:
gotIt.setBackgroundResource(R.drawable.checked_list);
checked = 1;
break;
case 1:
gotIt.setBackgroundResource(R.drawable.not_checked_list);
checked = 0;
break;
}
So here I want it to change between these two colors when I click it.
Any suggestions?
If all of the code you posted is inside your onClick method, then checked int is always 0 and will never be 1 because it is set in the first line of the method. Move your checked int outside of this method and it should work.
Setting click listener dynamically will have same result as setting in XML layout.
int checked = 0;
Button gotIt;
void sendMessage(View v) {
gotIt = (Button)findViewById(R.id.got_it);
switch(checked) {
case 0:
gotIt.setBackgroundResource(R.drawable.checked_list);
checked = 1;
break;
case 1:
gotIt.setBackgroundResource(R.drawable.not_checked_list);
checked = 0;
break;
}
}
You have to keep track of the last value for checked. Right now you are resetting it every time to 0 because it is a local field in your method call. Make checked as a class field and it will work as expected.
You can try this method
//global variables
boolean isChecked = true;
Button gotIt;
//put this in onCreate()
gotIt = (Button)findViewById(R.id.got_it);
gotIt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(isChecked == true){
gotIt.setBackgroundResource(R.drawable.checked_list);
isChecked = false;
}else{
gotIt.setBackgroundResource(R.drawable.not_checked_list);
isChecked = true;
}
}
});
I am programming a Memory game in java and I want to make a GameOver-Screen with an image from the sprites folder (I already have the image for it and it is an image with .jpg) after all cards are flipped. So when all cards are flipped, the game is actually over and this GameOver-Screen appears. I have no idea how I can do that. The only thing I know is that it should start with public void gameOver(){. Probably it is possible to make something with "if()" and between the brackets you could say that the condition is that all cards are flipped.
// Memory.java
import ch.aplu.jgamegrid.*; // Imports the Library of Gamegrid, on which this code is based
import ch.aplu.util.*;
public class Memory extends GameGrid implements GGMouseListener {
private boolean isReady = true;
private MemoryCard card1;
private MemoryCard card2;
public Memory() {
super(6, 6, 115, null, null, false);
MemoryCard[] cards = new MemoryCard[36];
for (int i = 0; i < 36; i++) {
if (i < 18)
cards[i] = new MemoryCard(i);
else
cards[i] = new MemoryCard(i - 18);
addActor(cards[i], getRandomEmptyLocation());
cards[i].show(1);
}
addMouseListener(this, GGMouse.lPress);
// Application thread used to flip back cards
doRun();
show();
while (true) {
// Wait until there is something to do
Monitor.putSleep();
delay(1000);
// Flip cards back
card1.show(1);
card2.show(1);
isReady = true;
// Rearm mouse events
setMouseEnabled(true);
}
}
// imports the GGMouse package
public boolean mouseEvent(GGMouse mouse) {
Location location = toLocation(mouse.getX(), mouse.getY());
MemoryCard card = (MemoryCard) getOneActorAt(location);
// Card already flipped->no action
if (card.getIdVisible() == 0)
return true;
// Show picture
card.show(0);
if (isReady) {
isReady = false;
card1 = card;
} else {
card2 = card;
// Pair found, let them visible
if (card1.getId() == card2.getId())
isReady = true;
else {
// Disable mouse events until application thread flipped back cards
setMouseEnabled(false);
Monitor.wakeUp();
}
}
return true;
}
public static void main(String[] args) {
new Memory();
}
}
Each time a card is flipped, check if all cards have been flipped. Use a loop - you already have one in your code. For each index in the for loop... ie each card... check if getIdVisible() is 0.
If the card at index i is still hidden, return null and continue running your program is normal. However, if you get all the way through the loop, that means all cards are visible, which means you can call the gameOver() method, which shows the game over screen.
Does that sound like it fits with your program?
Edit:
To check if all cards are flipped, one thing you can do is to make MemeryCard[] cards (up in the constructor) a global variable. Then you can just call this method every time you reveal two cards:
public boolean areAllCardsFlipped() {
// Start looking through each card
for (int i = 0; i < 36; i++) {
// We found a card that is NOT flipped. Stop here, because clearly all cards are not flipped.
if (cards[i].getIdVisible() != 0) {
return false;
}
}
// We have looked at all cards, and we know that each one is flipped. We can return true.
return true;
}
I'd say call this method in mouseEvent() before you return true for mouseEvent(), and if this returns true you can call the gameOver() method.
As for how to handle rendering the image... I don't know the api you're using for this game, so I can't tell you specifically how to do that. But at least with this code you should be able to trigger that gameOver.
Okay so I am pretty much doing a crash course through creating a java application and am having an issue with executing an action. The program is the 15 puzzle, the game where you slide one piece at a time and try to get all numbers in order, so I allow for an 'Auto' mode option that will solve the board for the user once clicked. So my code reads the solution from a text file which is working fine just none of the 'squares' (JButtons) move when I click the auto button. So i am not sure if I just don't understand the action even process completely or not. heres my code, I can supply more of it if necessary.
if (e.getSource() == ctrButtons[0]) {
System.out.println("Auto Mode started\n");
Scanner s = null;
try {
s = new Scanner(new BufferedReader(new FileReader("move_list.txt")));
int count = 0;
while (s.hasNext()) {
//Cycle through to move in move_list
if (count != 18) {
s.next();
count+=1;
}
else {
int cur_move = Integer.parseInt(s.next());
count = 0;
/*Use cur_move to move blank space accordingly
*UP------------3
*LEFT----------2
*RIGHT---------1
*DOWN----------0
*/
int zero_index = -1;
for (int j=0; j<jbnButtons.length; j++) {
if (Integer.parseInt(jbnButtons[j].getText()) == 0) {
zero_index = j;
break;
}
}
Point zero = jbnButtons[zero_index].getLocation();
//Check if move is up
if (cur_move == 3) {
Point next = jbnButtons[zero_index-4].getLocation();
jbnButtons[zero_index].setLocation(next);
jbnButtons[zero_index-4].setLocation(zero);
}
//Check if move is left
else if (cur_move == 2) {
Point next = jbnButtons[zero_index-1].getLocation();
jbnButtons[zero_index].setLocation(next);
jbnButtons[zero_index-1].setLocation(zero);
}
//Check if move is right
else if (cur_move == 1) {
Point next = jbnButtons[zero_index+1].getLocation();
jbnButtons[zero_index].setLocation(next);
jbnButtons[zero_index+1].setLocation(zero);
}
//Check if move is down
else {
System.out.println("Current move = 0");
Point next = jbnButtons[zero_index+4].getLocation();
jbnButtons[zero_index].setLocation(next);
jbnButtons[zero_index+4].setLocation(zero);
}
}
}
}
So my code executes when I click the 'Auto' button and I was printing output to the screen to see if it was looping through the code which it was, just none of the buttons move each time through the loop. Any ideas??
You code is executing on the Event Dispatch Thread. The GUI can't repaint itself until the code finishes executing, so you won't see the intermediate steps only the final location of each component.
Read tje section from the Swing tutorial on Concurrency for a more complete explanation.
Maybe you should use a Swing Timer (the tutorial also has a section on this). Each time the Timer fire you do the next move.