I've tried a hundred different methods to implement a delay between automated button clicks, including thread.sleep, Handler.postDelayed, and so on… It could be I have used it incorrectly somehow. My most recent attempt was with a simple boolean toggle. It seems that no matter how I try, all the buttons that are to be automatically clicked happen at the same time after the delay, INSTEAD of being delayed between clicks.
my code as it stands now:
setting up button onClickListener:
for (int i = 0; i < mDifficulty; i++) {
ButtonsOCLArray[i].setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
animating = true;
while (animating) {
animateButtons(v);
}
}
});
}
animation of buttons:
public static void animateButtons(View v) {
AlphaAnimation fadeInAnimation = new AlphaAnimation(0F, 1F);
fadeInAnimation.setDuration(1500);
fadeInAnimation.setFillAfter(true);
v.startAnimation(fadeInAnimation);
animating = false;
}
and finally, from a separate class, the automatic button setup:
public void pushAiButton(final View [] v){
mWhichButton = (mGameAi.getRandomNumber(MainActivity.mDifficulty)); // get random number for random button to press
mListOfAiButtonsToPress.add(mWhichButton); // add random number to mLOABTP
mListOfAiButtonsTemp.addAll(mListOfAiButtonsToPress); // add all elements of mLOABTP to mLOABT
boolean empty = false;
while (!empty) {
if (empty) {
break;
}
tempArrayIndex = mListOfAiButtonsTemp.get(0); // tempArray given value in first slot of mLOABT
mListOfAiButtonsTemp.remove(mListOfAiButtonsTemp.get(0)); // first slot of MLOABT removed
if (mListOfAiButtonsTemp.isEmpty()) {
// looped through whole list, empty now
empty = true;
} // end if
v[tempArrayIndex].performClick(); // click button at index *button*[*index*]
} // end !empty while
} // end pushAiButton()
any ideas HIGHLY appreciated! thanks!
UPDATE
This got it working:
mButtonStart.setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
Log.d(TAG, "START BUTTON CLICKED!");
if (firstRun) {
mGameAi.setupAiButtons();
firstRun = false;
}
v.setVisibility(View.INVISIBLE);
animateButtons(ButtonsOCLArray[mGameAi.getFirstButtonInList()]);
mGameAi.deleteFirstButtonInList();
v.postDelayed(new Runnable() {
public void run() {
if (!mGameAi.buttonsListIsEmpty()) {
v.performClick();
}
else {
v.setVisibility(View.VISIBLE);
firstRun = true;
}
}
}, 500);
System.out.println("end of mButtonStart's onclicklistener");
}
});
You have coded it so that they all click nearly simultaneously. The automatic button setup does a "while" loop that iterates through all the buttons - removing them one at a time nearly simultaneously.
In other words, your while loop iterating through the buttons needs to pause (or not queue another click) until the animation is complete.
Here is the problem said another way; when each "onClick" occurs, the boolean "animateButtons" is true and they all enter into the animateButtons method.
You need to have a thread with a wait call on in pushAiButton and wait for each button to finish its animation.
Related
I am programming a darts counter app and at the moment I am trying to get an user input (which field on the dart board they hit) by pressing on a specific button.
Each button press will return an int which will be used to update list values that are used by my adapter to then update the views.
The method that should happen in looks like this:
private void startRound(MatchActivityPlayerAdapter adapter) {
for (int playerIndex = 0; playerIndex < getMatchParticipants().size(); playerIndex++) {
for (int currentDart = 1; currentDart <= maximumDartsToThrow; currentDart++) {
// Here I want the activity to "wait" until the user presses a button
if (pointsButtonClicked) {
setDartValue(playerIndex, currentDart);
setDartsCombinedValues(playerIndex, currentDart);
setRemainingPointsValues(playerIndex, currentDart);
adapter.notifyDataSetChanged();
}
}
}
setCurrentRound(getCurrentRound() + 1);
}
Since I cant really stop the activity at the point mentioned above until user has made an input I'll propably have to include a while loop. But in order to do this I think I'll have to create a second thread and handle things differently. That actually overwhelmed me, even though I've been reading through this.
Can anyone please explain to me how I have to design my code in order to achieve what i want?
Edit: Actually pointsButtonClicked is a boolean.
I have over 20 buttons in the global OnClick method and whenever one of them is clicked pointsButtonClicked will be set to true.
Button button1 = findViewById(R.id.btn1);
button1.setOnClickListener(v -> {
outputInt = 1;
pointsButtonClicked = true;
});
Button button2 = findViewById(R.id.btn2);
button2.setOnClickListener(v -> {
outputInt = 2;
pointsButtonClicked = true;
});
// [...]
I think that there should be a better way to this without the while-wait loop.
First of all I suppose that you used an android.widget.Button to implement that pointsButtonClicked
Here's what I would do:
//Global in your activity
int playerIndex = 0;
int currentDart = 1;
Then
private void startRound(MatchActivityPlayerAdapter adapter)
{
pointsButtonClicked.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
setDartValue(playerIndex, currentDart);
setDartsCombinedValues(playerIndex, currentDart);
setRemainingPointsValues(playerIndex, currentDart);
adapter.notifyDataSetChanged();
nextThrow();
}
});
}
And finally
//Also in your activity
private void nextThrow()
{
if (currentDart == maximumDartsToThrow)
{
currentDart = 1;
if (playerIndex == getMatchParticipants().size()-1)
{
playerIndex = 0;
setCurrentRound(getCurrentRound() + 1);
}
else
{
++playerIndex;
}
}
else
{
++currentDart;
}
}
It can be better
You can use private variables and access them with getter and setter
Explained
Using this approach you do not create a thread that waits for every single button pressed. You simply create a button that listen for every click event that it receives and then you apply the logic to cycle the players and the darts for every players.
I hope this could be helpful.
[EDIT]
In this case I would apply the same OnClickListener to every button you use.
First of all I would create an inner class that implements OnClickListener and allows you to manage outputInt variable.
//Also in Activity (it is an inner class)
public class MyOnClickListener implements View.OnClickListener
{
private int myOutputInt;
public MyOnClickListener (int outputInt)
{
this.myOutputInt = outputInt;
}
#Override
public void onClick(View v)
{
pointsButtonClicked = true;
outputInt = myOutputInt;
setDartValue(playerIndex, currentDart);
setDartsCombinedValues(playerIndex, currentDart);
setRemainingPointsValues(playerIndex, currentDart);
nextThrow();
}
}
And then you create 20 MyOnClickListener passing only the int you want to assign. For example:
button1.setOnClickListener(new MyOnClickListener(1));
button2.setOnClickListener(new MyOnClickListener(2));
button3.setOnClickListener(new MyOnClickListener(3));
etc etc...
Can someone help me to understand what is happening here? Have been trying to debug, but feel like stuck!
I am trying to animate some online images in my Android app using the following method.
private void animateImages() {
// URL loading
// int i = 1; (initialized earlier)
// ArrayList<String> myImages = new ArrayList<>(); (initialized earlier)
myImages.clear();
While (i < 11) {
// Adds ten images using web link
myImages.add("My_web_url");
i++;
}
AccelerateInterpolator adi = new AccelerateInterpolator();
try {
Field mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
mScroller.set(viewPager, new MyScroller(getApplicationContext(), adi, 1));
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
if (viewPager != null) {
viewPager.setAdapter(new MyPageAdapter(getApplicationContext(), myImages));
}
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
// Printing variables for debugging
System.out.println("The page number is=" + currentPage);
System.out.println("The myImages size is=" + myImages.size());
public void run() {
if (currentPage == myImages.size() - 1) {
currentPage = 0;
}
viewPager.setCurrentItem(currentPage++, true);
}
};
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
// delay and period can be initialized as desired
}, delay, period);
}
}
When I call this method in OnCreate, animation works fine. However, when I call this method in OnClickButton Listener, variable myImages size (before public void run()) become zero and due to this animation doesn't work.
In the above, MySCroller and MyPageAdapeter are java classes. But, most likely, the issue is related to button click, and I don't understand why it resets the myImages size which halts the animation!
This is how button click listener is called. What am I doing wrong?
MyButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
animateImages();
}
});
Edit 1:
Thanks to all the comments, I made a little progress.
I moved all these variables from MainActivity to animateImages() method. The animation runs with button click as well but there is a bump in animation, where too images moves too fast then bump and so on..
// Added just before while loop
DELAY_MS = 1000;
PERIOD_MS = 1000;
i = 1;
currentPage = 0;
I notice the same animation bump if I move the URL loading while loop to OnCreate().
The second time you call animateImages it clears myImages but then doesn't loop because i is not reset so it remains empty. Move creation of that list to onCreate instead to avoid that issue.
So I created a small app with a play and a stop button. When I press the play button it should loop until I press the stop button. The problem is, once I press the play button, I can't do anything else and the stop button is not responsive. If I try to press the stop button my app says app doesn't respond and quit. I don't know why this is happening and I'm super new to Android and Java so this is a bit complicated for me to know why it doesn't work. Here is my actual code :
Play and stop method :
public void play() {
playing = true;
while (playing) {
for (int i = 0; i < 4; i++) {
if (buttonArray[i].isChecked()) {
sp.play(snareId, 1, 1, 1, 0, 1);
}
if (!playing) {
break;
}
try {
Thread.sleep(1000);
if(i == 3) i = -1;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void stop(){
playing = false;//
}
Main activity
playButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
mySample.play();
}
});
stopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
mySample.stop();
}
});
According Android document, there are some common patterns to look for when diagnosing ANRs:
The app is doing slow operations involving I/O on the main thread.
The app is doing a long calculation on the main thread.
The main thread is doing a synchronous binder call to another
process, and that other process is taking a long time to return.
And others.
You are in situation number 2 because you create an infinite loop while(playing) in main thread (it's UI thread in this case). Just listen to changes from check boxes and do your task respectively.
public void play(){
//call startService to play sound
//for each toggle button
buttonArray[i].setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
//do your tasks
} else {
//do your tasks
}
}
});
}
public void stop(){
//call stopService to play sound
//for each toogle button:
buttonArray[i].setOnCheckedChangeListener(null);
}
I am using android eclipse for programming.
I will use this basic calculation to fit to what I want to happen.
Here:
I got two buttons add and minus. if i press add it will obviously call the method add.
But my problem is. if I will keep pressing add button. It will keep adding multiple times and
if I will click 2 buttons at the same time it will also do add and minus. What I want is that
if i click both button at the same time there's priority that add button will execute first
and the minus button will not send data.
Add(){
a = b + c;
}
Minus(){
a = b - c;
}
public void add(View view){
Add();
}
public void subtract(View view){
Minus();
}
Just set button_minus.setClickable(false); in Add method and
button_add.setClickable(false); in Minus method. Then enable them back.
try:
private boolean isAddButtonPressed;
private void setListener(){
buttonAdd.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
isAddButtonPressed = true;
else
isAddButtonPressed = false;
return false;
}
});
}
public void add(View view){
doMath(true);
}
public void subtract(View view){
if(!isAddButtonPressed)
doMath(false);
}
// "synchronized" means that this method can ran only one time at a time
private synchronized void doMath(boolean isAdd) {
if(isAdd) {
Add();
} else {
Minus();
}
}
I try to use this code to prevent multi-click in ImageView but it doesn't help.
Boolean isClicked = false;
#Override
public void onClick(View v)
{
if (v == imgClick && !isClicked)
{
//lock the image
isClicked = true;
Log.d(TAG, "button click");
try
{
//I try to do some thing and then release the image view
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
isClicked = false;
}
}
In the log cat, I can see 5 lines "button click" when I click on ImageView for 5 times as quickly as possible. I can see the log cat print the first line, wait for a while (2 seconds) and then print the next line. I think when I click the ImageView, the fired event is moved to queue in order, isn't it?. So how can I stop that?
I also try to use setEnable() or setClickable() instead of isClicked variable but it doesn't work too.
Just try this working code
Boolean canClick = true; //make global variable
Handler myHandler = new Handler();
#Override
public void onClick(View v)
{
if (canClick)
{
canClick= false; //lock the image
myHandler.postDelayed(mMyRunnable, 2000);
//perform your action here
}
}
/* give some delay..*/
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run()
{
canClick = true;
myHandler.removeMessages(0);
}
};
Instead of sleeping in 2 seconds, I use some task like doSomeThing() method (has accessed UI thread), and I don't know when it completed. So how can I try your way?
//I referred this android link. You can handle thread more efficiently but i hope below code will work for you..
//you try this and
Boolean canClick = true; //make global variable
public void onClick(View v) {
if(canClick){
new DownloadImageTask().execute();
}
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
Log.d("MSG","Clicked");
canClick =false;
//perform your long operation here
return null;
}
protected void onPostExecute(Bitmap result) {
canClick =true;
}
}
You could keep track of the last consumed click upon your View, and based on it either perform the necessary actions, or simply return:
private long calcTime;
private boolean isClickedLately(final long millisToWait)
{
if (System.currentTimeMillis() - calcTime < millisToWait)
return true;
return false;
}
#Override
public void onClick(View v)
{
if (isClickedLately(2000))
return;
calcTime = System.currentTimeMillis();
Log.d(TAG, "consuming button click");
// perform the necessary actions
}
With the millisToWait parameter you can adjust the threshold of "waiting", but if you know that you want to wait exactly 2 seconds between two consecutive clicks, you can eliminate it.
This way you don't have to deal with Threads, which is good, since it's not a great idea to make the gui thread wait.