2015-04-24 15 views
16

Ich habe gerade meine App aktualisiert, um die neu veröffentlichte v22.1.0 AppCompat zu verwenden und jetzt onKeyDown und onKeyUp werden nicht ausgelöst, wenn Menütaste ist gedrückt. Die anderen Tasten lösen korrekt onKeyDown und onKeyUp aus, aber wenn ich die Menütaste drücke, passiert nichts. Wenn ich auf v22.0.0 herabstufen, wird alles ordnungsgemäß funktionieren.Aktualisiert auf AppCompat v22.1.0 und jetzt onKeyDown und onKeyUp werden nicht ausgelöst, wenn die Menütaste gedrückt wird

Wie behebe ich es?

+1

Antwort auf Ihre eigene Frage? Im selben Moment? – Niels

+2

Ja, ich folgte dem Ratschlag von diesem [Artikel] (http://stackoverflow.com/help/self-answer) in der Hilfe von Stack Overflow –

+0

Es scheint onKeyDown und onKeyUp Ereignisse werden jetzt korrekt für Menüschlüssel auf AppCompat ausgelöst v22.2.0. – NullNoname

Antwort

31

-Update 23. August

Diese has been fixed wieder im v23.0.0 von appcompat-v7 Support-Bibliothek. Aktualisieren Sie auf die letzte Version, um dies zu beheben.


-Update 19. Juli

Leider brach AppCompat v22.2.1 die onKeyDown und onKeyUp Ereignisse again. Ich habe gerade AppCompatActivityMenuKeyInterceptor aktualisiert v22.1.x zu unterstützen und auch v22.2.1


-Update 29. Mai

Diese has been fixed im v22.2.0 von appcompat-v7 Support-Bibliothek. Aktualisieren Sie auf die letzte Version, um dies zu beheben.


Leider AppCompat v22.1.0 fängt die onKeyDown und onKeyUp Ereignisse und sie nicht propagieren, wenn die Menütaste gedrückt wird. Die einzige mögliche Lösung besteht darin, Reflection zu verwenden, um die Ereignisse onKeyDown und onKeyUp abzufangen, bevor die AppCompat tut.

Fügen Sie diese Klasse zu einem Projekt:

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

Anruf AppCompatActivityMenuKeyInterceptor.intercept(this) im onCreate Ihrer Aktivität:

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

Wenn Sie ProGuard verwenden oder DexGuard diese Regeln zu Ihrer Konfiguration hinzuzufügen:

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

Jetzt kann Ihre Aktivitätempfangenund onKeyUp Ereignis auch für die Menütaste.

+0

Ich habe ein Problem damit, nachdem ich meinen Code verschleiert habe. Die Support-Bibliothek zu halten schien zu helfen. Bitte kommentieren Sie dies, wenn dies falsch erscheint, aber ich habe meine proguard-rules.pro '-Konferenzklasse android.support.v7 hinzugefügt. ** {*; } -halte Schnittstelle android.support.v7. ** {*; } ' –

+1

@DaiwikDaarun Ja, Ihre Regeln sind korrekt, aber Sie behalten alle Support-Bibliotheken. Wenn Sie bestimmte Regeln haben möchten, können Sie die aktualisierte Antwort sehen. Vielen Dank für den Hinweis aus –

+1

Danke, du bist mein Tag gerettet – Dima

0

Anstelle von onKeyUp() oder onKeyDown() kann einfach dispatchKeyEvent() verwendet werden. Sehen Sie sich den folgenden Code von android-developers.blogspot.com an.

@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

Leider funktioniert Ihre Lösung nicht. Das 'dispatchKeyEvent' wird nicht weitergegeben, wenn die Menütaste gedrückt wird, während die v7-Unterstützungsbibliothek v22.1.0, v22.1.1 und v22.2.1 verwendet wird. Es ist ein bekannter Fehler und wurde in den neuesten Versionen der v7-Support-Bibliothek behoben. –

+0

Es bedeutet, dass die Hardware-Menü-Taste drücken nicht einmal auf 22.0.1 –

+0

Wie Sie im Titel sehen können, ist diese Frage speziell für v22.1.0 und höher (v22.1.0, v22.1.1 und v22.2.1) –