I have LinearLayout in a vertical ScrollView with multiple ImageViews, when I click on one image view I do a Transition Animation from (Activity 1) to a Full Screen Gallery (Activity 2), the gallery has view pager where user can swipe right / left .. now when user swipes to a different Image in the gallery and presses back the current visible Image in (Activity 2) is restored back (again using transition animation) to the starting position in Activity 1, the problem is that the image is restored until it reaches the old position and then disappears and reveals a different image (since this is already too complicated to discuss) I attached an image.
Activity1 Layout:
<LinearLayout>
<ImageView />
<ImageView />
<ImageView />
<ImageView />
<ImageView />
<ImageView />
</LinearLayout>
Acivity2 (Gallery) Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/animation_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:scaleType="fitCenter"
android:src="@drawable/placeholder"
android:transitionName="@string/transition_article_image_gallery"
android:layout_centerInParent="true"
/>
<ViewPager
android:id="@+id/gallery_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
On click on any image from Activity 1:
public void openGallery(int position, ArrayList<String> images, View view)
{
Intent intent = new Intent(getActivity(), GalleryActivity.class);
intent.putStringArrayListExtra(GalleryActivity.EXTRA_GALLERY_IMAGES, images);
intent.putExtra(GalleryActivity.EXTRA_POSITION, position);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
getActivity(),
new Pair<>(view, TARGET_ELEMENT_TRANSITION_NAME_ATTRIB_VALUE)
);
ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
} else {
getActivity().startActivity(intent);
}
}
UPDATE The code from George Mount, worked but there's one trick, it's mainly that this callback is called 2 times when the transition starts and on the way back, so it's important to reset the position and check it on the way back as follows:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getDetailsActivity().setExitSharedElementCallback(new SharedElementCallback()
{
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements)
{
if (position != -1) { sharedElements.put(getString(R.string.transition_article_image_gallery), articleImagesHolder.getChildAt(position));
}
}
});
}
and
public void openGallery(int pos, ArrayList<String> images, View view)
{
// reset the return position
position = -1;
}
and the following in my fragment:
public void onActivityReenter(int resultCode, Intent data)
{
if (resultCode == Activity.RESULT_OK) {
int pos = data.getIntExtra(EXTRA_IMAGE_POSITION, -1);
if (pos != -1) {
position = pos;
}
}
}
and the following in my (Activity1):
@Override
public void onActivityReenter(int resultCode, Intent data)
{
ArticleDetailsFragment fragment = (ArticleDetailsFragment) getSupportFragmentManager().findFragmentByTag(ArticleDetailsFragment.class.getSimpleName());
if (fragment != null) {
fragment.onActivityReenter(resultCode, data);
}
}
Update 2 It seems that George already wrote some articles about Transitions, but they're not popular nor showing in Google Search Results, so I thought that it'd be useful to share them here:
https://halfthought.wordpress.com/2014/12/
https://halfthought.wordpress.com/2014/11/
https://halfthought.wordpress.com/2015/06/
First, you're using the transition names wrong in your call. The name in the ActivityOptions.makeSceneTransitionAnimation
is the name of the view in Activity 2. You can use any name you want in Activity 1 and the framework will map the names. This way you can do a many-to-one mapping. For example, if you have a list view of images and you click on one of them, it should be able to transition to a single image in Activity 2. In your case:
<LinearLayout>
<ImageView android:transitionName="image1" />
<ImageView android:transitionName="image2" />
<ImageView android:transitionName="image3" />
<ImageView android:transitionName="image4" />
<ImageView android:transitionName="image5" />
<ImageView android:transitionName="image6" />
</LinearLayout>
and when you make the call:
ActivityOptionsCompat options =
ActivityOptionsCompat.makeSceneTransitionAnimation(
getActivity(), view, Activity2.IMAGE_VIEW_NAME);
And in your Activity 2, you should have an ImageView with the transitionName of Activity2.IMAGE_VIEW_NAME.
But that's not what you were asking about.
Because you're sharing a different element on the way back, you're going to have to override the shared element mappings. You can do this in a couple of ways in your circumstances. The best way requires you to change the mapping of shared elements in Activity 1.
In Activity1, set a SharedElementCallback:
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names,
Map<String, View> sharedElements) {
sharedElements.put(Activity2.IMAGE_VIEW_NAME, newSharedElement);
}
}
So, how do you know which view it should return to? You should use startActivityForResult and so that the called Activity can return the correct view to you. Here, I assume you use the same EXTRA_POSITION field when you call setResult with an Intent. You can then override onActivityReenter
to do what you need:
@Override
public void onActivityReenter(int resultCode, Intent data) {
int position = data.getIntExtra(GalleryActivity.EXTRA_POSITION, -1);
if (position != -1) {
// I'm assuming child index is the same as position here.
newSharedElement = mLinearLayout.getChildAt(position);
}
}
The other thing you can do at this point is scroll the view to a position such that the view is visible. If you're using a recycling container like ListView or RecyclerView, you'll need to use postponeEnterTransition
and startPostponedEnterTransition
to ensure that the views are laid out prior to starting the transition.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With