버튼을 누를 때 활동이 두 번로드되는 것을 방지하는 방법


96

첫 번째 클릭 후 즉시 버튼을 두 번 누르면 활동이 두 번로드되는 것을 방지하려고합니다.

버튼 클릭시로드되는 활동이 있습니다.

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

이제로드 할 활동에 네트워크 호출이 있기 때문에로드하는 데 약간의 시간이 걸립니다 (MVC). 이에 대한 로딩 뷰를 보여 주지만 그 전에 버튼을 두 번 누르면 액티비티가 두 번로드되는 것을 볼 수 있습니다.

아무도 이것을 방지하는 방법을 알고 있습니까?


활동을 연 후 버튼을 비활성화 할 수 있으며 활동이 완료되면 다시 활성화 할 수 있습니다. onActivityResult 함수를 호출하여 두 번째 활동의 완료를 감지 할 수 있습니다.
Maneesh

버튼을 처음 클릭 할 때 비활성화하고 나중에 다시 클릭 할 때만 다시 활성화합니다.
JimmyB 2011

다음 문이 긴 프로세스 또는 활동 시작에 대한 것이라면 비활성화는 간단한 방식으로 작동하지 않습니다 ... 버튼을 비활성화하려면 별도의 스레드를 만들어야합니다 ...
Awais Tariq 2011

같은 API를 치는 경우 두 번 여기를 참조하십시오 techstricks.com/avoid-multiple-requests-when-using-volley
쉐일 렌드 라 Madda

답변:


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
이것이 올바른 접근 방식입니다. 버튼 선택 상태 (제공 한 경우)와 간단한 표준 위젯에서 기대할 수있는 모든 머티리얼 디자인 "상품"도 처리합니다. 사람들이 이것을 위해 타이머를 사용한다는 것을 믿을 수 없습니다. 그럼 당신은 ... 이런 핸들 것에 이상한 라이브러리를보고 시작
마틴 Marconcini

158

이것을 Activity정의에 추가하십시오 AndroidManifest.xml...

android:launchMode = "singleTop"

예를 들면 :

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

네, 새로운 활동을 시작한 후 약간의 처리를하고있는 것 같습니다. 그래서 화면이 검게 변합니다. 이제 검은 화면을 피하려면 활동 시작시 진행률 대화 상자를 표시하고 별도의 스레드에서 긴 처리를 수행해야합니다 (예 : UI 스레드 또는 간단히 비동기 클래스 사용). 처리가 완료되면 해당 대화 상자를 숨 깁니다. 제가 아는 한 최고의 솔루션이며 여러 번 사용해 왔습니다 ... :)
Awais Tariq 2011

표시 할 대화가 있습니다. 하지만 예, onCreate에서 웹을 가리키는 한 가지 방법이 있습니다. 그러나 이것이 유일한 해결책입니까? 이 시점에서는 실을 바꾸지 않고 처리하고 싶기 때문입니다. 그래서 다른 가능한 방법을 알고 있습니까? 그리고 나는 내 목록 어댑터에있는 버튼을 가지고 그리고 난은 XML에 대한 방법을 선언하지 프로그래밍
테자스

2
다른 무엇이 가능합니까 ??? 매끄럽게 보이는 앱을 얻으려면 스레딩을 구현해야합니다 ... 시도해보세요 ..;) 모든 현재 코드를 메서드에 넣고 작성한 동일한 위치의 별도 스레드에서 해당 메서드를 호출하면됩니다. 이전에 ... 5 ~ 6 줄의 코드를 거의 늘릴 수 없습니다 ..
Awais Tariq

19
이렇게하면 활동의 두 인스턴스가 존재하지 않지만 코드가 두 번 잘못 실행되는 것을 방지 할 수는 없습니다. 찬성표가 적음에도 불구하고 받아 들여진 답변이 더 좋습니다.
lilbyrdie

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
이 답변은 가장 많이 찬성 된 답변과 결합하여 가장 잘 작동하는 것 같습니다. 활동의 매니페스트에서이 플래그를 사용합니다 : android:launchMode = "singleTop", 이렇게하면 모든 인 텐트에 플래그를 추가하지 않고도 해결할 수 있습니다.
Nohus

1
두 개의 동일한 유형 활동을 가질 수 없기 때문에 중첩 된 활동이 필요한 경우 유용하지 않습니다.
Behnam Heydari 19

5
이것은 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에서 저를 위해 일하는 유일한 것입니다
Mingjiang Shi

1
"FLAG_ACTIVITY_REORDER_TO_FRONT"를 사용하고 있으며 정상적으로 작동하고 뒤로 버튼도 정상적으로 작동합니다. "Breaks back button"이 정확히 무엇을 의미 했습니까? 그것에 대해 명확히 해주시겠습니까?
Mirmuhsin Sodiqov

"REORDER"플래그에 버그가 있음을 발견했습니다. 그리고 KitKat에서 재정렬 되지 않았습니다 . 그러나 Lollipop과 Pie에서 확인했는데 정상적으로 작동합니다.
Mirmuhsin Sodiqov

9

나를 위해 일했을 때만 startActivity(intent)

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

1
@raj android:launchMode = "singleInstance"활동 태그의 매니페스트 파일에 이것을 추가해 보셨습니까 ?
Shailendra Madda

5

두 번 호출하는 활동을 피하려면 singleInstance를 사용하십시오.

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

4

@wannik이 옳다고 가정 해 보겠습니다.하지만 동일한 액션 리스너를 호출하는 버튼이 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로 설정하면 동일한 리스너를 사용하는 다른 버튼이 계속 활성화됩니다. 따라서 리스너의 액션이 두 번 호출되지 않도록하려면 리스너의 모든 호출을 비활성화하는 전역적인 것이 있어야합니다 (새 인스턴스인지 여부는 신경 쓰지 마십시오).

내 답변과 다른 답변의 차이점은 무엇입니까?

그들은 올바른 방식으로 생각하고 있지만 향후 호출 활동의 동일한 인스턴스로 돌아갈 생각은 없습니다. :)


서보 퍼, 조사 해주셔서 감사합니다. 이 질문은 이미 해결되었으며, 귀하의 답변도 귀하가 말한 상황에 대해 유망 해 보입니다. 내가 시도하고 :) 결과와 함께합시다
테자스

1
내 게임 중 하나에이 문제가 있습니다. 동일한 리스너를 가진 "선택 레벨"풍선이 있고 뷰는 태그에 따라 다릅니다. 따라서 두 개의 풍선을 빨리 선택하면 두 가지 활동이 시작됩니다. 새 활동이 소리를 시작하기 때문에 ..이 경우 소리가 두 번 재생된다는 것을 알고 있습니다.하지만 뒤로 클릭하면 이전 활동으로 이동할 수 있습니다
Sir NIkolay Cesar The First

1
이것으로는 충분하지 않습니다. synchronized(mIsClicked) {...}100 % 안전하려면를 사용해야합니다 .
Monstieur

이 ... 모든 주요 스레드이기 때문에 당신은 동기화 된 블록을 필요가 없습니다 @Monstieur
마틴 Marconcini

@MartinMarconcini Android 활동에서 안전하다고해서 좋은 코드가되지는 않습니다. 독립형 클래스라면 스레드로부터 안전하지 않은 것으로 문서화되어야합니다.
Monstieur

4

이 상황에서는 singleTaskmanifest.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()등). 실제로 이러한 메서드는 활동에서 사용할 개체를 인스턴스화하고 초기화하는 데 사용되어야하며 따라서 상대적으로 빠릅니다.

웹 요청을 수행해야하는 경우 새로 시작된 활동의 백그라운드 스레드에서이 작업을 수행하고 새 활동에로드 대화 상자를 표시합니다. 백그라운드 요청 스레드가 완료되면 활동을 업데이트하고 대화 상자를 숨길 수 있습니다.

그러면 새 활동이 즉시 시작되고 더블 클릭이 가능하지 않게됩니다.


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 메소드에서 다음과 같이 하나의 플래그를 유지하십시오.

공개 부울 oneTimeLoadActivity = false;

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

1

클릭시 활동이 두 번 열리는 것을 방지하기 위해 실행 모드를 매니페스트의 단일 작업으로 추가합니다.

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

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

이벤트에서 요청 코드가 반환되기 때문에 뒤로 버튼을 눌렀을 때도 작동합니다.


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

최종적으로 선언해야하므로 작동하지 않을 것입니다.

-1

flag변수를 사용하여 설정 to 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.