Я пытаюсь реализовать переходы между фрагментами, которые имеют "общие элементы", как описано в спецификациях нового материала. Единственный метод, который я могу найти в ActivityOptionsCompat.makeSceneTransitionAnimation, который я считаю, работает только на активность. Я искал эту же функциональность, но с фрагментами/для фрагментов.
Как начать общий переход элементов с помощью фрагментов?
Ответ 1
У меня была такая же проблема, но она работала, добавляя новый фрагмент из другого фрагмента. Следующая ссылка очень полезна в этом: 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>
Теперь вы можете начать. Допустим, у вас есть фрагмент A, содержащий изображение, и вы хотите добавить фрагмент B.
Запустите это в фрагменте 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
Я отправляю это как ответ, так как я здесь новый и не могу комментировать.
Переходы фрагментов разделяемых элементов работают с ListViews, если исходное и целевое представления имеют одно и то же (и уникальное) имя перехода.
Если вы сделаете свой адаптер просмотра списка для установки уникальных имен перехода в нужные вам виды (например, некоторый константный + определенный идентификатор элемента), а также измените фрагмент детали, чтобы установить те же самые имена перехода в целевые представления во время выполнения (onCreateView), переходы действительно работают!
Ответ 3
Общие элементы работают с фрагментами, но есть некоторые вещи, о которых нужно помнить:
-
Не пытайтесь установить
sharedElementsTransition
вonCreateView
вашего фрагмента. Вы должны определить их при создании экземпляра вашего фрагмента или вonCreate
. -
Обратите внимание на официальную документацию о возможных анимациях для переходов ввода/вывода и sharedElementTransition. Они не то же самое.
-
Судебная и ошибка:)
Ответ 4
Это должен быть комментарий к принятому ответу, поскольку я не могу прокомментировать его.
Принятый ответ (от 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));
Ответ 5
Ниже приведены некоторые полезные ресурсы:
Ответ 6
Ключ заключается в использовании пользовательской транзакции с
transaction.addSharedElement(sharedElement, "sharedImage");
Переход между двумя фрагментами между двумя фрагментами
В этом примере один из двух разных 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 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());
}
}
Ответ 7
Я искал SharedElement во фрагментах и нашел очень полезный исходный код на GitHub.
1. Сначала вы должны определить transitionName для своих объектов (например, ImageView) в макете обоих фрагментов (мы добавляем кнопку во фрагменте A для обработки события нажатия):
фрагмент A:
<ImageView
android:id="@+id/fragment_a_imageView"
android:layout_width="128dp"
android:layout_height="96dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="80dp"
android:scaleType="centerCrop"
android:src="@drawable/gorilla"
android:transitionName="@string/simple_fragment_transition />
<Button
android:id="@+id/fragment_a_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"
android:text="@string/gorilla" />
фрагмент B:
<ImageView
android:id="@+id/fragment_b_image"
android:layout_width="match_parent"
android:layout_height="250dp"
android:scaleType="centerCrop"
android:src="@drawable/gorilla"
android:transitionName="@string/simple_fragment_transition" />
- Затем вы должны написать этот код в файле перехода в каталоге перехода (если у вас нет этого каталога, создайте его: res> new> Каталог ресурсов Android> Тип ресурса = переход> имя = change_image_transform):
change_image_transform.xml:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
</transitionSet>
- На последнем шаге вы должны заполнить коды в Java:
фрагмент A:
public class FragmentA extends Fragment {
public static final String TAG = FragmentA.class.getSimpleName();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ImageView imageView = (ImageView) view.findViewById(R.id.fragment_a_imageView);
Button button = (Button) view.findViewById(R.id.fragment_a_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getFragmentManager()
.beginTransaction()
.addSharedElement(imageView, ViewCompat.getTransitionName(imageView))
.addToBackStack(TAG)
.replace(R.id.content, new FragmentB())
.commit();
}
});
}
}
фрагмент B:
public class FragmentB extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
}
не забудьте показать свой "А" фрагмент в своей деятельности:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.content, new SimpleFragmentA())
.commit();
}
источник: https://github.com/mikescamell/shared-element-transitions