Как предотвратить загрузку активности дважды при нажатии кнопки


то, что я пытаюсь выяснить, это предотвратить загрузку активности дважды, если я дважды нажму кнопку.

У меня есть активность, которая загружается при нажатии кнопки, скажем

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

теперь, поскольку загружаемое действие имеет некоторое отношение к веб-сервису и другим операциям, для его загрузки требуется немного времени. Конечно, у меня есть вид загрузки для этого. Но до этого, если я нажму кнопку дважды, я вижу, что активность загружается дважды.

кто-нибудь знает, как это предотвратить?

17 51

17 ответов:

в прослушивателе событий кнопки отключите кнопку и покажите другую активность.

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

добавьте это в ваш Activity определение Androidmanifest.xml...

android:launchMode = "singleInstance"

вы можете использовать флаги намерений, как это.

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

это сделает только одно действие будет открыто в верхней части стека истории.

поскольку SO не позволяет мне комментировать другие ответы, я должен загрязнить эту тему новым ответом.

общие ответы на проблему "активность открывается дважды" и мой опыт работы с этими решениями (Android 7.1.1):

  1. кнопка отключения, которая запускает активность: работает, но чувствует себя немного неуклюжей. Если у вас есть несколько способов запустить действие в вашем приложении (например, кнопка на панели действий и нажав на элемент в виде списка), вы должны сохранить отслеживание включенного / отключенного состояния нескольких элементов графического интерфейса. Кроме того, не очень удобно отключать щелкнутые элементы в виде списка, например. Так что, не очень универсальный подход.
  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: это единственный, кто работает для меня.

EDIT: это было для начала деятельности с startActivity(). При использовании startActivityForResult () мне нужно установить как FLAG_ACTIVITY_SINGLE_TOP, так и FLAG_ACTIVITY_CLEAR_TOP.

допустим, @wannik прав, но если у нас есть более 1 кнопки вызова одного и того же слушателя действий, и я нажимаю две кнопки один раз почти одновременно перед началом следующего действия...

так это хорошо, если у вас есть поле 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?

если вы установите значение Включено в false в прослушивателе его вызов другой кнопки просмотра с использованием того же прослушивателя по-прежнему будет включен. Поэтому, чтобы быть уверенным, что действие слушателя не вызывается дважды, вам нужно иметь что-то глобальное, которое отключает все вызовы слушателя(неважно, если это новый экземпляр или нет)

в чем разница между моим ответом и другим?

они думают правильно, но они не думают о будущем возвращении к тому же экземпляру вызывающей активности :)

используйте singleInstance, чтобы избежать активности для вызова дважды.

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

Я думаю, что вы собираетесь решать проблему неверным путем. Как правило, это плохая идея для деятельности, чтобы делать длительные веб-запросы в любом из его методов жизненного цикла запуска (onCreate(),onResume(), etc). На самом деле эти методы должны просто использоваться для создания экземпляров и инициализации объектов, которые будут использоваться в вашей деятельности, и поэтому должны быть относительно быстрыми.

Если вам нужно выполнить веб-запрос, сделайте это в фоновом потоке из вашего недавно запущенного действия (и показать диалоговое окно загрузки в новом действии). После завершения потока фонового запроса он может обновить действие и скрыть диалоговое окно.

это означает, что Ваша новая деятельность должна быть запущена немедленно и предотвратить двойной щелчок от возможности.

надеюсь, это поможет:

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

он работал только для меня, когда startActivity(intent)

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

в этой ситуации я пойду на один из двух подошедших, singleTask в манифесте.xml или флаг в действии onResume() & onDestroy() методы соответственно.

на первый решение: я предпочитаю использовать singleTask для активности в манифесте, а не singleInstance, а за помощью singleInstance я понял, что в некоторых случаях активность создает новый отдельный экземпляр для себя, в результате чего в запущенных приложениях появляется два отдельных окна приложений bcakground и помимо дополнительных выделений памяти, которые приведут к очень плохому пользовательскому опыту, когда пользователь открывает представление приложений, чтобы выбрать какое-либо приложение для возобновления. Таким образом, лучший способ-определить активность в манифесте.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);
}

другое очень простое решение, если вы не хотите использовать 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...
        }
    });

просто поддерживать один флаг в методе кнопки onClick как:

public boolean oneTimeLoadActivity = false;

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

Если вы используете 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;
  }
}

работает кнопка Назад нажата тоже, потому что код запроса возвращается на события.

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

использовать flag переменная set it to true, Проверьте, если это правда просто return выполнить вызов активности.

вы также можете использовать setClickable (false), выполняющий вызов действия

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

вы можете попробовать это

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

        });

вы можете просто переопределить 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);
    }
}