2014-10-25 1 views
50

Я пытаюсь реализовать переходы между фрагментами, которые имеют «общие элементы», как описано в новых спецификациях дизайна материалов. Единственный способ, который я могу найти, это ActivityOptionsCompat.makeSceneTransitionAnimation, который, я считаю, работает только с Activity. Я искал эту же функциональность, но с/для фрагментов.Как начать общий переход элементов с помощью фрагментов?

+1

Вы проверили FragmentTransaction.addSharedElement -method; http://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html#addSharedElement(android.view.View,java.lang.String)? – harism

+0

Я попытался использовать это на самом деле, но он, похоже, не работал, по крайней мере, из изображения в элементе listview. Вероятно, существует множество недокументированных ограничений. Однако отключение переходов и анимаций в транзакции не помогло. – sbaar

+1

Мне также не удалось заставить его работать с ImageViews внутри элементов списка. Мне удалось собрать очень простое действие с двумя полноэкранными фрагментами. Каждый фрагмент имел 2 вида с черным фоном в разных размерах и положениях, и когда я нажимаю на экран, он переключает фрагмент. В этом случае общие элементы ожидали, как ожидалось. Так что это работает, возможно, нет, когда ваше представление находится в элементе списка. Интересно, это потому, что элементы списка неизвестны до времени выполнения? – brockoli

ответ

39

У меня была такая же проблема, но она работала, добавляя новый фрагмент из другого фрагмента. Следующая страница этого раздела очень полезна для начала работы: https://developer.android.com/training/material/animations.html#Transitions

Следующий мой код, который работает. Я анимация ImageView от одного фрагмента к другому. Убедитесь, что View, который вы хотите оживить, имеет одинаковые android:transitionName в обоих фрагментах. Другое содержание не имеет большого значения.

В качестве теста вы можете скопировать его в оба ваших XML-файла макета. Убедитесь, что изображение существует.

<ImageView 
android:transitionName="MyTransition" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:scaleType="centerCrop" 
android:src="@drawable/test_image" /> 

Тогда у меня есть 1 файл в моем res/transition папку с именем change_image_transform.xml.

<?xml version="1.0" encoding="utf-8"?> 
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> 
    <changeImageTransform /> 
</transitionSet> 

Теперь вы можете начать. Допустим, у вас есть фрагмент А, содержащий изображение и хотите добавить фрагмент B.

Запустить в Fragment A:

@Override 
public void onClick(View v) { 
    switch(v.getId()) { 
     case R.id.product_detail_image_click_area: 
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
       setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform)); 
       setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode)); 

       // Create new fragment to add (Fragment B) 
       Fragment fragment = new ImageFragment(); 
       fragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform)); 
       fragment.setEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.explode)); 

       // Our shared element (in Fragment A) 
       mProductImage = (ImageView) mLayout.findViewById(R.id.product_detail_image); 

       // Add Fragment B 
       FragmentTransaction ft = getFragmentManager().beginTransaction() 
         .replace(R.id.container, fragment) 
         .addToBackStack("transaction") 
         .addSharedElement(mProductImage, "MyTransition"); 
       ft.commit(); 
      } 
      else { 
       // Code to run on older devices 
      } 
      break; 
    } 
} 
+2

Это работает для меня, за исключением того, что он всегда запускает анимацию в верхней части экрана во втором фрагменте. Поэтому, если у меня есть представление в каждом элементе моего списка (или в моем случае RecyclerView), и вы нажимаете на него в нижней части списка, а новое место для этого представления во втором фрагменте находится в нижней части экрана, это фактически анимирует сверху вниз, вместо того, чтобы анимировать из места на экране в первом фрагменте, где началось представление. Кто-нибудь знает, почему это? – brockoli

+0

Я думаю, это потому, что в виде списка многократно содержатся одни и те же элементы. Если вы выполните findViewById, первый элемент вернется. Я думаю, вы должны однозначно идентифицировать анимированный вид, назначая dynamicName динамически при нажатии на элемент. – ar34z

+2

Я назначаю уникальный идентификатор для каждого экземпляра в своем адаптере. Я добавляю позицию позиции. Это, как говорится, я решил свою проблему. Как оказалось, вам нужно включить ChangeTransform в свой TransitionSet. Это указывает системе сохранить начальную позицию выбранного вами вида и использовать это как начальную позицию для анимации в новом фрагменте. – brockoli

19

Я отправляю это как ответ, так как я новичок здесь и не могу комментировать.

Переходы фрагмента общего элемента do работают с ListViews, если исходное и целевое представления имеют одинаковое (и уникальное) имя перехода.

Если вы сделаете свой список вид адаптера установить уникальные transitionNames мнение, которые вы хотите (например, некоторая константа + конкретный идентификатор элемента) , а также изменить фрагмент детали, чтобы установить то же transitionNames на целевых просмотры во время выполнения (onCreateView), переходы действительно работают!

+3

Это исправлено для меня. Теперь я могу анимировать свои общие ImageViews из одного фрагмента в другой в рамках одной и той же Activity. Теперь моя проблема в том, что мои ImageViews не переводятся на новые позиции, они просто появляются в новом месте, но запускают анимацию масштаба. Я использую setSharedElementEnterTransition (новый ChangeBounds()); когда я создаю экземпляр фрагмента. Аналогичное поведение с ChangeImageTransform() – brockoli

+3

Возможно ли получить часть рабочего кода, чтобы проверить это? Я пробовал какое-то время, и ничего не работает. – FMontano

+0

для меня, даже если каждый из изображений имеет одно и то же имя перехода, он работает. он работает нормально, если изображение все еще находится в памяти и только после первого появления анимации. Уловка, так как я использую загруженные изображения, вы можете сначала просмотреть сводку изображения для резервной, прежде чем загружать правильный. Тем не менее, я должен принудительно загрузить одно и то же изображение в фрагменте B, передав URL-адрес изображения в качестве аргумента из фрагмента A. – takecare

10

Общие элементы работают с фрагментами, но есть некоторые вещи, которые нужно иметь в виду:

  1. Не пытайтесь установить sharedElementsTransition в onCreateView вашего фрагмента. Вы должны определить их при создании экземпляра вашего фрагмента или в onCreate.

  2. Обратите внимание на официальную документацию о возможных анимациях для переходов ввода/выхода & sharedElementTransition. Они не одинаковы.

  3. проб и ошибок :)

2

Это должно быть комментарий к принятому ответ, так как я не могу прокомментировать.

Принятый ответ (от WindsurferOak и ar34z) работает, за исключением «незначительной» проблемы, которая вызвала исключение нулевого указателя при навигации с backStack. Кажется, что setSharedElementReturnTransition() следует вызывать на целевом фрагменте вместо исходного фрагмента.

Таким образом, вместо:

setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform)); 

должно быть

fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform)); 

https://github.com/tevjef/Rutgers-Course-Tracker/issues/8

+0

Мастов и Мэтт, спасибо за ответ. См. Мое редактирование. –

-1

Ключ использовать пользовательскую транзакцию с

transaction.addSharedElement(sharedElement, "sharedImage"); 

Shared элемент перехода между двумя фрагментами

В этом примере, один из двух различных ImageViews следует перевести с ChooserFragment - DetailFragment.

В ChooserFragment макете нам нужны уникальные transitionName атрибуты:

<ImageView 
    android:id="@+id/image_first" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:src="@drawable/ic_first" 
    android:transitionName="fistImage" /> 

<ImageView 
    android:id="@+id/image_second" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:src="@drawable/ic_second" 
    android:transitionName="secondImage" /> 

В ChooserFragments классе, нам нужно пройти View, который щелкнул и идентификатор родительского Activity которым обрабатывает замена фрагменты (нам нужен идентификатор, чтобы узнать, какой ресурс изображения будет отображаться в DetailFragment). Как подробно передавать информацию в родительскую деятельность, безусловно, рассматривается в другой документации.

view.findViewById(R.id.image_first).setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View view) { 
     if (mCallback != null) { 
      mCallback.showDetailFragment(view, 1); 
     } 
    } 
}); 

view.findViewById(R.id.image_second).setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View view) { 
     if (mCallback != null) { 
      mCallback.showDetailFragment(view, 2); 
     } 
    } 
}); 

В DetailFragment, то ImageView совместно используемого элемента также нуждается уникальный transitionName атрибут.

<ImageView 
    android:id="@+id/image_shared" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_gravity="center" 
    android:transitionName="sharedImage" /> 

В onCreateView() способе DetailFragment, мы должны решить, какое изображение ресурс должен быть показаны (если мы не делаем этого, общий элемент исчезнет после перехода).

public static DetailFragment newInstance(Bundle args) { 
    DetailFragment fragment = new DetailFragment(); 
    fragment.setArguments(args); 
    return fragment; 
} 

@Nullable 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    super.onCreateView(inflater, container, savedInstanceState); 
    View view = inflater.inflate(R.layout.fragment_detail, container, false); 

    ImageView sharedImage = (ImageView) view.findViewById(R.id.image_shared); 

    // Check which resource should be shown. 
    int type = getArguments().getInt("type"); 

    // Show image based on the type. 
    switch (type) { 
     case 1: 
      sharedImage.setBackgroundResource(R.drawable.ic_first); 
      break; 

     case 2: 
      sharedImage.setBackgroundResource(R.drawable.ic_second); 
      break; 
    } 

    return view; 
} 

Родитель Activity является получение обратных вызовов и обрабатывает замену фрагментов.

@Override 
public void showDetailFragment(View sharedElement, int type) { 
    // Get the chooser fragment, which is shown in the moment. 
    Fragment chooserFragment = getFragmentManager().findFragmentById(R.id.fragment_container); 

    // Set up the DetailFragment and put the type as argument. 
    Bundle args = new Bundle(); 
    args.putInt("type", type); 
    Fragment fragment = DetailFragment.newInstance(args); 

    // Set up the transaction. 
    FragmentTransaction transaction = getFragmentManager().beginTransaction(); 

    // Define the shared element transition. 
    fragment.setSharedElementEnterTransition(new DetailsTransition()); 
    fragment.setSharedElementReturnTransition(new DetailsTransition()); 

    // The rest of the views are just fading in/out. 
    fragment.setEnterTransition(new Fade()); 
    chooserFragment.setExitTransition(new Fade()); 

    // Now use the image's view and the target transitionName to define the shared element. 
    transaction.addSharedElement(sharedElement, "sharedImage"); 

    // Replace the fragment. 
    transaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()); 

    // Enable back navigation with shared element transitions. 
    transaction.addToBackStack(fragment.getClass().getSimpleName()); 

    // Finally press play. 
    transaction.commit(); 
} 

Не следует забывать - сама Transition. Этот пример перемещает и масштабирует общий элемент.

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
public class DetailsTransition extends TransitionSet { 

    public DetailsTransition() { 
     setOrdering(ORDERING_TOGETHER); 
     addTransition(new ChangeBounds()). 
      addTransition(new ChangeTransform()). 
      addTransition(new ChangeImageTransform()); 
    } 

} 
Смежные вопросы