如何防止在按下按钮时两次加载活动


93

如果试图在第一次单击后立即按两次按钮,则试图阻止两次加载活动。

我有一个点击一下按钮就会加载的活动

 myButton.setOnClickListener(new View.OnClickListener() {
      public void onClick(View view) {
       //Load another activity
    }
});

现在,由于要加载的活动具有网络调用,因此加载(MVC)需要一些时间。我确实为此显示了加载视图,但是如果在此之前我按了两次按钮,则可以看到活动被加载了两次。

有人知道如何预防吗?


您可以在打开活动后禁用该按钮...,并在活动完成后重新启用...您可以通过调用onActivityResult函数来检测第二个活动的完成
Maneesh

第一次单击时禁用该按钮,以后仅在您希望再次单击该按钮时才重新启用它。
JimmyB 2011年

如果下一条语句需要较长的过程或活动开始,则禁用将无法简单地实现。要禁用按钮,您必须创建一个单独的线程...
Awais Tariq

如果两次击中相同的API,请参见此处:techstricks.com/avoid-multiple-requests-when-using-volley
Shylendra Madda '18

Answers:


69

在按钮的事件侦听器中,禁用按钮并显示另一个活动。

    Button b = (Button) view;
    b.setEnabled(false);

    Intent i = new Intent(this, AnotherActitivty.class);
    startActivity(i);

重写onResume()以重新启用按钮。

@Override
    protected void onResume() {
        super.onResume();

        Button button1 = (Button) findViewById(R.id.button1);
        button1.setEnabled(true);
    }

1
这是正确的方法。它甚至可以为您处理“按钮选定状态”(如果您提供的话)以及您希望从简单的标准Widget获得的所有“材料设计”“好东西”。我不敢相信人们为此使用计时器。然后您开始看到奇怪的库来处理类似的事情……
Martin Marconcini

157

将此添加到您的Activity定义中AndroidManifest.xml...

android:launchMode = "singleTop"

例如:

<activity
            android:name=".MainActivity"
            android:theme="@style/AppTheme.NoActionBar"
            android:launchMode = "singleTop"/>

好的,我想您在开始新活动后需要进行一些长时间的处理。这就是为什么屏幕变黑的原因。现在,如果要避免出现黑屏,应该在活动开始时显示进度对话框,并在单独的线程中进行长时间处理(即UI线程或仅使用async类)。处理完成后,隐藏该对话框。就我所知,这是最好的解决方案,并且已经使用了好几次... :)
Awais Tariq

我有要显示的对话框。但是,是的,我在onCreate中有一种指向Web的方法。但这是唯一的解决方案吗?因为在这一点上,我想处理而不更改线​​程和所有线程。那么您知道其他可能的方式吗?而我在我的列表中的适配器的按钮,我已经宣布了它的方法在XML中,没有编程
TEJAS

2
还有什么可能的???您必须通过一种或多种方式来实现线程以获取外观流畅的应用程序……试试看吧..;)只需将所有当前代码放入一个方法中,并在您编写相同位置的单独线程中调用该方法即可。它前面......这将很难增加五到六行代码..
AWAIS塔里克

19
这样可以防止存在两个活动实例,但不能防止代码错误地运行两次。尽管投票数较少,但可接受的答案更好。
lilbyrdie 2014年

18
这是错误的,它使活动永远不会存在两次,即使在不同的任务中也是如此。正确的方法是android:launchMode = "singleTop",可以在不破坏Android多任务处理的情况下达到效果。文档指出大多数应用程序不应使用该singleInstance选项。
Nohus

37

您可以像这样使用意图标志。

Intent intent = new Intent(Class.class);    
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
activity.startActivity(intent);

它只会使一个活动在历史记录堆栈的顶部打开。


4
该答案与最受好评的答案相结合似乎效果最好。在Activity的清单中使用此标志:android:launchMode = "singleTop",这样就可以解决该问题,而不必将标志添加到每个Intent。
Nohus

1
当您需要嵌套活动时,这是没有用的,因为您不能拥有两个相同类型的活动。
Behnam Heydari

4
在startActivityForResult的情况下,此方法不起作用
raj

27

由于SO不允许我对其他答案发表评论,因此我不得不用新的答案来污染该线程。

关于“活动两次打开”问题的常见答案以及我对以下解决方案的体验(Android 7.1.1):

  1. 禁用启动活动的按钮:可以,但是有点笨拙。如果您有多种方法来启动应用程序中的活动(例如,操作栏中的按钮并单击列表视图中的某个项目),则必须跟踪多个GUI元素的启用/禁用状态。另外,例如,禁用列表视图中的单击项不是很方便。因此,这不是一种非常普遍的方法。
  2. launchMode =“ singleInstance”:不能与startActivityForResult()一起使用,使用startActivity()中断导航,Android清单文档不建议将其用于常规应用程序。
  3. launchMode =“ singleTask”:不适用于startActivityForResult(),Android清单文档不建议将其用于常规应用程序。
  4. FLAG_ACTIVITY_REORDER_TO_FRONT:暂停返回按钮。
  5. FLAG_ACTIVITY_SINGLE_TOP:不起作用,活动仍然打开两次。
  6. FLAG_ACTIVITY_CLEAR_TOP:这是唯一为我工作的人。

编辑:这是使用startActivity()启动活动的。使用startActivityForResult()时,我需要同时设置FLAG_ACTIVITY_SINGLE_TOP和FLAG_ACTIVITY_CLEAR_TOP。


FLAG_ACTIVITY_CLEAR_TOP:这是在Android 7.1.1上为我工作的唯一软件
Shi Mingjiang Shi

1
我正在使用“ FLAG_ACTIVITY_REORDER_TO_FRONT”,并且工作正常,并且“返回”按钮也正常运行。“中断返回按钮”到底是什么意思?你能澄清一下吗?
Mirmuhsin Sodiqov

我发现“ REORDER”标志存在错误……并且在KitKat 中没有重新排序。但是,我在Lollipop和Pie中检查了它,效果很好。
Mirmuhsin Sodiqov

7

它只对我有用 startActivity(intent)

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

1
@raj您是否尝试过将其添加到android:launchMode = "singleInstance"活动代码的清单文件中?
Shylendra Madda


4

假设@wannik是正确的,但是如果我们有多个按钮调用同一个动作侦听器,并且在开始下一个活动之前我几乎同时单击了两个按钮...

因此,如果您有字段private boolean mIsClicked = false;并且在侦听器中,那就很好了:

if(!mIsClicked)
{
    mIsClicked = true;
    Intent i = new Intent(this, AnotherActitivty.class);
    startActivity(i);
}

onResume()我们需要返回的状态:

@Override
protected void onResume() {
    super.onResume();

    mIsClicked = false;
}

我和@wannik的答案有何区别?

如果在其调用的侦听器中将enabled设置为false,则仍将启用使用同一侦听器的其他按钮。因此,要确保不会两次调用侦听器的动作,则需要全局禁用所有侦听器的调用(无论是新实例还是否,请不要介意)

我的答案和其他答案有什么区别?

他们以正确的方式思考,但他们并未在考虑将来返回到呼叫活动的同一实例:)


伺服器,谢谢您的研究。这个问题已经解决了,您的回答对您所讲的情况也看起来很有希望。让我尝试得出结果:)
tejas

1
我在其中一款游戏中遇到了这个问题。我有具有相同侦听器的“选择级别”气球,并且视图仅因标签而不同。因此,如果我快速选择两个气球,它将启动两个活动。我知道这是因为新活动开始发出声音..在这种情况下,声音会播放两次...,但是您可以通过单击返回进行检查,这将带您进入上一个活动
NIkolay Cesar爵士

1
这还不够。您还需要使用synchronized(mIsClicked) {...},以确保100%安全。
Monstieur 2013年

@Monstieur,您不需要同步块,因为这都是主线程…
Martin Marconcini

@MartinMarconcini仅仅因为它在Android活动中是安全的,并不能使其成为良好的代码。如果是独立类,则必须记录为非线程安全。
Monstieur

4

对于这种情况,我将singleTask在manifest.xml或Activity的onResume()onDestroy()方法中的一个标志中分别采用两种方法之一。

对于第一个解决方案:我更喜欢将其singleTask用于清单中的活动,而不是singleInstance,按照singleInstance我的使用,我发现在某些情况下,该活动会为其自身创建一个新的单独实例,从而在运行的应用程序中具有两个单独的应用程序窗口在bcakground中,除了额外的内存分配外,当用户打开应用程序视图以选择一些要恢复的应用程序时,这将导致非常糟糕的用户体验。因此,更好的方法是在manifest.xml中定义活动,如下所示:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"</activity>

您可以在此处检查活动启动模式。


对于第二种解决方案,您只需定义静态变量或首选项变量,例如:

public class MainActivity extends Activity{
    public static boolean isRunning = false;

    @Override
    public void onResume() {
        super.onResume();
        // now the activity is running
        isRunning = true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // now the activity will be available again
        isRunning = false;
    }

}

然后从另一端启动此活动时,只需检查:

private void launchMainActivity(){
    if(MainActivity.isRunning)
        return;
    Intent intent = new Intent(ThisActivity.this, MainActivity.class);
    startActivity(intent);
}

3

我认为您打算以错误的方式解决问题。通常,以任何一种启动生命周期方法(onCreate()onResume()等等)。确实,这些方法仅应用于实例化和初始化您的活动将使用的对象,因此应该相对较快。

如果您需要执行Web请求,请在新启动的活动的后台线程中执行此操作(并在新活动中显示加载对话框)。后台请求线程完成后,便可以更新活动并隐藏对话框。

然后,这意味着您的新活动应立即启动,并防止可能的双击。


3

希望这可以帮助:

 protected static final int DELAY_TIME = 100;

// to prevent double click issue, disable button after click and enable it after 100ms
protected Handler mClickHandler = new Handler() {

    public void handleMessage(Message msg) {

        findViewById(msg.what).setClickable(true);
        super.handleMessage(msg);
    }
};

@Override
public void onClick(View v) {
    int id = v.getId();
    v.setClickable(false);
    mClickHandler.sendEmptyMessageDelayed(id, DELAY_TIME);
    // startActivity()
}`

2

如果您不想使用其他非常非常简单的解决方案,则将onActivityResult()按钮禁用2秒钟(或您想要的时间),虽然不理想,但是可以部分解决问题,在某些情况下并且代码很简单:

   final Button btn = ...
   btn.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            //start activity here...
            btn.setEnabled(false);   //disable button

            //post a message to run in UI Thread after a delay in milliseconds
            btn.postDelayed(new Runnable() {
                public void run() {
                    btn.setEnabled(true);    //enable button again
                }
            },1000);    //1 second in this case...
        }
    });

2

//追踪事件时间的变量

private long mLastClickTime = 0;

2.在onClick中检查是否当前时间和最终点击时间差小于我秒,然后不做任何事情(返回),否则进行点击事件

 @Override
public void onClick(View v) {
    // Preventing multiple clicks, using threshold of 1 second
    if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) {
        return;
          }
    mLastClickTime = SystemClock.elapsedRealtime();
            // Handle button clicks
            if (v == R.id.imageView2) {
        // Do ur stuff.
         }
            else if (v == R.id.imageView2) {
        // Do ur stuff.
         }
      }
 }

1

只需在按钮onClick方法中维护一个标志为:

public boolean oneTimeLoadActivity = false;

    myButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
               if(!oneTimeLoadActivity){
                    //start your new activity.
                   oneTimeLoadActivity = true;
                    }
        }
    });

0

如果您使用的是onActivityResult,则可以使用变量来保存状态。

private Boolean activityOpenInProgress = false;

myButton.setOnClickListener(new View.OnClickListener() {
  public void onClick(View view) {
    if( activityOpenInProgress )
      return;

    activityOpenInProgress = true;
   //Load another activity with startActivityForResult with required request code
  }
});

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if( requestCode == thatYouSentToOpenActivity ){
    activityOpenInProgress = false;
  }
}

也可以在按下后退按钮上使用,因为事件返回请求代码。


0

在清单中将启动模式添加为单个任务,以避免活动在单击时打开两次

<activity
        android:name=".MainActivity"
        android:label="@string/activity"
        android:launchMode = "singleTask" />

-1
myButton.setOnClickListener(new View.OnClickListener() {
      public void onClick(View view) {
      myButton.setOnClickListener(null);
    }
});

那可能行不通,因为您必须将其声明为final。
2015年

-1

使用flag变量设置它to true,检查它是否为真,return否则执行活动调用。

您还可以使用setClickable(false)来执行Activity调用

flg=false
 public void onClick(View view) { 
       if(flg==true)
         return;
       else
       { flg=true;
        // perform click}
    } 

perform click; wait; flg = false;当我们回来的时候
Xeno Lupus

-1

您可以覆盖startActivityForResult并使用实例变量:

boolean couldStartActivity = false;

@Override
protected void onResume() {
    super.onResume();

    couldStartActivity = true;
}

@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
    if (couldStartActivity) {
        couldStartActivity = false;
        intent.putExtra(RequestCodeKey, requestCode);
        super.startActivityForResult(intent, requestCode, options);
    }
}

-4

您也可以尝试

Button game = (Button) findViewById(R.id.games);
        game.setOnClickListener(new View.OnClickListener() 
        {
            public void onClick(View view) 
            {
                Intent myIntent = new Intent(view.getContext(), Games.class);
                startActivityForResult(myIntent, 0);
            }

        });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.