20

Quello che voglio è che quando l'utente fa clic su una voce di elenco in un ListView, converte in un'attività intera (come puoi vedere nel seguente esempio), ma non sono riuscito a trovare un tutorial che spiegasse questo e, in realtà, Non so come si chiama questo movimento.Android - Come creare una transizione da una voce in listview a un'intera attività?

In altre parole, quello che voglio ottenere è:

  1. Aumento elevazione List Item quando si fa clic (come si può vedere nella gif destra)

  2. Espandi e trasformare elemento della lista al successivo layout di frammento/attività che contiene informazioni dettagliate circa l'oggetto cliccato

enter image description here

Ho provato molte transizioni ma senza fortuna. Qualcuno può aiutarmi a realizzare questo?

+0

Avete considerato l'utilizzo di [ActivityOptions.makeSceneTransitionAnimation] (http://developer.android.com/reference/android/app/ActivityOptions.html#makeSceneTransitionAnimation%28android.app.Activity,%20android.util.Pair%3Candroid. view.View,% 20java.lang.String% 3E% ... 29)? – jdebon

+0

In realtà, sto usando il seguente codice: 'Opzioni ActivityOptions = ActivityOptions.makeSceneTransitionAnimation (this, view, getString (R.string.transition_name));' – Antonio

+0

Puoi trovare informazioni sulla transizione qui https://developer.android.com /training/material/animations.html#Transitions – Prakash

risposta

15

I creare un'applicazione di esempio che le transizioni tra due attività con l'effetto desiderato: Sample Application

Tuttavia le transizioni nei GIF forniti sono leggermente diverse. La transizione nella gif sul lato sinistro transita l'elemento dell'elenco nell'area contenuto della seconda attività (la barra degli strumenti rimane in posizione). Nella gif sul lato destro la transizione trasforma l'elemento dell'elenco nello schermo completo della seconda attività. Il codice seguente fornisce l'effetto nella gif sinistra. Tuttavia dovrebbe essere possibile adattare la soluzione con piccole modifiche per ottenere la transizione nella giusta gif.

Nota questo funziona solo su Lollipop. Tuttavia è possibile simulare un effetto diverso sui dispositivi più vecchi. Inoltre l'unico scopo del codice fornito è mostrare come potrebbe essere fatto. Non usare questo direttamente nella tua app.

MainActivity:

public class MainActivity extends AppCompatActivity { 

    MyAdapter myAdapter; 

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

     setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); 
     ListView listView = (ListView) findViewById(R.id.list_view); 

     myAdapter = new MyAdapter(this, 0, DataSet.get()); 

     listView.setAdapter(myAdapter); 

     listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
      @Override 
      public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) { 
       startTransition(view, myAdapter.getItem(position)); 
      } 
     }); 
    } 

    private void startTransition(View view, Element element) { 
     Intent i = new Intent(MainActivity.this, DetailActivity.class); 
     i.putExtra("ITEM_ID", element.getId()); 

     Pair<View, String>[] transitionPairs = new Pair[4]; 
     transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar 
     transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen) 

     // We also want to transition the status and navigation bar barckground. Otherwise they will flicker 
     transitionPairs[2] = Pair.create(findViewById(android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME); 
     transitionPairs[3] = Pair.create(findViewById(android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME); 
     Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle(); 

     ActivityCompat.startActivity(MainActivity.this, i, b); 
    } 
} 

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

    <android.support.v7.widget.Toolbar 
     android:id="@+id/toolbar" 
     android:layout_width="match_parent" 
     android:layout_height="?attr/actionBarSize" 
     android:background="@color/colorPrimary" 
     android:transitionName="toolbar" /> 

    <ListView 
     android:id="@+id/list_view" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 

</LinearLayout> 

DetailActivity:

public class DetailActivity extends AppCompatActivity { 

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

     setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); 

     long elementId = getIntent().getLongExtra("ITEM_ID", -1); 
     Element element = DataSet.find(elementId); 


     ((TextView) findViewById(R.id.title)).setText(element.getTitle()); 
     ((TextView) findViewById(R.id.description)).setText(element.getDescription()); 

     // if we transition the status and navigation bar we have to wait till everything is available 
     TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this); 
     // set a custom shared element enter transition 
     TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition); 
    } 
} 

activity_detail.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

    <android.support.v7.widget.Toolbar 
     android:id="@+id/toolbar" 
     android:layout_width="match_parent" 
     android:layout_height="?attr/actionBarSize" 
     android:background="@color/colorPrimary" 
     android:transitionName="toolbar" /> 

    <LinearLayout 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:background="#abc" 
     android:orientation="vertical" 
     android:paddingBottom="200dp" 
     android:transitionName="content_area" 
     android:elevation="10dp"> 

     <TextView 
      android:id="@+id/title" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" /> 

     <TextView 
      android:id="@+id/description" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" /> 
    </LinearLayout> 
</LinearLayout> 

detail_activity_shared_element_enter_transition.xml (/ res/transizione /):

<?xml version="1.0" encoding="utf-8"?> 
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" 
    android:transitionOrdering="together"> 
    <changeBounds/> 
    <changeTransform/> 
    <changeClipBounds/> 
    <changeImageTransform/> 
    <transition class="my.application.transitions.ElevationTransition"/> 
</transitionSet> 

my.application.transitions.ElevationTransition:

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
public class ElevationTransition extends Transition { 

    private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation"; 

    public ElevationTransition() { 
    } 

    public ElevationTransition(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    @Override 
    public void captureStartValues(TransitionValues transitionValues) { 
     captureValues(transitionValues); 
    } 

    @Override 
    public void captureEndValues(TransitionValues transitionValues) { 
     captureValues(transitionValues); 
    } 

    private void captureValues(TransitionValues transitionValues) { 
     Float elevation = transitionValues.view.getElevation(); 
     transitionValues.values.put(PROPNAME_ELEVATION, elevation); 
    } 

    @Override 
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { 
     if (startValues == null || endValues == null) { 
      return null; 
     } 

     Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION); 
     Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION); 
     if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) { 
      return null; 
     } 

     final View view = endValues.view; 
     ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal); 
     a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
      @Override 
      public void onAnimationUpdate(ValueAnimator animation) { 
       view.setElevation((float)animation.getAnimatedValue()); 
      } 
     }); 

     return a; 
    } 
} 

TransitionHelper:

public class TransitionHelper { 

    public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) { 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) 
      return; 

     final View decor = activity.getWindow().getDecorView(); 
     if (decor == null) 
      return; 
     activity.postponeEnterTransition(); 
     decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
      @TargetApi(Build.VERSION_CODES.LOLLIPOP) 
      @Override 
      public boolean onPreDraw() { 
       decor.getViewTreeObserver().removeOnPreDrawListener(this); 
       activity.startPostponedEnterTransition(); 
       return true; 
      } 
     }); 
    } 

    public static void setSharedElementEnterTransition(final Activity activity, int transition) { 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) 
      return; 
     activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition)); 
    } 
} 

Quindi quali sono le diverse parti qui: Abbiamo due attività. Durante la transizione vengono passate quattro visualizzazioni tra le attività.

  • Barra degli strumenti: come nella gif sinistra la barra degli strumenti non si sposta con il resto del contenuto.

  • ListView elemento Visualizza -> diventa la vista contenuto del DetailActivity

  • StatusBar e NavigationBar fondo: se non aggiungiamo questi punti di vista al set di punti di vista la transizione saranno fade out e ritorno nel corso del transizione. Ciò richiede tuttavia di ritardare la transizione immettere (vedi: TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)

Nella MainActivity le viste la transizione vengono aggiunti al pacchetto che viene utilizzato per avviare il DetailActivity. Inoltre, le viste in transizione devono essere denominate (transitionName) in entrambe le attività. Questo può essere fatto sia nel layout xml che in modo programmatico.

L'insieme predefinito di transizioni, utilizzato durante la transizione dell'elemento condiviso, influisce su diversi aspetti della vista (ad esempio: limiti di vista - vedere 2). Tuttavia, le differenze nell'elevazione di una vista non sono animate. Questo è il motivo per cui la soluzione presentata utilizza l'ElevationTransition personalizzato.

+0

Sorprendente risposta. Ti sei guadagnato la taglia! Congratulazioni. Voglio solo aggiungere un commento: ho avuto alcuni problemi usando 'findViewById (android.R.id.navigationBarBackground)' perché era nullo. Il modo in cui lo risolvo: Aggiungendo il seguente codice in transizione xml ' ' – Antonio

+1

Felice di sentire che la risposta è stata utile. Questo non causa lo sfarfallio nello stato e nella barra di navigazione durante la transizione? Un'altra soluzione potrebbe essere quella di aggiungere queste viste al pacchetto solo se non sono nulle. Potrei immaginare che navigationBarBackground diventi nullo perché non è disponibile su tutti i dispositivi? –

+0

In realtà, hai ragione. Non avevo notato che la barra di navigazione lampeggia durante la transizione. Cercherò di capire perché sta succedendo questo. – Antonio

3

provare questo .. Material-Animations

blueIconImageView.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
     Intent i = new Intent(MainActivity.this, SharedElementActivity.class); 

     View sharedView = blueIconImageView; 
     String transitionName = getString(R.string.blue_name); 

     ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName); 
     startActivity(i, transitionActivityOptions.toBundle()); 
    } 
}); 

SharedElements

+0

Grazie per la risposta, Carlos. Ma quello che voglio realizzare è esattamente la transizione dell'esempio, in cui una voce dell'elenco diventa un'attività. Ho provato a impostare lo stesso transitionName per la mia voce di elenco e il mio genitore più esterno, ma senza fortuna. – Antonio

+0

Dovresti vedere l'esempio del codice sorgente e vedere cosa c'è di diverso .. https://github.com/lgvalle/Material-Animations/archive/master.zip –

+0

Ho aggiunto un esempio di gif che mostra esattamente quello che voglio. Grazie per la risposta. È davvero interessante ma non riesco a vedere cosa voglio fare – Antonio

0

l'animazione è necessario è chiamato Attività Transizioni tra gli elementi condivisi. Con ricerche ho scoperto che è necessario:

  1. Metti la tua vista ListView in un relativeLayout
  2. OnClick, gonfiare una copia del vostro renderer
  3. Trova le coordinate globali per cui il renderer siede in rapporto con il genitore del ListView
  4. Aggiungi il renderer copiato nella RelativeLayout (controllante di ListView)
  5. animare la listView via
  6. Sulla fine della t cappello animato, anima il tuo nuovo riproduttore
  7. Profitto!

    public class MainActivity extends Activity { 
    
    private RelativeLayout layout; 
         private ListView listView; 
         private MyRenderer selectedRenderer; 
    
         @Override 
         protected void onCreate(Bundle savedInstanceState) { 
          super.onCreate(savedInstanceState); 
          layout = new RelativeLayout(this); 
          setContentView(layout); 
          listView = new ListView(this); 
          RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); 
          layout.addView(listView, rlp); 
    
          listView.setAdapter(new MyAdapter()); 
          listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
           @Override 
           public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 
    
            // find out where the clicked view sits in relationship to the 
            // parent container 
            int t = view.getTop() + listView.getTop(); 
            int l = view.getLeft() + listView.getLeft(); 
    
            // create a copy of the listview and add it to the parent 
            // container 
            // at the same location it was in the listview 
            selectedRenderer = new MyRenderer(view.getContext()); 
            RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(), view 
              .getHeight()); 
            rlp.topMargin = t; 
            rlp.leftMargin = l; 
            selectedRenderer.textView.setText(((MyRenderer) view).textView.getText()); 
            layout.addView(selectedRenderer, rlp); 
            view.setVisibility(View.INVISIBLE); 
    
            // animate out the listView 
            Animation outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, 
              Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f, 
              Animation.RELATIVE_TO_SELF, 0f); 
            outAni.setDuration(1000); 
            outAni.setFillAfter(true); 
            outAni.setAnimationListener(new Animation.AnimationListener() { 
             @Override 
             public void onAnimationStart(Animation animation) { 
             } 
    
             @Override 
             public void onAnimationRepeat(Animation animation) { 
             } 
    
             @Override 
             public void onAnimationEnd(Animation animation) { 
              ScaleAnimation scaleAni = new ScaleAnimation(1f, 
                1f, 1f, 2f, 
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f); 
              scaleAni.setDuration(400); 
              scaleAni.setFillAfter(true); 
              selectedRenderer.startAnimation(scaleAni); 
             } 
            }); 
    
            listView.startAnimation(outAni); 
           } 
          }); 
         } 
    
         public class MyAdapter extends BaseAdapter { 
          @Override 
          public int getCount() { 
           return 10; 
          } 
    
          @Override 
          public String getItem(int position) { 
           return "Hello World " + position; 
          } 
    
          @Override 
          public long getItemId(int position) { 
           return position; 
          } 
    
          @Override 
          public View getView(int position, View convertView, ViewGroup parent) { 
           MyRenderer renderer; 
           if (convertView != null) 
            renderer = (MyRenderer) convertView; 
           else 
            renderer = new MyRenderer(MainActivity.this); 
           renderer.textView.setText(getItem(position)); 
           return renderer; 
          } 
         } 
    
         public class MyRenderer extends RelativeLayout { 
    
          public TextView textView; 
    
          public MyRenderer(Context context) { 
           super(context); 
           setPadding(20, 20, 20, 20); 
           setBackgroundColor(0xFFFF0000); 
    
           RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
             RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); 
           rlp.addRule(CENTER_IN_PARENT); 
           textView = new TextView(context); 
           addView(textView, rlp); 
          } 
    
         }  } 
    
+0

Grazie per la tua risposta, @Vishavjeet ma nel tuo esempio l'oggetto è appena stato ridimensionato. Ma quello di cui ho bisogno è una nuova finestra con una barra degli strumenti, altre viste, ecc. – Antonio

0

Prova questa pagina spettacolare @Getting Started with Activity & Fragment Transitions (part 1). Qui hanno parlato di Transizioni di attività e frammenti. Non l'ho provato La mia opinione è che Fragment Transitions sia migliore e meno dispendioso del computer, quindi è un buon inizio. E potrebbe non essere necessario cambiare le barre degli strumenti, è possibile mostrarle/nasconderle.

Un altro buon collegamento SO è @Animate the transition between fragments, guarda la risposta migliore. In quel post, hanno parlato di objectAnimator.

Un'altra opinione riguarda l'animazione campione che hai pubblicato, non mostra un'animazione fluida da un'arte all'altra. È meno impressionante quando l'animazione non è liscia.

Buona fortuna, buon divertimento, teneteci tutti postati.

+0

Grazie per la risposta. Ho lavorato con elementi condivisi ma non so come applicarli nell'animazione che voglio raggiungere. Ho modificato il mio post aggiungendo ulteriori informazioni su ciò che voglio raggiungere. – Antonio

Problemi correlati