Как предотвратить загрузку активности дважды при нажатии кнопки
то, что я пытаюсь выяснить, это предотвратить загрузку активности дважды, если я дважды нажму кнопку.
У меня есть активность, которая загружается при нажатии кнопки, скажем
myButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//Load another activity
}
});
теперь, поскольку загружаемое действие имеет некоторое отношение к веб-сервису и другим операциям, для его загрузки требуется немного времени. Конечно, у меня есть вид загрузки для этого. Но до этого, если я нажму кнопку дважды, я вижу, что активность загружается дважды.
кто-нибудь знает, как это предотвратить?
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):
- кнопка отключения, которая запускает активность: работает, но чувствует себя немного неуклюжей. Если у вас есть несколько способов запустить действие в вашем приложении (например, кнопка на панели действий и нажав на элемент в виде списка), вы должны сохранить отслеживание включенного / отключенного состояния нескольких элементов графического интерфейса. Кроме того, не очень удобно отключать щелкнутые элементы в виде списка, например. Так что, не очень универсальный подход.
- launchMode= "singleInstance": не работает с startActivityForResult(), прерывает навигацию с помощью startActivity(), не рекомендуется для обычных приложений в документации манифеста Android.
- launchMode="singleTask": не работает с startActivityForResult(), не рекомендуется обычные приложения по документации манифеста Android.
- FLAG_ACTIVITY_REORDER_TO_FRONT: ломает кнопку Назад.
- FLAG_ACTIVITY_SINGLE_TOP: не работает, активность по-прежнему открыта дважды.
- 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 itto 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); } }