2014-11-10 2 views
5

Voglio creare un'animazione in RecyclerView che richiede a un bambino di avere uno z-index più grande di un altro figlio. Tuttavia, quando ho utilizzato il metodo bringToFront in ViewGroup, non ha funzionato.bringToFront non funziona in RecyclerView

C'è un modo per avvicinare un bambino in RecyclerView?

risposta

4

L'ordine dei bambini è controllato dal LayoutManager. Costruito in LayoutManagers hanno il proprio ordine figlio rigoroso per motivi di prestazioni.

L'implementazione corrente di bringToFront sposta semplicemente il bambino alla fine dell'elenco dei bambini che non funzionerà correttamente con i LayoutManager esistenti.

Se si sta prendendo di mira Lollipop, è possibile utilizzare il metodo setElevation. Per targetizzare piattaforme precedenti, avrete bisogno di un LayoutManager personalizzato che non si basa sull'ordine dei bambini.

+1

Grazie, ma un costume LayoutManager può sollevare una certa complessità, sono riuscito a fare qualche trucco per clonare la vista che voglio animare. – Juude

7

Infatti, bringToFront() non aiuta; cambia semplicemente l'ordine dei bambini nella matrice figli dei suoi genitori, ma non cambia l'ordine in cui i bambini sono resi.

Se si utilizza API-21 o superiore, è possibile modificare il valore Z della vista (view.setZ(..)) per portarlo in primo piano.

Sotto API-21 è disponibile una richiamata che è possibile utilizzare: RecyclerView.ChildDrawingOrderCallback. Con esso puoi definire l'ordine in cui i bambini devono essere sottoposti a rendering. Puoi anche utilizzarlo con API-21 + nel caso non ti piaccia l'ombra extra che ottieni cambiando il valore Z.)

Esempio:

final int indexOfFrontChild = rv.indexOfChild(desiredFrontView); 
rv.setChildDrawingOrderCallback(new RecyclerView.ChildDrawingOrderCallback() { 

    private int nextChildIndexToRender; 

    @Override 
    public int onGetChildDrawingOrder(int childCount, int iteration) { 
     if (iteration == childCount - 1) { 
      // in the last iteration return the index of the child 
      // we want to bring to front (and reset nextChildIndexToRender) 
      nextChildIndexToRender = 0; 
      return indexOfFrontChild; 
     } else { 
      if (nextChildIndexToRender == indexOfFrontChild) { 
       // skip this index; we will render it during last iteration 
       nextChildIndexToRender++; 
      } 
      return nextChildIndexToRender++; 
     } 
    } 
}); 
rv.invalidate(); 

in sostanza, onGetChildDrawingOrder(...) si chiamerà childCount volte e in ogni iteration possiamo specificare quale bambino da rendere (restituendo il suo indice).

+1

Ho provato questo codice. Rende l'app bloccata dopo averla utilizzata durante il tentativo di scorrere RecyclerView. – 0101100101

+0

@ 0101100101, sei sicuro che indexOfFrontChild si trova nell'intervallo [0, childCount-1]? O per chiederlo in modo diverso, sei sicuro che il tuo preferito ViewView è figlio di RecyclerView? –

+1

Entrambi sì. L'animazione funziona davvero perfettamente con quel codice, solo scorrendo dopo non lo è. – 0101100101

0

Risposta sopra da @Zsolt Safrany si basa sul presupposto che il callback sia sempre chiamato dall'iterazione 0, che come ho verificato non è vero.

Qui inserisco un'implementazione funzionante che non viene inoltrata sul contatore esterno.

//index of item you want to be displayed on top 
private int indexOfFrontChild = 2; 

    @Override 
     public int onGetChildDrawingOrder(int childCount, int iteration) { 
      int childPos = iteration; 
      //index must be in at least smaller than all children's 
      if (indexOfFrontChild < childCount) { 
       //in the last iteration we return view we want to have on top 
       if (iteration == childCount - 1) { 
        childPos = indexOfFrontChild; 
       }else if (iteration >= indexOfFrontChild) { 
        childPos = iteration + 1; 
       } 
      } 
      return childPos; 
     } 

Nei miei articoli di casi sono stati in grado di cambiare la loro altezza in modo 1 articolo potrebbe prendere tutto lo schermo. Quindi è bello per verificare se l'oggetto indexOfFrontChild è visualizzabile

if (indexOfFrontChild < childCount)