Подтвердить что ты не робот

Переходы общего элемента с фрагментами не работают с ViewPager

Мое приложение содержит представление, состоящее из ViewPager, состоящего из нескольких фрагментов. Когда вы нажимаете на элемент в одном из этих фрагментов, ожидаемое поведение для общего элемента (в данном случае изображения) для перехода к фрагменту, который отображает больше информации о кликнутом контенте.

Вот очень простое видео о том, как оно должно выглядеть:

https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-114842.mp4

Это просто использование перехода фрагмента → фрагмента.

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

https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-120029.mp4

Я почти уверен, что проблема здесь, как я объяснил выше, - это менеджер дочерних фрагментов и менеджер фрагментов активности. Вот как я делаю переход:

SimpleFragment fragment = new SimpleFragment();

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.am_list_pane, fragment, fragment.getClass().getSimpleName());

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    TransitionSet enterTransition = new TransitionSet();
    enterTransition.addTransition(new ChangeBounds());
    enterTransition.addTransition(new ChangeClipBounds());
    enterTransition.addTransition(new ChangeImageTransform());
    enterTransition.addTransition(new ChangeTransform());

    TransitionSet returnTransition = new TransitionSet();
    returnTransition.addTransition(new ChangeBounds());
    returnTransition.addTransition(new ChangeClipBounds());
    returnTransition.addTransition(new ChangeImageTransform());
    returnTransition.addTransition(new ChangeTransform());

    fragment.setSharedElementEnterTransition(enterTransition);
    fragment.setSharedElementReturnTransition(returnTransition);

    transaction.addSharedElement(iv, iv.getTransitionName());
}

transaction.addToBackStack(fragment.getClass().getName());

transaction.commit();

Это отлично работает, когда оба фрагмента управляются менеджером фрагментов активности, но когда я загружаю ViewPager следующим образом:

ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
pager.setAdapter(new Adapter(getChildFragmentManager()));

Дети ViewPager не управляются действием, и он больше не работает.

Это надзор со стороны команды Android? Есть ли способ снять это? Спасибо.

4b9b3361

Ответ 1

Вероятно, вы уже нашли ответ на этот вопрос, но в случае, если вы этого не сделали, вот что я сделал, чтобы исправить это через несколько часов царапин на голове.

Проблема, о которой я думаю, представляет собой комбинацию двух факторов:

  • Загрузка Fragmnets in ViewPager с задержкой, что означает, что активность возвращается намного быстрее, чем ее фрагменты, которые содержатся внутри ViewPager

  • Если вы похожи на меня, то ваши дочерние фрагменты ViewPager, скорее всего, будут одного и того же типа. Это означает, что все они имеют одно и то же имя перехода (если вы указали их в своем макете xml), , если вы не установите их в коде и только установите его один раз, на видимый фрагмент.

Чтобы исправить обе эти проблемы, это то, что я сделал:

1. Фиксирование проблемы с задержкой загрузки:

Внутри вашей активности (той, которая содержит ViewPager), добавьте эту строку после super.onCreate() и до setContentView():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivityCompat.postponeEnterTransition(this); // This is the line you need to add

    setContentView(R.layout.feeds_content_list_activity);
    ...
}

2. Фиксирование проблемы с несколькими фрагментами с одним и тем же именем перехода:

Теперь есть довольно много способов сделать это, но это то, что у меня получилось внутри моего "подробного" действия, то есть активности, которая содержит ViewPageronCreate(), но вы можете это сделать где угодно):

_viewPager.setAdapter(_sectionsPagerAdapter);
_viewPager.setCurrentItem(position);
...
...
_pagerAdapter.getItem(position).setTransitionName(getResources().getString(R.string.transition_contenet_topic));

Вам нужно быть осторожным, так как Activity может еще не прикрепляться к вашему фрагменту ViewPager, поэтому проще просто передать имя перехода из операции в фрагмент, если вы загружаете его из ресурса

Фактическая реализация так же проста, как вы ожидаете:

public void setTransitionName(String transitionName) {
    _transitionName = transitionName;
}

Затем внутри фрагмента onViewCreated() добавьте эту строку:

public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    ...
    if (_transitionName != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        setTransitionNameLollipop();
    }
    ...
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setTransitionNamesLollipop() {
    _imgTopic.setTransitionName(_transitionName);
}

Последний фрагмент головоломки заключается в том, чтобы узнать, когда ваш фрагмент полностью загружен, а затем вызовите ActivityCompat.startPostponedEnterTransition(getActivity());.

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

Проблема с этим подходом заключается в том, что переход на выход может не работать, если вы не очень осторожны и reset имя перехода на "видимом" фрагменте прямо перед вами exit активность для перехода назад. Это можно сделать следующим образом:

  • Прослушайте изменение страницы на ViewPager
  • Удалите имена (имена) перехода, как только ваш фрагмент не отображается.
  • Установите имя перехода на видимый фрагмент.
  • Вместо вызова finish() вызовите ActivityCompat.finishAfterTransition(activity);

Это может стать очень сложным очень скоро, если вам потребуется переход, чтобы перейти к RecyclerView, который был моим делом. Для этого есть гораздо лучший ответ, предоставленный @Alex Lockwood здесь: Фрагменты ViewPager - Переходы с общим элементом, который имеет очень хорошо написанный примерный код (хотя намного сложнее, чем что я только что написал) здесь: https://github.com/alexjlockwood/activity-transitions/tree/master/app/src/main/java/com/alexjlockwood/activity/transitions

В моем случае мне не пришлось заходить так далеко, чтобы реализовать его решение, и вышеприведенное решение, которое я написал для моего дела.

Если у вас несколько общих элементов, я уверен, что вы можете выяснить, как расширить методы, чтобы удовлетворить их.

Ответ 3

Я недавно ударил головой о стену. Все, что я хотел, это фрагмент в ViewPager, чтобы запустить еще один фрагмент с хорошим расширением разделяемого элемента с расширением открытой карты. Ни одно из вышеперечисленных предложений не работало для меня, поэтому я решил попробовать запустить Activity в стиле Dialog:

<style name="AppTheme.CustomDialog" parent="MyTheme">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:backgroundDimEnabled">true</item>
</style>

Затем для запуска Activity from the Fragment:

Intent intent = new Intent(getContext(), MyDialogActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
                        .makeSceneTransitionAnimation(getActivity(),
                                Pair.create(sharedView, "target_transition"));
ActivityCompat.startActivity(getActivity(), intent, options.toBundle());

В макете фрагмента установите андроид: имя_перехода в sharedView и в макете Activity есть android: transition_name = "target_transition" (то же самое, что и второй аргумент Pair.create()).