ボタンを押したときにアクティビティが2回読み込まれないようにする方法


93

最初にクリックした直後にボタンを2回押すと、アクティビティが2回読み込まれないようにしています。

ボタンをクリックするとロードされるアクティビティがあります。

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

ロードするアクティビティにネットワークコールがあるため、ロードに少し時間がかかります(MVC)。これについては読み込みビューを表示していますが、その前にボタンを2回押すと、アクティビティが2回読み込まれているのがわかります。

これを防ぐ方法を知っている人はいますか?


アクティビティを開いた後にボタンを無効にすることができます...そしてアクティビティが終了したら、再度有効にします... onActivityResult関数を呼び出すことで2番目のアクティビティの終了を検出できます
Maneesh

最初にクリックしたときにボタンを無効にし、後でもう一度クリックしたい場合にのみ、ボタンを再度有効にします。
JimmyB

次のステートメントが長いプロセスまたはアクティビティの開始である場合、無効化は単純な方法では機能しません...ボタンを無効にするには、別のスレッドを作成する必要があります...
Awais Tariq


回答:


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
これが正しいアプローチです。ボタンが選択された状態(提供されている場合)や、シンプルな標準ウィジェットに期待されるすべてのマテリアルデザインの「グッズ」も処理します。これにタイマーを使うなんて信じられない。次に、このようなことを処理するための奇妙なライブラリーを目にし始めます…
Martin Marconcini

157

これを...のActivity定義に追加しますAndroidManifest.xml

android:launchMode = "singleTop"

例えば:

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

わかりました、新しいアクティビティを開始した後、長い処理を行っているようです。そのため、画面が黒くなります。この黒い画面を避けたい場合は、アクティビティの開始時に進行状況ダイアログを表示し、長い処理を別のスレッド(UIスレッドまたは単に非同期クラスを使用)で行う必要があります。処理が完了したら、そのダイアログを非表示にします。それは私の知識の中で最高の解決策であり、私は何度かそれを使用しました... :)
Awais Tariq '10

表示するダイアログがあります。しかし、ええ、私はonCreateにWebを指す1つのメソッドがあります。しかし、これが唯一の解決策ですか?この時点で、スレッドやすべてを変更せずに処理したいので。他の可能な方法を知っていますか?リストアダプターにボタンがあり、そのメソッドをプログラムではなくxmlで宣言しています
tejas

2
他に何が可能ですか?いずれにしても、スムーズな外観のアプリを取得するにはスレッドを実装する必要があります...試してみてください.. ;;)メソッドに現在のコードをすべて配置し、記述した同じ場所で別のスレッドからそのメソッドを呼び出すだけです。それより前に... 5行から6行のコードはほとんど増加しません..
Awais Tariq

19
これにより、アクティビティの2つのインスタンスが存在しないようになりますが、コードが2回実行されるのを誤って防ぐことはできません。賛成票が少ないにもかかわらず、承認された回答の方が優れています。
lilbyrdie

18
これは誤りです。異なるタスクであっても、アクティビティが2回存在することはありません。正しい方法はですandroid:launchMode = "singleTop"。これにより、Androidのマルチタスクを中断せずに効果を得ることができます。ドキュメントには、ほとんどのアプリがこのsingleInstanceオプションを使用してはならないことが記載されています。
Nohus、

37

このようなインテントフラグを使用できます。

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

履歴スタックの一番上で開いているアクティビティは1つだけになります。


4
この回答は、最も支持されている回答と組み合わせると、最も効果的に機能するようです。このフラグをアクティビティのマニフェストで使用します。これにより、android:launchMode = "singleTop"すべてのインテントにフラグを追加しなくても解決されます。
Nohus、

1
同じタイプのアクティビティを2つ作成することはできないため、ネストされたアクティビティが必要な場合、これは役に立ちません。
Behnam Heydari

4
これは、startActivityForResult
raj

27

SOでは他の回答にコメントできないので、このスレッドを新しい回答で汚染する必要があります。

「アクティビティが2回開く」問題の一般的な回答とこれらのソリューション(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:機能していません。アクティビティはまだ2回開かれています。
  6. FLAG_ACTIVITY_CLEAR_TOP:これが私のために働く唯一のものです。

編集:これは、startActivity()でアクティビティを開始するためのものでした。startActivityForResult()を使用する場合、FLAG_ACTIVITY_SINGLE_TOPとFLAG_ACTIVITY_CLEAR_TOPの両方を設定する必要があります。


FLAG_ACTIVITY_CLEAR_TOP:Android 7.1.1で動作するのはこれだけです
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

5

singleInstanceを使用して、2回呼び出すアクティビティを回避します。

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

4

@wannikが正しいとしましょう。ただし、同じアクションリスナーを呼び出すボタンが2つ以上あり、次のアクティビティを開始する前にほぼ同時に2つのボタンをクリックした場合...

したがって、フィールドが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に設定した場合、同じリスナーを使用する他のボタンは引き続き有効になります。したがって、リスナーのアクションが2回呼び出されないようにするには、リスナーのすべての呼び出しを無効にするグローバルなものが必要です(新しいインスタンスであるかどうかは気にしないでください)。

私の回答と他の回答の違いは何ですか?

彼らは正しい方法で考えていますが、呼び出し元のアクティビティの同じインスタンスに将来戻ることを考えていません:)


サーボパー、ご研究ありがとうございます。この質問はすでに解決されていますが、あなたの答えはあなたが言った状況に対しても有望であるように見えます。結果を試してみましょう:)
tejas 2013年

1
私のゲームの1つにこの問題があります。同じリスナーを持つ「選択レベル」バルーンがあり、タグによってビューが異なるだけです。したがって、2つの風船をすばやく選択すると、2つのアクティビティが開始されます。私は、新しい活動が音を起動するためにことを知っている...と、この場合に音が二回再生されます...しかし、あなたは前のアクティビティにあなたを駆動するどのバックをクリックして確認することができます
サーニコライ・セザールザ・ファースト

1
これでは不十分です。synchronized(mIsClicked) {...}100%安全にするには、a も使用する必要があります。
Monstieur 2013

@Monstieurこれはすべてメインスレッドなので、同期ブロックは必要ありません…
Martin Marconcini

@MartinMarconcini Androidアクティビティでたまたま安全であるという理由だけで、優れたコードにはなりません。スタンドアロンクラスの場合は、スレッドセーフではないことを文書化する必要があります。
Monstieur 2018年

4

この状況ではsingleTask、manifest.xmlまたはアクティビティのonResume()onDestroy()メソッドのフラグで、2つのアプローチのいずれかを使用します。

以下のために最初の溶液:私が使用することを好むsingleTaskのではなく、マニフェストにおける活性についてsingleInstance用い通り、singleInstance私はいくつかの場面において活性が実行中のアプリケーションに2つの別々のアプリケーション・ウィンドウを有すること結果そのもののための新たな独立したインスタンスを作成することを考え出し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

あなたは問題を間違った方法で解決しようとしていると思います。一般的には、その起動ライフサイクルメソッド(のいずれかで長時間動作するWebリクエストを作成する活動のための悪い考えである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で、現在の時刻とラストクリックの時間差がi秒未満の場合は何もしない(リターン)かどうかを確認します。それ以外の場合はクリックイベントに進みます。

 @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メソッドで1つのフラグを維持するだけです。

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

起動モードをマニフェストの単一タスクとして追加して、クリック時にアクティビティが2回開かないようにする

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

最終的に宣言する必要があるため、おそらく機能しません。
キング

-1

flag変数set itを使用してto true、trueであるかどうかを確認しますreturn

アクティビティ呼び出しを実行するsetClickable(false)を使用することもできます

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

        });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.