Android:防止在按钮被禁用后多次触发onClick事件

20

一个按钮触发了一个只应该被调用一次的操作。在执行该操作之前,单击处理程序会禁用并隐藏该按钮:

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)
}

虽然按钮立即被禁用,但仍然有可能通过快速点击多次来触发多个“onClick”事件(即performTaskOnce被多次调用)。似乎这些onClick事件在按钮实际禁用之前被排队。

我可以通过在每个单独的onClick处理程序中检查相应的按钮是否已经被禁用来解决问题,但那似乎是一个hack。是否有更好的方法来避免这个问题?

该问题出现在Android 2.3.6上,在Android 4.0.3上我无法复现该问题。但考虑到4.x设备的罕见性,排除旧设备并不是一个选项。


那个问题让我烦了好几天了.. - Mohamed Nageh
5个回答

8
当按钮被点击时,您可以将布尔变量设置为true,并在处理完点击后将其设置为false。
这样一来,您就可以忽略多次点击,而不必禁用按钮,可能会避免按钮的刺眼闪烁。
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)
}

3
这似乎是最简单的方法。在iOS中,UIApplication类有一个名为'beginIgnoringInteractionEvents'的消息,可以全局执行此操作。这有时可能很方便。然而,我找不到Android的等效方法。您还可以使用一个简单的保护条件“if(!processClick)return;”,这将减少一些嵌套。 - Dave Cameron
@DaveCameron +1 完全同意你的观点。 - Vipul

6
为了保持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
            }
        });
    }
}

1
有点晚了,但这可能对某些人有用。在我的情况下,我正在调用另一个活动,因此需要声明一个布尔值;
boolean clickable;

在点击监听器中;
if(clickable){
   // Launch other activity
   clickable = false;
}

当 onResume 被调用时启用;

@Override
public void onResume() {
    Log.e(TAG, "onResume");
    super.onResume();
    clickable = true;
}

0

你可以使用 RxView(com.jakewharton.rxbinding2.view.RxView),这是由Jake Wharton创建的RxJava扩展。

要将其集成到项目中,您应该使用 implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'

简单的Java用法:

RxView.clicks(yourButton)
    .sample(500, TimeUnit.MILLISECONDS)
    .subscribe { action() }

在 Kotlin 中,您可以创建扩展函数来处理单击事件:
View.singleClick(action: () -> Any) {
    RxView.clicks(this)
        .sample(500, TimeUnit.MILLISECONDS)
        .subscribe { action() }
}

范例:

Kotlin

yourButton.singleClick({
    //do some stuff here
})

Java

SingleClickListenerKt.singleClick(yourButton, () -> {
    doSomeStuff();
    return null;
    });

注意:如果你愿意,可以使用任何 RxJava 操作符,如 debounce, map, first 等。


-1

声明一个变量并使用它

 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;
 }


    }
});

通过这种方法,您可以防止多次点击按钮。
希望能对您有所帮助。

1
使用int类型并对其进行递增,相比使用标志(flag)来表示不太清晰。而且您还没有展示如何以及何时将其重置为零。另外,为什么要使用类变量而不是实例变量呢? - Dave Cameron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接