我正在尝试实现具有“共享元素”的片段之间的转换,如新材料设计规范中所述。 我能找到的唯一方法是ActivityOptionsCompat.makeSceneTransitionAnimation,我相信它只适用于Activity。 我一直在寻找相同的功能,但有/为片段。
答案 0 :(得分:45)
我遇到了同样的问题,但是通过从另一个片段添加一个新片段来实现它。 以下链接对于开始此操作非常有帮助: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" />
然后我的res/transition
文件夹中有1个文件,名为 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;
}
}
答案 1 :(得分:21)
我发布这个作为答案,因为我是新来的,无法发表评论。
共享元素片段转换 do 与ListViews一起使用,只要源视图和目标视图具有相同(且唯一)的transitionName。
如果您使列表视图适配器将唯一的transitionNames设置为您想要的视图(例如某些常量+特定项ID)并且更改您的详细信息片段以将相同的transitionNames设置为目标视图在运行时(onCreateView),转换实际上有效!
答案 2 :(得分:11)
共享元素可以与Fragments一起使用,但有些事情要记住:
请勿尝试在片段的sharedElementsTransition
中设置onCreateView
。您必须在创建片段实例时或在onCreate
。
请注意有关进入/退出转换的可能动画的官方文档&amp; sharedElementTransition。它们不一样。
试错:)
答案 3 :(得分:3)
这应该是对接受的答案的评论,因为我无法评论它。
接受的答案(WindsurferOak和ar34z)有效,除了&#34;未成年人&#34;使用backStack导航时导致空指针异常的问题。似乎应该在目标片段而不是原始片段上调用setSharedElementReturnTransition()
。
所以而不是:
setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
应该是
fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
答案 4 :(得分:0)
以下是一些有用的资源:
答案 5 :(得分:0)
关键是使用
的自定义交易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
和ID传递给处理替换片段的父Activity
(我们需要ID到知道要在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()
的{{1}}方法中,我们必须决定应该显示哪个图像资源(如果我们不这样做,共享元素将在转换后消失)。
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'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();
}
本身。此示例移动并缩放共享元素。
Transition
答案 6 :(得分:0)
我在片段中搜索了SharedElement,并在GitHub上找到了非常有用的源代码。
1。首先,您应该在两个“片段”布局中为对象(如ImageView)定义 transitionName (我们在片段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" />
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>
片段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);
}
}
别忘了在活动中显示“ A”片段:
@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
答案 7 :(得分:-1)
如何使用片段开始共享元素转换?
我假设您想使用Fragment(而不是Activity)来传输图片
Disclamer:如果您已经设置了AppTheme,它将无法完美运行
请保持源transitionName和目标transitionName相同
您必须做三件事以进行过渡:
1。在调用makeFragmentTransition之前,以xml或编程方式将transitionName设置为源视图->
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。片段过渡
为特定的Transition动画设置TransitionSet
将它们应用于片段
在fragmentTransition时调用addSharedElement(View,transitionName)
@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。带有sourceTransitionName-> imageTransition的最后一个设置目标的过渡名称
因此在ImageTransitionFragment的ImageView中添加transitionName
<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" />
如果不清楚或需要进一步改进,请回复