По сути, Android запускает переход с предварительно заданными View
и transitionName
и автоматически использует те же свойства для обратного перехода. Когда вы изменяете свой сфокусированный View в ViewPager, Android не знает об этом и сохраняет переход предыдущего на обратном пути. Так что вам нужно сообщить Android об изменениях:
- Переопределить свойства перехода: используйте
setEnterSharedElementCallback
, чтобы изменить transitionName
и View
на новые, прежде чем вернуться из Activity2
.
- Подождите, пока
Activity1
завершит рендеринг addOnPreDrawListener
.
Это немного сложно в финальной реализации. Но вы можете посмотреть на мой пример кода https://github.com/tamhuynhit/PhotoGallery. Я пытаюсь реализовать переход от общего элемента от множества простых к сложным разделам.
Ваша проблема появилась с Level 3
и решена в Level 4
.
Я пишу учебник по этому вопросу, но он не на английском, поэтому надеюсь, что код поможет
ОБНОВЛЕНИЕ 1: рабочий процесс
Вот как я реализую это в своем коде:
Переопределите finishAfterTransition
в Activity2 и вызовите метод setEnterSharedElementCallback
, чтобы повторно отобразить текущий выбранный элемент в ViewPager. Также, позвоните setResult
, чтобы передать новый выбранный индекс назад к предыдущему действию здесь.
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void finishAfterTransition() {
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
View selectedView = getSelectedView();
if (selectedView == null)
return;
// Clear all current shared views and names
names.clear();
sharedElements.clear();
// Store new selected view and name
String transitionName = ViewCompat.getTransitionName(selectedView);
names.add(transitionName);
sharedElements.put(transitionName, selectedView);
setExitSharedElementCallback((SharedElementCallback) null);
}
});
Intent intent = new Intent();
intent.putExtra(PHOTO_FOCUSED_INDEX, mCurrentIndex);
setResult(RESULT_PHOTO_CLOSED, intent);
super.finishAfterTransition();
}
Напишите пользовательский ShareElementCallback
, чтобы я мог установить обратный вызов, прежде чем узнать, какой View
будет использоваться.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static class CustomSharedElementCallback extends SharedElementCallback {
private View mView;
/**
* Set the transtion View to the callback, this should be called before starting the transition so the View is not null
*/
public void setView(View view) {
mView = view;
}
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// Clear all current shared views and names
names.clear();
sharedElements.clear();
// Store new selected view and name
String transitionName = ViewCompat.getTransitionName(mView);
names.add(transitionName);
sharedElements.put(transitionName, mView);
}
}
Переопределить onActivityReenter
в Activity1, получить выбранный индекс из результата Intent
. Установите setExitSharedElementCallback
для повторного отображения нового выбранного View
, когда начинается переход. Вызовите supportPostponeEnterTransition
, чтобы немного задержать, потому что ваш новый View
может не отображаться в этой точке. Используйте getViewTreeObserver().addOnPreDrawListener
, чтобы прослушать изменения макета, найдите право View
по выбранному индексу и продолжите переход supportStartPostponedEnterTransition
.
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityReenter(int resultCode, Intent data) {
if (resultCode != LevelFourFullPhotoActivity.RESULT_PHOTO_CLOSED || data == null)
return;
final int selectedIndex = data.getIntExtra(LevelFourFullPhotoActivity.PHOTO_FOCUSED_INDEX, -1);
if (selectedIndex == -1)
return;
// Scroll to the new selected view in case it's not currently visible on the screen
mPhotoList.scrollToPosition(selectedIndex);
final CustomSharedElementCallback callback = new CustomSharedElementCallback();
getActivity().setExitSharedElementCallback(callback);
// Listen for the transition end and clear all registered callback
getActivity().getWindow().getSharedElementExitTransition().addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {}
@Override
public void onTransitionPause(Transition transition) {}
@Override
public void onTransitionResume(Transition transition) {}
@Override
public void onTransitionEnd(Transition transition) {
removeCallback();
}
@Override
public void onTransitionCancel(Transition transition) {
removeCallback();
}
private void removeCallback() {
if (getActivity() != null) {
getActivity().getWindow().getSharedElementExitTransition().removeListener(this);
getActivity().setExitSharedElementCallback((SharedElementCallback) null);
}
}
});
// Pause transition until the selected view is fully drawn
getActivity().supportPostponeEnterTransition();
// Listen for the RecyclerView pre draw to make sure the selected view is visible,
// and findViewHolderForAdapterPosition will return a non null ViewHolder
mPhotoList.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mPhotoList.getViewTreeObserver().removeOnPreDrawListener(this);
RecyclerView.ViewHolder holder = mPhotoList.findViewHolderForAdapterPosition(selectedIndex);
if (holder instanceof ViewHolder) {
callback.setView(((ViewHolder) holder).mPhotoImg);
}
// Continue the transition
getActivity().supportStartPostponedEnterTransition();
return true;
}
});
}
ОБНОВЛЕНИЕ 2: getSelectedItem
Чтобы получить выбранный вид из ViewPager, не используйте getChildAt
, иначе вы получите неправильный вид, используйте findViewWithTag
вместо
В PagerAdapter.instantiateItem
используйте позицию в качестве тега для каждого просмотра:
@Override
public View instantiateItem(ViewGroup container, int position) {
// Create the View
view.setTag(position)
// ...
}
Прослушайте событие onPageSelected
, чтобы получить выбранный индекс:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mSelectedIndex = position;
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
Позвоните getSelectedView
, чтобы получить текущий вид по выбранному индексу
private View getSelectedView() {
try {
return mPhotoViewPager.findViewWithTag(mSelectedIndex);
} catch (IndexOutOfBoundsException | NullPointerException ex) {
return null;
}
}