Android How to start shared element transition using Fragments?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26561579/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to start shared element transition using Fragments?
提问by unchosen
I am trying to implement transitions between fragments which have "shared elements" as described in the new material design specs. The only method I can find is the ActivityOptionsCompat.makeSceneTransitionAnimation, which I believe works on Activity only. I've been searching for this same functionality but with/for fragments.
I am trying to implement transitions between fragments which have "shared elements" as described in the new material design specs. The only method I can find is the ActivityOptionsCompat.makeSceneTransitionAnimation, which I believe works on Activity only. I've been searching for this same functionality but with/for fragments.
采纳答案by ar34z
I had the same problem but had it working by adding a new fragment from another fragment. The following link is very helpful in getting started on this: https://developer.android.com/training/material/animations.html#Transitions
I had the same problem but had it working by adding a new fragment from another fragment. The following link is very helpful in getting started on this: https://developer.android.com/training/material/animations.html#Transitions
Following is my code that works. I'm animating an ImageView
from one fragment to the other.
Make sure the View
you want to animate has the same android:transitionName
in both fragments.
The other content doesn't really matter.
Following is my code that works. I'm animating an ImageView
from one fragment to the other.
Make sure the View
you want to animate has the same android:transitionName
in both fragments.
The other content doesn't really matter.
As a test, you could copy this to both your layout xml files. Make sure the image exists.
As a test, you could copy this to both your layout xml files. Make sure the image exists.
<ImageView
android:transitionName="MyTransition"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/test_image" />
Then I have 1 file in my res/transition
folder, named change_image_transform.xml.
Then I have 1 file in my res/transition
folder, named change_image_transform.xml.
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform />
</transitionSet>
Now you can get started. Lets say you have Fragment A containing the image and want to add Fragment B.
Now you can get started. Lets say you have Fragment A containing the image and want to add Fragment B.
Run this in Fragment A:
Run this in 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;
}
}
回答by Dimitris Kazakos
The shared element fragment transitions dowork with ListViews, as long as the source and target views have the same (and unique) transitionName.
The shared element fragment transitions dowork with ListViews, as long as the source and target views have the same (and unique) transitionName.
If you make your list view adapter to set unique transitionNames to the views you want (e.g. some constant + specific item id) and alsochange your detail fragment to set the same transitionNames to the target views at runtime (onCreateView), the transitions actually work!
If you make your list view adapter to set unique transitionNames to the views you want (e.g. some constant + specific item id) and alsochange your detail fragment to set the same transitionNames to the target views at runtime (onCreateView), the transitions actually work!
回答by Jordy
Shared elements do work with Fragments but there are some things to keep in mind:
Shared elements do work with Fragments but there are some things to keep in mind:
Don't try to set the
sharedElementsTransition
in theonCreateView
of your Fragment. You have to define them when creating an instance of your Fragment or inonCreate
.Take note of the official documentation on the possible animations for enter/exit transitions & sharedElementTransition. They are not the same.
Trial and error :)
Don't try to set the
sharedElementsTransition
in theonCreateView
of your Fragment. You have to define them when creating an instance of your Fragment or inonCreate
.Take note of the official documentation on the possible animations for enter/exit transitions & sharedElementTransition. They are not the same.
Trial and error :)
回答by Paul L
This should be a comment to the accepted answer, as I am unable to comment on it.
This should be a comment to the accepted answer, as I am unable to comment on it.
The accepted answer (by WindsurferOak and ar34z) works, except for a "minor" problem which caused a null pointer exception when navigating up with the backStack. It seems that setSharedElementReturnTransition()
should be called on the target fragment instead of the original fragment.
The accepted answer (by WindsurferOak and ar34z) works, except for a "minor" problem which caused a null pointer exception when navigating up with the backStack. It seems that setSharedElementReturnTransition()
should be called on the target fragment instead of the original fragment.
So instead of:
So instead of:
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
it should be
it should be
fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
回答by Lym Zoy
Following are some helpful resources:
Following are some helpful resources:
https://github.com/lgvalle/Material-Animations
https://github.com/lgvalle/Material-Animations
回答by Marius
The key is to use a custom transaction with
The key is to use a custom transaction with
transaction.addSharedElement(sharedElement, "sharedImage");
Shared Element Transition Between Two Fragments
Shared Element Transition Between Two Fragments
In this example, one of two different ImageViews
should be translated from the ChooserFragment
to the DetailFragment
.
In this example, one of two different ImageViews
should be translated from the ChooserFragment
to the DetailFragment
.
In the ChooserFragment
layout we need the unique transitionName
attributes:
In the ChooserFragment
layout we need the unique transitionName
attributes:
<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" />
In the ChooserFragments
class, we need to pass the View
which was clicked and an ID to the parent Activity
wich is handling the replacement of the fragments (we need the ID to know which image resource to show in the DetailFragment
). How to pass information to a parent activity in detail is surely covered in another documentation.
In the ChooserFragments
class, we need to pass the View
which was clicked and an ID to the parent Activity
wich is handling the replacement of the fragments (we need the ID to know which image resource to show in the DetailFragment
). How to pass information to a parent activity in detail is surely covered in another documentation.
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);
}
}
});
In the DetailFragment
, the ImageView
of the shared element also needs the unique transitionName
attribute.
In the DetailFragment
, the ImageView
of the shared element also needs the unique transitionName
attribute.
<ImageView
android:id="@+id/image_shared"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:transitionName="sharedImage" />
In the onCreateView()
method of the DetailFragment
, we have to decide which image resource should be shown (if we don't do that, the shared element will disappear after the transition).
In the onCreateView()
method of the DetailFragment
, we have to decide which image resource should be shown (if we don't do that, the shared element will disappear after the transition).
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;
}
The parent Activity
is receiving the callbacks and handles the replacement of the fragments.
The parent Activity
is receiving the callbacks and handles the replacement of the fragments.
@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();
}
Not to forget - the Transition
itself. This example moves and scales the shared element.
Not to forget - the Transition
itself. This example moves and scales the shared element.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class DetailsTransition extends TransitionSet {
public DetailsTransition() {
setOrdering(ORDERING_TOGETHER);
addTransition(new ChangeBounds()).
addTransition(new ChangeTransform()).
addTransition(new ChangeImageTransform());
}
}
回答by yasin
I searched for SharedElement in fragments and I find very useful source code on GitHub.
I searched for SharedElement in fragments and I find very useful source code on GitHub.
1.first you should define transitionNamefor your Objects(Like ImageView) in both Fragments layout(We add a button in fragment A for handling click event):
1.first you should define transitionNamefor your Objects(Like ImageView) in both Fragments layout(We add a button in fragment A for handling click event):
fragment A:
fragment 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" />
fragment B:
fragment 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" />
- Then you should write this code in your transition file in transition Directory(if you haven't this Directory so create One: res > new > Android Resource Directory > Resource Type = transition > name = change_image_transform ):
- Then you should write this code in your transition file in transition Directory(if you haven't this Directory so create One: res > new > Android Resource Directory > Resource Type = transition > name = change_image_transform ):
change_image_transform.xml:
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>
- In the last step you should complete codes in java:
- In the last step you should complete codes in java:
fragment A:
fragment 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();
}
});
}
}
fragment B:
fragment 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);
}
}
don't forget to show your "A" fragment in your activity:
don't forget to show your "A" fragment in your activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.content, new SimpleFragmentA())
.commit();
}
source : https://github.com/mikescamell/shared-element-transitions
source : https://github.com/mikescamell/shared-element-transitions
回答by Mayank Goyal
How to start shared element transition using Fragments?
How to start shared element transition using Fragments?
I assume you want to transit your Image using Fragment (not using Activity)
I assume you want to transit your Image using Fragment (not using Activity)
Disclamer: it wont work perfectly if you have already set AppTheme
Disclamer: it wont work perfectly if you have already set AppTheme
And please, keep the source transitionName and destination transitionName same
And please, keep the source transitionName and destination transitionName same
You have to do three things for transition:
You have to do three things for transition:
1.Set transitionName to the source View -> in xml or programatically before calling makeFragmentTransition
1.Set transitionName to the source View -> in xml or programatically before calling makeFragmentTransition
private void setImageZoom(boolean isImageZoom) {
ImageView imageView = this.findViewById(R.id.image);
if (isImageZoom) {
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ViewCompat.setTransitionName(imageView, "imageTransition");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
makeFragmentTransition(imageView);
}
}
});
}
}
2.Fragment Transition
2.Fragment Transition
Set TransitionSet for the specicific Transition animation
apply them on Fragment
call addSharedElement(View, transitionName) while fragmentTransition
Set TransitionSet for the specicific Transition animation
apply them on Fragment
call addSharedElement(View, transitionName) while fragmentTransition
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void makeFragmentTransition(ImageView sourceTransitionView) {
//transtionName for sourceView
//MUST set transitionName before calling this method(programattically or give ->transitionName to the view in xml)
String sourceTransitionName = ViewCompat.getTransitionName(sourceTransitionView);
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(500);
transitionSet.addTransition(new ChangeBounds()); //to expand boundaries
transitionSet.addTransition(new ChangeTransform()); //for transtion vertically
transitionSet.addTransition(new ChangeImageTransform()); // image transform work
transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER); // all three work together to look one task
ImageTransitionFragment fragment = new ImageTransitionFragment();
fragment.setSharedElementEnterTransition(transitionSet);
fragment.setSharedElementReturnTransition(transitionSet);
fragment.setAllowReturnTransitionOverlap(false);
try {
getHostActivity().getSupportFragmentManager()
.beginTransaction()
//sharedElement is set here for fragment
//it will throw exception if transitionName is not same for source and destionationView
.addSharedElement(sourceTransitionView, sourceTransitionName)
//R.id.fragmentView is the View in activity on which fragment will load...
.replace(R.id.fragmentView, fragment)
.addToBackStack(null)
.commit();
} catch (Exception e) {
//
String string = e.toString();
}
}
3.Last Set destionation transition Name with sourceTransitionName -> imageTransition
3.Last Set destionation transition Name with sourceTransitionName -> imageTransition
so add transitionName in ImageView of ImageTransitionFragment
so add transitionName in ImageView of ImageTransitionFragment
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/destionationTransitionPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="@string/pageTransition"
android:background="@color/black_color">
<com.android.foundation.ui.component.FNImageView
android:id="@+id/destinationImageView"
android:layout_width="@dimen/_400dp"
android:layout_gravity="center"
android:transitionName="imageTransition"
android:layout_height="@dimen/_400dp" />
Please respond if anything is not clear or it need more improvement
Please respond if anything is not clear or it need more improvement