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);
}
Related
I'm building a camera app with Camera2 API, relatively new to Android development. Everything is working just working out the bugs. But I have a switch camera button, going from the front camera to the back or vise-versa. If the user continuosly presses the button, the app will crash. Trying to set it up in a way that it finishes everything it needs to do before the button can be used again.
I have the button set to enabled, but after press, it disables the button until everyting finishes, then renables, but that doesn't seem to work:
//The button to switch the camera to front and back camera.
mChangeCamera = (ImageButton) findViewById(R.id.change_camera);
mChangeCamera.setEnabled(true);
mChangeCamera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mChangeCamera.setEnabled(false);
closeCamera();
// stopBackgroundthread();
if (mTextureView.isAvailable()) {
setUpCamera(mTextureView.getWidth(), mTextureView.getHeight());
transformImage(mTextureView.getWidth(), mTextureView.getHeight());
connectCamera();
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
mChangeCamera.setEnabled(true);
}
});
there has to be a simple way to do this, but not finding anyting from searchs. Anyone know how I can set it up not to crash when the user smashes the button?
Alight, so I ended up figuring out how to do this. You can use a handler with a post delay like so:
mChangeCamera = (ImageButton) findViewById(R.id.change_camera);
mChangeCamera.setEnabled(true);
mChangeCamera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mChangeCamera.setEnabled(false);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mChangeCamera.setEnabled(true);
}
},1000);
closeCamera();
// stopBackgroundthread();
if (mTextureView.isAvailable()) {
setUpCamera(mTextureView.getWidth(), mTextureView.getHeight());
transformImage(mTextureView.getWidth(), mTextureView.getHeight());
connectCamera();
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
});
i did this by calculate time between multiple button presses...
create global variable in your class first
private long mLastClickTime = 0;
and a final int for duration between clicks
public static final int CLICK_TIME = 400;
then paste this code in on click of button
if (SystemClock.elapsedRealtime() - mLastClickTime < CLICK_TIME) {
return; //button pressed repeatedly so do nothing
}
mLastClickTime = SystemClock.elapsedRealtime();
// button is not pressed repeatedly so add your desired action
and of course you can increase the CLICK_TIME value if error still occurs
I am new to Android development so excuse me for this question.
So I have a button that when clicked, it will call a method called btnDelay(btnName).
Inside that method is this line of codes:
private void btnDelay(final Button btn){
btn.setEnabled(false);
/*if (counter == 0){
counter++;
}*/
Timer buttonTimer = new Timer();
buttonTimer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setEnabled(true);
}
});
}
}, 5000);
}
That will disable the button for 5 seconds.
Now what I want to do is when the user clicks the button again and the 5 seconds is not finished, will display a Toast stating that the user's action is too frequent.
Is there a way I can do this? I am thinking of using a counter that will count how many times the user clicked that specific button and will reset to 0 after the 5 seconds on the TimerTask is done. But is there a simplier way to do that? Thank you.
Your button won't fire an onClick event if it's disabled. So instead of disabling it, set the colours to grey or something so it looks disabled and then in your onClick handler for the button:
if(enabled){
btnDelay();
}
else {
sendAToast();
}
Then in btnDelay(), set enabled = false (and set the colours to grey if you want), and inside run() set enabled = true.
Also don't forget to private boolean enabled = true at the top of your class :)
You should declare a Boolean variable for button state. Because if you write btn.setEnabled(false); ,buttonClickEvent can not be triggered for five seconds.
boolean btnState = true;
private void btnDelay(final Button btn){
if (btnState){
Timer buttonTimer = new Timer();
buttonTimer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
btnState = false;
}
});
}
}, 5000);
}else{
Toast.makeText(this, "your_message", Toast.LENGTH_SHORT).show();
}
}
i am trying to make a button that when its clicked , it changes its color image and starts a countdowntimer in a method activeDelay() as here:
piscaAutoButton = (Button) rootView.findViewById(R.id.piscaAutoButton);
piscaAutoButton.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(final View view) {
if (sessionManager.getPisca()) {
sessionManager.setPisca(false);
trigger = false;
piscaAutoButton.setBackgroundResource(R.drawable.button_bg_round);
} else {
sessionManager.setPisca(true);
piscaAutoButton.setBackgroundResource(R.drawable.button_add_round);
trigger = true;
activeDelay(trigger);
}
here is my activeDelay method:
private boolean activeDelay(boolean trigger) {
while (trigger) { // LOOP WHILE BUTTON IS TRUE CLICKED
int timerDelay = manualControl.getDelayPisca(); //input for timer
//delay manual
new CountDownTimer(timerDelay * 1000, 1000) {
public void onFinish() {
System.out.println("sent");
try {
System.out.println("blink button " + manualControl.getBlinkButton());
if (!manualControl.getBlinkButton().isEmpty()) {
MenuActivity.mOut.write(manualControl.getBlinkButton().getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void onTick(long millisUntilFinished) {
}
}.start();
}
return trigger;
}
My problem is that i need the counter keeps going after finished, stopping just when the user clicks again in the button (trigger = false). I am having problems to program that, if someone could help,i know the return inside activeDelay ejects from the method, how can we solve that ,tks
I would suggest you to don't use CountDownTimer(this runs for some specific time period) , instead of this you should use Handler(this run infinitely) . i am sending you handler code.
private Handler handler = new Handler();
//call this when you want to start the timer .
handler.postDelayed(runnable, startTime);
Runnable runnable = new Runnable() {
#Override
public void run() {
// Do here , whatever you want to do(show updated time e.t.c.) .
handler.postDelayed(this, xyz); //xyz is time interval(in your case it is 1000)
}
};
//Stop handler when you want(In your case , when user click the button)
handler.removeCallbacks(runnable);
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.
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.