Android: Prevent multiple onClick events on a button (that has been disabled) - java

A button triggers an action that should only be invoked once. The button is disabled and hidden in the onClick handler before the action is performed:
someButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
someButton.setEnabled(false);
someButton.setClickable(false);
someButton.setVisibility(View.GONE);
performTaskOnce();
}
});
private void performTaskOnce() {
Log.i("myapp", "Performing task");
//Do something nontrivial that takes a few ms (like changing the view hierarchy)
}
Even though the button is disabled immediately, it is nonetheless possible to trigger multiple "onClick" events by tapping multiple times very quickly. (i.e. performTaskOnce is called multiple times). Is seems that the onClick events are queued before the the button is actually disabled.
I could fix the problem by checking in every single onClick handle whether the corresponding button is already disabled but that seems like a hack. Is there any better way to avoid this issue?
The problem occurs on Android 2.3.6, I cannot reproduce it on Android 4.0.3. But given the rarity of 4.x devices it is not an option to exclude older devices.

You could set a boolean variable to true when the button is clicked and set it to false when you're done processing the click.
This way you can ignore multiple clicks and not having to disable the button possibly avoiding annoying flickering of the button.
boolean processClick=true;
someButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(processClick)
{
someButton.setEnabled(false);
someButton.setClickable(false);
someButton.setVisibility(View.GONE);
performTaskOnce();
}
processClick=false;
}
});
private void performTaskOnce() {
Log.i("myapp", "Performing task");
//Do something nontrivial that takes a few ms (like changing the view hierarchy)
}

In the interest of keeping DRY:
// Implementation
public abstract class OneShotClickListener implements View.OnClickListener {
private boolean hasClicked;
#Override public final void onClick(View v) {
if (!hasClicked) {
onClicked(v);
hasClicked = true;
}
}
public abstract void onClicked(View v);
}
// Usage example
public class MyActivity extends Activity {
private View myView;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView.setOnClickListener(new OneShotClickListener() {
#Override public void onClicked(View v) {
// do clicky stuff
}
});
}
}

Bit late but this might be of use to someone. In my case I am calling another activity so;
Declare a boolean;
boolean clickable;
In the click listener;
if(clickable){
// Launch other activity
clickable = false;
}
Enable when onResume is called;
#Override
public void onResume() {
Log.e(TAG, "onResume");
super.onResume();
clickable = true;
}

You can use RxView(com.jakewharton.rxbinding2.view.RxView) is an extension around RxJava that created by Jake Wharton.
To integrate it to project you should use implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'
Simple Java usage:
RxView.clicks(yourButton)
.sample(500, TimeUnit.MILLISECONDS)
.subscribe { action() }
In Kotlin you can create extension function to handle your clicks:
View.singleClick(action: () -> Any) {
RxView.clicks(this)
.sample(500, TimeUnit.MILLISECONDS)
.subscribe { action() }
}
Sample:
Kotlin
yourButton.singleClick({
//do some stuff here
})
Java
SingleClickListenerKt.singleClick(yourButton, () -> {
doSomeStuff();
return null;
});
Note: you can use any RxJava operators like debounce, map, first, etc if you wish.

declare a varieble
and use it as
boolean boo = false;
someButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(boo==false){
someButton.setEnabled(false);
someButton.setClickable(false);
someButton.setVisibility(View.GONE);
boo = true;
}
}
});
by this you prevent multiple clicks on your button
hope it help

Related

switch between different listener

I'm using a gesture listener to monitor user's action, but when the app pops up the dialog, I don't know how to switch my gesture listener to dialog event and handle the event button (ok and cancel), can anyone give me a suggestion?
Pseudo code likes this
public class MainActivity extends FragmentActivity
implements ConnectionEventListener{
......
// when connection established,
// pop a diaglog (android native diaglog with listview and its adapter) to ask user to select ok or cancel button
#Override
public void onUpdateAlert(final int event, final String message){
}
// gesture listener
// if a dialog pops up, the pose can be used to select OK or cancel
#Override
public void onDetected(Hand pose){
}
}
The problem I have is not the button listener. Actually, I have two listeners work at the same time, one for event monitor and another for pose monitor. When an event comes, the event will pop up a dialog to select "ok" or "cancel" . In the mean time, a pose listener still works. I'd like to know when this case happens, how can I use the pose listener to select "ok" or "cancel" while the diaglod pops up?
I think we need something like this:
public static void showDialog(SomeActivity someActivity, final SomeCallback callBack {
final Dialog dialog = new Dialog(someActivity);
dialog.setContentView(R.layout.dialog_with_buttons);
// OK button ...
Button dialogButtonOk = dialog.findViewById(R.id.btn_ok);
dialogButtonOk.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
callBack.execute(true);
dialog.dismiss();
}
});
// Cancel button ...
Button buttonCancel = dialog.findViewById(R.id.btn_cancel);
buttonCancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callBack.execute(false);
dialog.dismiss();
}
});
dialog.show();
}
we can call the showDialog from SomeActivity like this:
showDialog(this, new SomeCallback () {
#Override
public void execute(boolean status) {
if (status) {
...
} else {
...
}
}
});
and the callback interface:
public interface SomeCallback {
void execute(boolean status);
}
good luck
the pseudo code can be like:
boolean status = false;
#Override
public void onUpdateAlert(final int event, final String message){
status = true;
// pop up the dialog
}
#Override
public void onDetected(Hand pose){
if(status) {
status = false;
...
}
}

How to simulate onLongClick?

How can I simulate onLongClick? Basically I need the user to click once - and a method to turn it into longClick without actually long clicking.
On Android, every View object has the method performLongClick, that allows you to simulate the action programmatically. But you have to set the listener before:
View dummyView = findViewById(R.id.dummy_view);
dummyView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return true;
}
});
And now you can call dummyView.performLongClick(), to simulate the longClick action
View dummyView = findViewById(R.id.dummy_view);
dummyView .setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dummyView .performLongClick();
}
});

App button as keyboard press

I'm trying to map a button in my fragment layout as a keypress on a keyboard.
This is my button press:
Button down = (Button)(myView.findViewById(R.id.btnDOWN));
down.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "Down pressed");
return false;
}
});
I wasn't able to find any library which would allow me to do so, all I've found was reverse case, when a keyboard or controller press would be sent to device.
What I'm trying to generally do here, is an app that acts like a controller for RetroPie by using the phone as a bluetooth keyboard, with only the keys necessary for a specific controller.
The method you're using is not correct. you should use the setOnClickListener:
Here an example from Android official guide:
public class MyActivity extends Activity {
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.content_layout_id);
final Button button = findViewById(R.id.button_id);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
}
}
If you're looking for continuos aka long click, you should change it to setOnLongClickListener:
down.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// TODO Auto-generated method stub
return true;
}
});
It wasn't hard to find in this case the solution on this site

Andorid : setOnClickListener does not work when calling Twice

I'm using setOnClickListener for listening on the click event on imageButton in two methods, but it's does not fire in my another method,my first listener firing but my second listener does not fire please see my codes :
Class FirstActivity extends BaseActivity
{
#Override
public void onCreate()
{
super.onCreate();
this.methodA();
this.methodB();
}
public void methodA()
{
ImageButton imageButton = (ImageButton) RContextHelper.getActivity().findViewById(R.id.my_location_button);
imageButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//event firing when image button touched
}
});
}
public void methodB()
{
Test test = new Test(this);
test.methodA();
}
}
class Test
{
Context con;
public Test(Context con)
{
this.con = con;
}
public void methodA()
{
ImageButton imageButton = (ImageButton) getActivity().findViewById(R.id.my_location_button);
imageButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//event does not fire when image button touched
}
});
}
protected ActionBarActivity getActivity()
{
return (ActionBarActivity) con;
}
}
As you can guess from the name setOnClickListener sets the new listener and replaces the old one. That is the case with all the set* listeners in Java. If it was addOnClickListener then you could expect that both listeners should be called.
If you want both of them to be called, you can write a composite on click listener and add both of the listeners to it and set the composite listener to the target.
class CompositeListener implements OnEventListener {
private List<OnEventListener> registeredListeners = new ArrayList<OnEventListener>();
public void registerListener (OnEventListener listener) {
registeredListeners.add(listener);
}
public void onEvent(Event e) {
for(OnEventListener listener:registeredListeners) {
listener.onEvent(e);
}
}
}
And then:
CompositeListener composite = new CompositeListener();
composite.registerListener(listener1);
composite.registerListener(listener2);
imageButton.setOnEventListener(composite);
Source
Very confusing to code with two methodA functions. You never call the second one. At least you are not showing code for that. Moreover - as has been said already - there can only be one listener.

ANDROID JAVA - Define all id's at once. (+create easy buttons)

I am new to programming in Java, i've managed to create a little calculator as a little test app.
But i think i am using way to much code for my needs.
So i've given a Button a name: buttonname
Now to change it's text when clicked i need to:
public class MyActivity extends Activity {
Button buttonname;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
buttomname = (Buttom) findViewById(R.id.buttomname);
}
public void buttonnameOnClick(View v) {
button1.setText ("NewText")
}
}
(i've bolted everything i had to add)
So i had to do everything above + connect the buttonClick through the xml file.
So i was wondering if there is a easier way to define all objects so i dont have to do: Button buttonname; and buttomname = (Buttom) findViewById(R.id.buttomname); all the time.
And i was wondering if there is a easier way to auto create button events.
(I am used to Visual Studio, but now i am kinda lost in Android Studio. So on Visual Studio i just had to double click the button and type: buttonname.Text = "NewText";)
There is a library called Butter Knife to do approximately that
However, I'm not sure if you really need it.
Oh, and you don't have to find the same Button every time. You find it once in onCreate and store in a field.
First of all you have typo in
buttomname = (Buttom) findViewById(R.id.buttomname);
It should be
buttomname = (Button) findViewById(R.id.buttomname);
and you forgot ; in one line "didn't your IDE show error to you!!" and also small correction in
public void buttonnameOnClick(View v) {
button1.setText ("NewText")
}
it should be
buttomname.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
buttomname.setText ("NewText");
}
});
inside protected void onCreate.
2nd method:
And if you have define android:onclick="buttonnameOnClick" in XML then
public void buttonnameOnClick(View v) {
button1.setText ("NewText")
}
To be corrected to
public void buttonnameOnClick(View v) {
buttomname.setText ("NewText");
}
You can do it in a loop if you have a lot of identical buttons to process
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
for (int btn_id : new int[]{
R.id.buttomname
, R.id.buttomname2
, R.id.buttomname3
}) {
View v = view.findViewById(btn_id);
if (v != null) {
v.setOnClickListener(onClickButton);
}
}
}
//
private View.OnClickListener onClickButton = new View.OnClickListener() {
#Override
public void onClick(View view) {
// .. handle click
if (view.getId()==R.id.buttomname2){
}
}
Your code is partly correct,
however the
(Buttom) is wrong change it to (Button)
the other thing
public void buttonnameOnClick(View v) {
button1.setText ("NewText")
}
can just be changed to:
public void buttonnameOnClick(View v) {
Button buttonTemp = (Button)v;
buttonTemp.setText ("NewText");
}
Assuming you are calling the method from layout xml file.
you must use the onClickListener() method for Button object.
Your code like this structure;
buttonname = (Button)findViewById(R.id.buttonname);
buttonname.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
I recommend to your visit Button | Android Dev page for Button.

Categories