如何使用Fragments启动共享元素转换?

时间:2014-10-25 10:50:48

标签: android fragment material-design

我正在尝试实现具有“共享元素”的片段之间的转换,如新材料设计规范中所述。 我能找到的唯一方法是ActivityOptionsCompat.makeSceneTransitionAnimation,我相信它只适用于Activity。 我一直在寻找相同的功能,但有/为片段。

8 个答案:

答案 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一起使用,但有些事情要记住:

  1. 请勿尝试在片段的sharedElementsTransition中设置onCreateView。您必须在创建片段实例时或在onCreate

  2. 中定义它们
  3. 请注意有关进入/退出转换的可能动画的官方文档&amp; sharedElementTransition。它们不一样。

  4. 试错:)

答案 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));

https://github.com/tevjef/Rutgers-Course-Tracker/issues/8

答案 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" />
  1. 然后,您应该在过渡目录的过渡文件中编写此代码(如果没有此目录,请创建一个:res>新建> Android资源目录>资源类型=过渡>名称= change_image_transform):

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>
  1. 在最后一步中,您应使用Java完成代码:

片段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" />

如果不清楚或需要进一步改进,请回复