如何使用从RecyclerView到片段的共享过渡

时间:2019-05-02 06:20:42

标签: android android-fragments android-recyclerview android-transitions

我已经多次在这里看到这个被问到的问题而没有答案。我希望我再问一次,也许有人会很友好地为我提供帮助。

我正在尝试实现从Recyclerview中的项到片段的共享过渡。

我目前在适配器的onBindView方法中有我的片段事务。

public void onClick(View v) {
    ...
    activity.getSupportFragmentMnager().beginTransaction()
            .addSharedElement(v, "SharedString")
            .replace(R.id.container, fragment2)
            .addToBackStack(null)
            .commit();
}

android docs中,addSharedElement(视图和字符串)使我感到困惑。如何为视图赋予唯一的ID,我该在这里使用v吗?

字符串可以是我想要的吗?

2 个答案:

答案 0 :(得分:1)

这是我的实现。每个项目上都有FragmentListRecyclerView带有图像。并且FragmentDetail仅包含大图片。来自FragmentList列表项的图像会飞到FragmentDetailTransitionNameFragmentList上的动画视图中的FragmentDetail必须相同。因此,我为每个列表项ImageView赋予了唯一的过渡名称:

imageView.setTransitionName("anyString" + position)

然后,我通过FragmentDetail将该字符串传递给setArguments并将大图transitionName设置为该字符串。另外,我们应该提供Transition,它描述了一个视图层次结构中的视图如何动画化到另一个视图层次结构。之后,我们应该传递那个transition进行分段。我在FragmentTransaction之前做过:

 detailFragment.setSharedElementEnterTransition(getTransition());
 detailFragment.setSharedElementReturnTransition(getTransition());

或者您可以覆盖getSharedElementEnterTransition的{​​{1}}和getSharedElementReturnTransition并在那里声明Fragment。这是完整的代码:

transition

activity_main.xml

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if(savedInstanceState == null) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fragment_container, new FragmentList())
                    .commit();
        }
    }

    public void showDetail(View view) {
        String transitionName = view.getTransitionName();

        FragmentDetail fragment = new FragmentDetail();
        fragment.setArguments(FragmentDetail.getBundle(transitionName));
        fragment.setSharedElementEnterTransition(getTransition());
        fragment.setSharedElementReturnTransition(getTransition());

        getSupportFragmentManager()
                .beginTransaction()
                .addSharedElement(view, transitionName)
                .replace(R.id.fragment_container, fragment)
                .addToBackStack(null)
                .commit();
    }

    private Transition getTransition() {
        TransitionSet set = new TransitionSet();
        set.setOrdering(TransitionSet.ORDERING_TOGETHER);
        set.addTransition(new ChangeBounds());
        set.addTransition(new ChangeImageTransform());
        set.addTransition(new ChangeTransform());
        return set;
    }
}

FragmentList:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" />

fragment_list.xml

public class FragmentList extends Fragment {

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_list, container, false);
        RecyclerView rv = view.findViewById(R.id.recyclerview);
        rv.setAdapter(new ListAdapter());
        return view;
    }

    private class ListAdapter extends RecyclerView.Adapter {
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            return new Holder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            Holder hold = (Holder) holder;
            hold.itemView.setOnClickListener(v -> {
                ((MainActivity) getActivity()).showDetail(hold.imageView);
            });
            //unique string for each list item
            hold.imageView.setTransitionName("anyString" + position);
        }

        @Override
        public int getItemCount() {
            return 10;
        }

        private class Holder extends ViewHolder {
            ImageView imageView;

            public Holder(View view) {
                super(view);
                imageView = view.findViewById(R.id.image);
            }
        }
    }
}

FragmentDetail:

<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

fragment_detail.xml

public class FragmentDetail extends Fragment {
    private static final String NAME_KEY = "key";

    public static Bundle getBundle(String transitionName) {
        Bundle args = new Bundle();
        args.putString(NAME_KEY, transitionName);
        return args;
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_detail, container, false);
        String transitionName = getArguments().getString(NAME_KEY);
        ImageView imageView = view.findViewById(R.id.image);
        imageView.setTransitionName(transitionName);
        return view;
    }
}

item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <ImageView
        android:id="@+id/image"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:src="@drawable/cat"/>
</LinearLayout>

这是结果:

enter image description here

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center"> <ImageView android:id="@+id/image" android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/cat"/> </LinearLayout> 可以是您想要的任何字符串。 如果需要支持Lollipop之前的设备,则有TransitionName

答案 1 :(得分:0)

添加到ashakirov's answer

问题

返回过渡无效!

原因: 从popbackstackFragment B的{​​{1}}上,调用Fragment A的onCreateView,如果您的数据仍在加载,则返回转换仅对ashakirov的代码无效。

解决方案

推迟片段中的过渡

在您的Fragment A's的加载开始位置添加以下行:

fragment A

在加载数据或设置适配器后,请输入以下内容:

postponeEnterTransition();

这可确保仅在加载数据后才进行转换。 另外,请确保即使在“数据无法加载方案”中也开始过渡。

如果您想在从startPostponedEnterTransition(); 开始时推迟fragment B中的过渡,请将其添加到A to B中:

fragment B