20

To, czego chcę, to to, że gdy użytkownik kliknie element listy w ListView, konwertuje na całą aktywność (jak widać w poniższym przykładzie), ale nie byłem w stanie znaleźć samouczka wyjaśniającego to i, faktycznie, Nie wiem, jak nazywa się ten ruch.Android - Jak utworzyć przejście z elementu w widoku listy do całej aktywności?

Innymi słowy, to, co chcę osiągnąć to:

  1. Wzrost elewacja Wykaz egzemplarzy, gdy zostanie kliknięty (jak widać w prawym gif)

  2. Rozszerzenie i przekształcić element listy do następnego układu fragment/aktywności, która zawiera szczegółowe informacje na temat klikniętego elementu

enter image description here

Próbowałem wielu przejść, ale bez powodzenia. Czy ktoś może mi pomóc, aby to osiągnąć?

+0

Czy rozważałeś użycie [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

W rzeczywistości używam następującego kodu: 'Opcje ActivityOptions = ActivityOptions.makeSceneTransitionAnimation (this, view, getString (R.string.transition_name));' – Antonio

+0

Możesz znaleźć o przeniesieniu tutaj https://developer.android.com /training/material/animations.html#Transitions – Prakash

Odpowiedz

15

zbudować małe zastosowanie przykładowego przejścia między dwoma działań z pożądanego efektu: Sample Application

Jednakże przemiany w określonych GIF są nieco różne. Przejście w gifie po lewej stronie powoduje przejście elementu listy do obszaru zawartości drugiego działania (pasek narzędzi pozostaje w miejscu). W gifie po prawej stronie przejście przekształca element listy na cały ekran drugiego działania. Poniższy kod przedstawia efekt w lewym gifie. Jednak powinno być możliwe dostosowanie rozwiązania z niewielkimi modyfikacjami, aby osiągnąć przejście w prawym gifie.

Uwaga: działa to tylko na Lollipop. Można jednak wyśmiewać inny efekt na starszych urządzeniach. Ponadto jedynym celem dostarczonego kodu jest pokazanie, jak można to zrobić. Nie używaj tego bezpośrednio w swojej aplikacji.

główną działalność:

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/przejście /):

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

Więc jakie są różne części tutaj: Mamy dwie czynności. Podczas przejścia cztery widoki są przenoszone między czynnościami.

  • Pasek narzędzi: podobnie jak w lewym gif pasek narzędzi nie porusza się wraz z resztą zawartości.

  • ListView elementem Widok -> staje się pogląd, zawartość DetailActivity

  • StatusBar i NavigationBar Tło: Jeśli nie dodawać tych poglądów do zestawu przeniesionych widoki będą one znikną iz powrotem w czasie przejście. Wymaga to jednak, aby opóźnić przejście wejścia (patrz TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)

W MainActivity się przeniesieni widoki są dodawane do wiązki, który jest wykorzystywany do rozpoczęcia DetailActivity. Ponadto przeniesione widoki muszą być nazwane (transitionName) w obu działaniach. Można to zrobić w układzie xml, a także programowo.

Domyślny zestaw przejść używany podczas przejścia elementu współdzielonego wpływa na różne aspekty widoku (na przykład: obejrzyj granice - patrz 2). Jednak różnice w wysokości widoku nie są animowane. Dlatego prezentowane rozwiązanie wykorzystuje niestandardową ElevationTransition.

+0

Niesamowita odpowiedź. Zasłużyłeś na nagrodę! Gratulacje. Chcę tylko dodać komentarz: Mam pewne problemy przy użyciu 'findViewById (android.R.id.navigationBarBackground)', ponieważ był pusty. Sposób, w jaki rozwiązać Dodanie następujący kod do xml przejściowego ' ' – Antonio

+1

Cieszę się, że odpowiedź była pomocna. Czy to nie powoduje migotania statusu i paska nawigacyjnego podczas przejścia? Innym rozwiązaniem może być tylko dodanie tych widoków do pakietu, jeśli nie są one zerowe. Mogę sobie wyobrazić, że element navigationBarBackground staje się pusty, ponieważ nie jest dostępny na wszystkich urządzeniach? –

+0

Właściwie masz rację. Nie zauważyłem, że pasek nawigacyjny migocze podczas przejścia. Postaram się dowiedzieć, dlaczego tak się dzieje. – Antonio

3

spróbować .. 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

Dzięki za odpowiedź, Carlos. Ale to, co chcę osiągnąć, to właśnie przejście przykładu, w którym element listy staje się działaniem. Próbowałem ustawić tę samą nazwę transitionName dla elementu listy i mojego najbardziej zewnętrznego rodzica, ale bez powodzenia. – Antonio

+0

Powinieneś zobaczyć przykład kodu źródłowego i zobaczyć, co jest innego .. https://github.com/lgvalle/Material-Animations/archive/master.zip –

+0

Dodałem przykład gif, który pokazuje dokładnie to, co chcę. Dzięki za twoją odpowiedź. To jest naprawdę interesujące, ale nie widzę, co chcę zrobić. – Antonio

0

Animacja trzeba nazywa aktywny przejścia między wspólnych elementów. Według badań stwierdziliśmy, że należy:

  1. Połóż widok ListView w relativeLayout
  2. OnClick, nadmuchać kopię renderujący
  3. Znajdź globalne współrzędne gdzie renderujący siedzi w stosunku do dominującą ListView
  4. Dodaj skopiowany renderujący do RelativeLayout (rodzica ListView)
  5. Animacja ListView dala
  6. na koniec t hat animuj, animuj swój nowy renderer
  7. Zysk!

    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

Dzięki za odpowiedź @Vishavjeet, ale w twoim przykładzie przedmiot jest właśnie skalowany. Potrzebuję jednak nowego okna z paskiem narzędzi, innymi widokami itp. – Antonio

0

Spróbuj spektakularny stronę @Getting Started with Activity & Fragment Transitions (part 1). Tutaj rozmawiali o Przejściach Aktywności i Fragmentów. Nie próbowałem tego. Uważam, że Fragment Transitions jest lepszy i mniej intensywny komputerowo, więc to dobry początek. Nie musisz zmieniać pasków narzędzi, możesz je pokazywać/ukrywać.

Kolejne dobre łącze SO to @Animate the transition between fragments, spójrz na najlepszą odpowiedź. W tym poście rozmawiali o obiekcie objectAnimator.

Inna opinia dotyczy opublikowanej animacji próbnej, nie pokazuje płynnej animacji z jednej sztuki na drugą. Jest mniej imponujący, gdy animacja nie jest płynna.

Życzymy powodzenia, baw się dobrze, informuj nas o wszystkim.

+0

Dziękuję za odpowiedź. Pracowałem z elementami współdzielonymi, ale nie wiem, jak zastosować je w animacji, którą chcę osiągnąć. Zmodyfikowałem swój wpis, dodając więcej informacji o tym, co chcę osiągnąć. – Antonio