2015-04-24 19 views
16

Sono appena passato la mia app per utilizzare il recente rilasciato v22.1.0 AppCompat e ora onKeyDown e onKeyUp non si attivano quando il tasto menu è premuto. Gli altri tasti attivano correttamente onKeyDown e onKeyUp, ma quando preme il tasto menu non accade nulla. Se eseguo il downgrade alla versione v.0.0.0, tutto torna a funzionare correttamente.aggiornato a AppCompat v22.1.0 e ora onKeyDown e onKeyUp non si attivano quando si preme il tasto menu

Come si risolve?

+1

Rispondere alla tua domanda? Nello stesso momento? – Niels

+2

Sì, ho seguito il consiglio di questo [articolo] (http://stackoverflow.com/help/self-answer) nel Centro assistenza di Stack Overflow –

+0

Sembra che gli eventi onKeyDown e onKeyUp siano ora correttamente attivati ​​per la chiave di menu su AppCompat v22.2.0. – NullNoname

risposta

31

Aggiornamento 23 agosto

Questo nuovo has been fixed nella v23.0.0 della libreria di supporto appcompat-V7. Aggiornamento all'ultima versione per vederlo corretto.


Aggiornamento 19 luglio

Purtroppo AppCompat v22.2.1 spezzarono le onKeyDown e onKeyUp eventi again. Ho appena aggiornato per supportare AppCompatActivityMenuKeyInterceptor v22.1.x e anche v22.2.1


Aggiornamento 29 maggio

Questo has been fixed nella v22.2.0 della libreria di supporto appcompat-V7. Aggiornamento all'ultima versione per vederlo corretto.


Purtroppo AppCompat v22.1.0 intercetta la onKeyDown e onKeyUp eventi e non propaga quando si preme il tasto del menu. L'unica soluzione possibile prevede l'utilizzo di Reflection per intercettare gli eventi onKeyDown e onKeyUp prima che AppCompat lo faccia.

Aggiungere questa classe al progetto:

public class AppCompatActivityMenuKeyInterceptor { 

    private static final String FIELD_NAME_DELEGATE = "mDelegate"; 
    private static final String FIELD_NAME_WINDOW = "mWindow"; 

    public static void intercept(AppCompatActivity appCompatActivity) { 
     new AppCompatActivityMenuKeyInterceptor(appCompatActivity); 
    } 

    private AppCompatActivityMenuKeyInterceptor(AppCompatActivity activity) { 
     try { 
      Field mDelegateField = AppCompatActivity.class.getDeclaredField(FIELD_NAME_DELEGATE); 
      mDelegateField.setAccessible(true); 
      Object mDelegate = mDelegateField.get(activity); 

      Class mDelegateClass = mDelegate.getClass().getSuperclass(); 
      Field mWindowField = null; 

      while (mDelegateClass != null) { 
       try { 
        mWindowField = mDelegateClass.getDeclaredField(FIELD_NAME_WINDOW); 
        break; 
       } catch (NoSuchFieldException ignored) { 
       } 

       mDelegateClass = mDelegateClass.getSuperclass(); 
      } 

      if (mWindowField == null) 
       throw new NoSuchFieldException(FIELD_NAME_WINDOW); 

      mWindowField.setAccessible(true); 
      Window mWindow = (Window) mWindowField.get(mDelegate); 

      Window.Callback mOriginalWindowCallback = mWindow.getCallback(); 
      mWindow.setCallback(new AppCompatWindowCallbackCustom(mOriginalWindowCallback, activity)); 
     } catch (NoSuchFieldException e) { 
      e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
    } 

    private class AppCompatWindowCallbackCustom extends WindowCallbackWrapper { 

     private WeakReference<AppCompatActivity> mActivityWeak; 

     public AppCompatWindowCallbackCustom(Window.Callback wrapped, AppCompatActivity appCompatActivity) { 
      super(wrapped); 

      mActivityWeak = new WeakReference<AppCompatActivity>(appCompatActivity); 
     } 

     @Override 
     public boolean dispatchKeyEvent(KeyEvent event) { 
      final int keyCode = event.getKeyCode(); 

      AppCompatActivity appCompatActivity = mActivityWeak.get(); 

      if (appCompatActivity != null && keyCode == KeyEvent.KEYCODE_MENU) { 
       if (appCompatActivity.dispatchKeyEvent(event)) 
        return true; 
      } 

      return super.dispatchKeyEvent(event); 
     } 
    } 
} 

Chiamata AppCompatActivityMenuKeyInterceptor.intercept(this) nel onCreate della vostra attività:

public class MainActivity extends AppCompatActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     //Initialize the interceptor 
     AppCompatActivityMenuKeyInterceptor.intercept(this); 
    } 

    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 
     // Now onKeyDown is called also for KEYCODE_MENU 
     if (keyCode == KeyEvent.KEYCODE_MENU) { 
      //do your stuff 

      //return false if you want to propagate the 
      //KeyEvent to AppCompat, return true otherwise 
      return false; 
     } 

     return super.onKeyDown(keyCode, event); 
    } 

    @Override 
    public boolean onKeyUp(int keyCode, KeyEvent event) { 
     // Now onKeyUp is called also for KEYCODE_MENU 
     if (keyCode == KeyEvent.KEYCODE_MENU) { 
      //do your stuff 

      //return false if you want to propagate the 
      //KeyEvent to AppCompat, return true otherwise 
      return false; 
     } 

     return super.onKeyUp(keyCode, event); 
    } 
} 

Se si utilizza ProGuard o DexGuard aggiungere queste regole alla configurazione:

-keepclassmembers class android.support.v7.app.AppCompatActivity { 
    private android.support.v7.app.AppCompatDelegate mDelegate; 
} 

-keepclassmembers class android.support.v7.app.AppCompatDelegateImplBase { 
    final android.view.Window mWindow; 
} 

Ora la tua attività può ricevere onKeyDown e onKeyUp evento anche per il tasto menu.

+0

Mi sono imbattuto in un problema con questo dopo aver offuscato il mio codice. Mantenere la libreria di supporto sembrava aiutare. Si prega di commentare questo se questo sembra sbagliato, ma ho aggiunto alla mia classe proguard-rules.pro '-keep android.support.v7. ** {*; } -keep interface android.support.v7. ** {*; } ' –

+1

@DaiwikDaarun Sì, le tue regole sono corrette, ma tu mantieni tutte le librerie di supporto. Se vuoi una regola specifica puoi vedere la risposta aggiornata. Grazie per avermelo fatto notare –

+1

Grazie, hai salvato il mio giorno – Dima

0

Invece di onKeyUp() o onKeyDown(), è sufficiente utilizzare dispatchKeyEvent(). Guarda il seguente codice da android-developers.blogspot.com.

@Override 
public boolean dispatchKeyEvent(KeyEvent event) { 
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 
     if (event.getAction() == KeyEvent.ACTION_DOWN 
       && event.getRepeatCount() == 0) { 

      // Tell the framework to start tracking this event. 
      getKeyDispatcherState().startTracking(event, this); 
      return true; 

     } else if (event.getAction() == KeyEvent.ACTION_UP) { 
      getKeyDispatcherState().handleUpEvent(event); 
      if (event.isTracking() && !event.isCanceled()) { 
       // DO BACK ACTION HERE 
       return true; 
      } 
     } 
     return super.dispatchKeyEvent(event); 
    } else { 
     return super.dispatchKeyEvent(event); 
    } 
} 
+0

Sfortunatamente la tua soluzione non funziona. Il 'dispatchKeyEvent' non viene propagato quando si preme il tasto menu mentre si utilizza la libreria di supporto v7 v22.1.0, v22.1.1 e v22.2.1. È un bug noto ed è stato corretto nelle versioni più recenti della libreria di supporto v7. –

+0

Significa che la pressione dei tasti del menu hardware non funziona nemmeno su 22.0.1 –

+0

Come si vede nel titolo, questa domanda è specifica per v22.1.0 e versioni successive (v22.1.0, v22.1.1 e v22.2.1) –