【问题标题】:How to used a shared transition from RecyclerView to a Fragment如何使用从 RecyclerView 到 Fragment 的共享转换
【发布时间】:2019-05-02 06:20:41
【问题描述】:

我已经看到这个问题在这里问了很多次,但没有答案。我希望如果我再问一次,也许有人会很好地帮助我。

我正在尝试实现从 Recyclerview 中的项目到片段的共享转换。

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

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

android docs 中,addSharedElement(view and string) 让我很困惑。如何给视图一个唯一的 ID,我什至应该在这里使用 v 吗?

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

【问题讨论】:

  • 字符串是提供的视图应该变形的视图的android:transitionName="。传递 v 参数以检索 android:transitionName=" 以进行后退导航。

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


【解决方案1】:

这是我的实现。有FragmentListRecyclerView,每个项目都有图像。还有FragmentDetail,只有大图。图片来自FragmentList 列表项飞到FragmentDetailFragmentListFragmentDetail 上的动画视图的 TransitionName 必须相同。所以我为每个列表项 ImageView 赋予唯一的过渡名称:

imageView.setTransitionName("anyString" + position)

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

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

或者您可以覆盖FragmentgetSharedElementEnterTransitiongetSharedElementReturnTransition 并在那里声明transition。这是完整的代码:

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;
    }
}

activity_main.xml

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

片段列表:

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);
            }
        }
    }
}

fragment_list.xml

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

片段详细信息:

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;
    }
}

fragment_detail.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>

item.xml

<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>

结果如下:

TransitionName 可以是您想要的任何字符串。 如果你需要支持棒棒糖之前的设备,有ViewCompat.setTransitionName

【讨论】:

    【解决方案2】:

    添加到ashakirov's answer:

    问题

    返回转换不起作用!

    原因: 在从Fragment BFragment Apopbackstack 上,调用Fragment A's onCreateView,如果您的数据仍在加载,则返回转换仅适用于ashakirov 的代码。

    解决方案

    推迟片段中的过渡

    在您的fragment A 中加载开始的位置添加这一行:

    postponeEnterTransition();
    

    加载数据或设置适配器后,编写以下内容:

    startPostponedEnterTransition();
    

    这可确保您的转换仅在数据加载后发生。 此外,请确保即使在“数据无法加载场景”中也开始转换。

    如果您想在从 A to B 转到 fragment B 时推迟转换,请将其添加到您的 fragment B

    @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            //hold the transition
            postponeEnterTransition();
            //when the views are available then start the transition
            view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    view.getViewTreeObserver().removeOnPreDrawListener(this);
                    startPostponedEnterTransition();
                    return true;
                }
            });
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      • 2017-07-13
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多