2015-08-31 13 views
5

UPDATEappcompat-v7 v23.0.0 barra di stato di colore nero quando in ActionMode

Stesso problema presente nella sua ultima Gmail. Non riesco ancora a capire perché Google dovrebbe apportare modifiche così spiacevoli all'interfaccia utente. Ossessivo in me impazzisce ogni volta che lo vedo

DOMANDA

ho questo problema strano con appcompat-v7 23. Problema che sto per descrivere non accade con 22 serie

È possibile ottenere il codice sorgente che riproduce questa forma issuse https://github.com/devserv/t/ una volta costruito, è possibile toccare e tenere premuto un elemento nella lista per attivare ActionMode

Edizione:

In modalità Action, appcompat trasforma la barra di stato in nero. Questo non accade se non utilizzare i seguenti

<item name="android:statusBarColor">@android:color/transparent</item> 
<item name="android:windowDrawsSystemBarBackgrounds">true</item> 

nel mio stile V21 ma devo usarlo perché voglio che il mio cassetto navigazione a guardare dietro la barra di stato.


Ho usato per usare dopo per evitare la barra di stato nero quando ActionMode avviato e concluso

public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.appColorPrimaryDark)); 
    } 

}

public void onDestroyActionMode(ActionMode actionMode) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     getActivity().getWindow().setStatusBarColor(getResources().getColor(android.R.color.transparent)); 
    } 

    mMode = null; 
} 

Sopra il codice non ha creato/barra di stato evitato anneriscano, ma non funziona correttamente su v23 di appcompat. Invece vedi una breve barra di stato nera mentre ActionMode viene distrutto. Sembra correlato all'animazione che viene riprodotta quando ActionMode viene distrutto.

ho cercato di aprire segnalazioni di bug, ma è stata rifiutata con commento

Don't re-create bugs. 

Mi sto perdendo qualcosa?

Qui ci sono le schermate per la modalità normale e l'azione

normali enter image description here

in ActionMode enter image description here

+0

Hai provato con la versione 23.0.1? È stato distribuito oggi. Qual è il livello API del tuo dispositivo? –

+0

Sì, sempre lo stesso. Non accettano nemmeno il bug report – nLL

risposta

5

Nel caso in cui solo il colore è il problema, è possibile modificarlo. Solo per una risorsa a colori fissa.

<color name="abc_input_method_navigation_guard" tools:override="true">@color/primary_dark</color> 

Ovvio ?colorPrimaryDark non funziona, nemmeno API 21.


La vista responsabile per il nero barra di stato di fondo è memorizzato in AppCompatDelegateImplV7.mStatusGuard. È possibile ottenere il delegato chiamando getDelegate() dalla propria attività e accedere al campo mStatusGuard tramite riflessione. Dopo aver avviato la modalità azione, puoi ottenere un riferimento a questa vista e personalizzarla come preferisci.

Questo è stato trovato in AppCompat 24.1.1.

1

Nessuno? Ecco la soluzione che ho trovato. Ritardo.

@Override 
    public void onDestroyActionMode(ActionMode mode) { 

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
      new Handler().postDelayed(new Runnable() { 
       @Override 
       public void run() { 
        try { 
         getActivity().getWindow().setStatusBarColor(getResources().getColor(android.R.color.transparent)); 
        } 
        catch(Exception e) 
        { 
         e.printStackTrace(); 
        } 
       } 
      }, 400); 

     } 
     mActionMode = null; 

    } 
6

La versione 23.0.0 di v7 appcompat library introdotto un'animazione che svanisce dentro e fuori la modalità di azione quando è iniziato e finito come potete leggere here:

La modalità di azione ha dissolvenze in ed è funziona come previsto.

Le modifiche vengono apportate nel metodo onDestroyActionMode in AppCompatDelegateImplV7:

public void onDestroyActionMode(ActionMode mode) { 
    mWrapped.onDestroyActionMode(mode); 
    if (mActionModePopup != null) { 
     mWindow.getDecorView().removeCallbacks(mShowActionModePopup); 
     mActionModePopup.dismiss(); 
    } else if (mActionModeView != null) { 
     mActionModeView.setVisibility(View.GONE); 
     if (mActionModeView.getParent() != null) { 
      ViewCompat.requestApplyInsets((View) mActionModeView.getParent()); 
     } 
    } 
    if (mActionModeView != null) { 
     mActionModeView.removeAllViews(); 
    } 
    if (mAppCompatCallback != null) { 
     mAppCompatCallback.onSupportActionModeFinished(mActionMode); 
    } 
    mActionMode = null; 
} 

Nella versione 23.0.0 è stato cambiato in:

public void onDestroyActionMode(ActionMode mode) { 
    mWrapped.onDestroyActionMode(mode); 
    if (mActionModePopup != null) { 
     mWindow.getDecorView().removeCallbacks(mShowActionModePopup); 
    } 

    if (mActionModeView != null) { 
     endOnGoingFadeAnimation(); 
     mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0f); 
     mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() { 
      @Override 
      public void onAnimationEnd(View view) { 
       mActionModeView.setVisibility(View.GONE); 
       if (mActionModePopup != null) { 
        mActionModePopup.dismiss(); 
       } else if (mActionModeView.getParent() instanceof View) { 
        ViewCompat.requestApplyInsets((View) mActionModeView.getParent()); 
       } 
       mActionModeView.removeAllViews(); 
       mFadeAnim.setListener(null); 
       mFadeAnim = null; 
      } 
     }); 
    } 
    if (mAppCompatCallback != null) { 
     mAppCompatCallback.onSupportActionModeFinished(mActionMode); 
    } 
    mActionMode = null; 
} 

Come si può vedere mWrapped.onDestroyActionMode(mode); è chiamato immediatamente, non quando l'animazione finisce. Questo è ciò che causa la barra di stato nera nella tua app e in altre app come Gmail e Keep.

La soluzione che hai trovato funziona, ma sfortunatamente non è affidabile, perché se l'animazione richiede più tempo puoi comunque vedere la barra di stato nera.

Penso che Google dovrebbe correggere il problema e chiamare onDestroyActionMode solo quando l'animazione è davvero finita. Nel frattempo è possibile modificare questo comportamento con un po 'di riflessioni. E 'necessario sostituire onSupportActionModeStarted nella vostra attività e chiamare il metodo fixActionModeCallback:

@Override 
public void onSupportActionModeStarted(ActionMode mode) { 
    super.onSupportActionModeStarted(mode); 

    //Call this method 
    fixActionModeCallback(this, mode); 
} 

private void fixActionModeCallback(AppCompatActivity activity, ActionMode mode) { 
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) 
     return; 

    if (!(mode instanceof StandaloneActionMode)) 
     return; 

    try { 
     final Field mCallbackField = mode.getClass().getDeclaredField("mCallback"); 
     mCallbackField.setAccessible(true); 
     final Object mCallback = mCallbackField.get(mode); 

     final Field mWrappedField = mCallback.getClass().getDeclaredField("mWrapped"); 
     mWrappedField.setAccessible(true); 
     final ActionMode.Callback mWrapped = (ActionMode.Callback) mWrappedField.get(mCallback); 

     final Field mDelegateField = AppCompatActivity.class.getDeclaredField("mDelegate"); 
     mDelegateField.setAccessible(true); 
     final Object mDelegate = mDelegateField.get(activity); 

     mCallbackField.set(mode, new ActionMode.Callback() { 

      @Override 
      public boolean onCreateActionMode(android.support.v7.view.ActionMode mode, Menu menu) { 
       return mWrapped.onCreateActionMode(mode, menu); 
      } 

      @Override 
      public boolean onPrepareActionMode(android.support.v7.view.ActionMode mode, Menu menu) { 
       return mWrapped.onPrepareActionMode(mode, menu); 
      } 

      @Override 
      public boolean onActionItemClicked(android.support.v7.view.ActionMode mode, MenuItem item) { 
       return mWrapped.onActionItemClicked(mode, item); 
      } 

      @Override 
      public void onDestroyActionMode(final android.support.v7.view.ActionMode mode) { 
       Class mDelegateClass = mDelegate.getClass().getSuperclass(); 
       Window mWindow = null; 
       PopupWindow mActionModePopup = null; 
       Runnable mShowActionModePopup = null; 
       ActionBarContextView mActionModeView = null; 
       AppCompatCallback mAppCompatCallback = null; 
       ViewPropertyAnimatorCompat mFadeAnim = null; 
       android.support.v7.view.ActionMode mActionMode = null; 

       Field mFadeAnimField = null; 
       Field mActionModeField = null; 

       while (mDelegateClass != null) { 
        try { 
         if (TextUtils.equals("AppCompatDelegateImplV7", mDelegateClass.getSimpleName())) { 
          Field mActionModePopupField = mDelegateClass.getDeclaredField("mActionModePopup"); 
          mActionModePopupField.setAccessible(true); 
          mActionModePopup = (PopupWindow) mActionModePopupField.get(mDelegate); 

          Field mShowActionModePopupField = mDelegateClass.getDeclaredField("mShowActionModePopup"); 
          mShowActionModePopupField.setAccessible(true); 
          mShowActionModePopup = (Runnable) mShowActionModePopupField.get(mDelegate); 

          Field mActionModeViewField = mDelegateClass.getDeclaredField("mActionModeView"); 
          mActionModeViewField.setAccessible(true); 
          mActionModeView = (ActionBarContextView) mActionModeViewField.get(mDelegate); 

          mFadeAnimField = mDelegateClass.getDeclaredField("mFadeAnim"); 
          mFadeAnimField.setAccessible(true); 
          mFadeAnim = (ViewPropertyAnimatorCompat) mFadeAnimField.get(mDelegate); 

          mActionModeField = mDelegateClass.getDeclaredField("mActionMode"); 
          mActionModeField.setAccessible(true); 
          mActionMode = (android.support.v7.view.ActionMode) mActionModeField.get(mDelegate); 

         } else if (TextUtils.equals("AppCompatDelegateImplBase", mDelegateClass.getSimpleName())) { 
          Field mAppCompatCallbackField = mDelegateClass.getDeclaredField("mAppCompatCallback"); 
          mAppCompatCallbackField.setAccessible(true); 
          mAppCompatCallback = (AppCompatCallback) mAppCompatCallbackField.get(mDelegate); 

          Field mWindowField = mDelegateClass.getDeclaredField("mWindow"); 
          mWindowField.setAccessible(true); 
          mWindow = (Window) mWindowField.get(mDelegate); 
         } 

         mDelegateClass = mDelegateClass.getSuperclass(); 
        } catch (NoSuchFieldException e) { 
         e.printStackTrace(); 
        } catch (IllegalAccessException e) { 
         e.printStackTrace(); 
        } 
       } 

       if (mActionModePopup != null) { 
        mWindow.getDecorView().removeCallbacks(mShowActionModePopup); 
       } 

       if (mActionModeView != null) { 
        if (mFadeAnim != null) { 
         mFadeAnim.cancel(); 
        } 

        mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0.0F); 

        final PopupWindow mActionModePopupFinal = mActionModePopup; 
        final ActionBarContextView mActionModeViewFinal = mActionModeView; 
        final ViewPropertyAnimatorCompat mFadeAnimFinal = mFadeAnim; 
        final AppCompatCallback mAppCompatCallbackFinal = mAppCompatCallback; 
        final android.support.v7.view.ActionMode mActionModeFinal = mActionMode; 
        final Field mFadeAnimFieldFinal = mFadeAnimField; 
        final Field mActionModeFieldFinal = mActionModeField; 

        mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() { 
         public void onAnimationEnd(View view) { 
          mActionModeViewFinal.setVisibility(View.GONE); 
          if (mActionModePopupFinal != null) { 
           mActionModePopupFinal.dismiss(); 
          } else if (mActionModeViewFinal.getParent() instanceof View) { 
           ViewCompat.requestApplyInsets((View) mActionModeViewFinal.getParent()); 
          } 

          mActionModeViewFinal.removeAllViews(); 
          mFadeAnimFinal.setListener((ViewPropertyAnimatorListener) null); 

          try { 
           if (mFadeAnimFieldFinal != null) { 
            mFadeAnimFieldFinal.set(mDelegate, null); 
           } 
          } catch (IllegalAccessException e) { 
           e.printStackTrace(); 
          } 

          mWrapped.onDestroyActionMode(mode); 

          if (mAppCompatCallbackFinal != null) { 
           mAppCompatCallbackFinal.onSupportActionModeFinished(mActionModeFinal); 
          } 

          try { 
           if (mActionModeFieldFinal != null) { 
            mActionModeFieldFinal.set(mDelegate, null); 
           } 
          } catch (IllegalAccessException e) { 
           e.printStackTrace(); 
          } 
         } 
        }); 
       } 
      } 
     }); 

    } catch (NoSuchFieldException e) { 
     e.printStackTrace(); 
    } catch (IllegalAccessException e) { 
     e.printStackTrace(); 
    } 
} 
+0

Ero sicuro che l'animazione lo causasse, (vedi le mie risposte sotto) la tua risposta lo conferma. Quello che non capisco è quello che stanno insinuando che funzioni come previsto. Chiaramente no. – nLL

+0

Penso che non abbiano capito chiaramente il problema. Il problema non è l'animazione stessa (che ovviamente funziona come previsto) ma il fatto che 'onDestroyActionMode' è chiamato troppo presto. –

+0

Spero che chris-bane ne sia a conoscenza – nLL

Problemi correlati