我已经多次在这里看到这个被问到的问题而没有答案。我希望我再问一次,也许有人会很友好地为我提供帮助。
我正在尝试实现从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吗?
字符串可以是我想要的吗?
答案 0 :(得分:1)
这是我的实现。每个项目上都有FragmentList
和RecyclerView
带有图像。并且FragmentDetail
仅包含大图片。来自FragmentList
列表项的图像会飞到FragmentDetail
。 TransitionName
和FragmentList
上的动画视图中的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>
这是结果:
<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)
返回过渡无效!
原因:
从popbackstack
到Fragment B
的{{1}}上,调用Fragment A
的onCreateView,如果您的数据仍在加载,则返回转换仅对ashakirov的代码无效。
推迟片段中的过渡
在您的Fragment A's
的加载开始位置添加以下行:
fragment A
在加载数据或设置适配器后,请输入以下内容:
postponeEnterTransition();
这可确保仅在加载数据后才进行转换。 另外,请确保即使在“数据无法加载方案”中也开始过渡。
如果您想在从startPostponedEnterTransition();
开始时推迟fragment B
中的过渡,请将其添加到A to B
中:
fragment B