问题描述
在主活动中,我有BottomNavigationView
,其中有3个不同的父片段.父片段具有recyclerview
,在单击recyclerview的项目时,我将启动子片段以获取有关该项目的更多详细信息.我正在尝试在两个片段(父级和子级)之间实现Shared Element Transition
,但是没有实现.
In the Main Activity, I have BottomNavigationView
where there are 3 different parent fragments. The parent fragment has recyclerview
and on item click of recyclerview, I am launching child fragment for more details about the item. I am trying to implement Shared Element Transition
between my two fragments (parent & child) but it's not happening.
启动子片段也没有问题,我已经检查了过渡名称,并且在子片段中分配了相同的名称,该片段分配给适配器中的项目.我正在使用Random
类将过渡名称分配给项目,因为在单个父片段中,我有很多recyclerviews.这是我的代码:
There is no issue with launching child fragment also I have checked the transition name and it's the same in child fragment which I assign to an item in the adapter. I am using Random
class to assign transition name to item as in single parent fragment I have many recyclerviews. Here is my code:
适配器
final String transition;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
transition = "transition" + new Random().nextInt(9999999);
viewHolder.image.setTransitionName(transition);
}
viewHolder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mCallback.itemClicked(i, book, viewHolder.image, transition);
}
});
父母片段
@Override
public void itemClicked(int pos, Book book, View view, String transition) {
MainActivity activity = (MainActivity) getActivity();
ChildFragment myFragment = new ChildFragment();
Bundle bundle = new Bundle();
bundle.putString(IntentExtraKeys.TRANSITION_NAME, transition);
myFragment.setArguments(bundle);
activity.showFragmentWithTransition(this, myFragment, ChildFragment.class.getName(), view, transition);
}
活动
public void showFragmentWithTransition(Fragment current, Fragment newFragment, String tag, View sharedView, String sharedElementName) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
current.setSharedElementReturnTransition(TransitionInflater.from(this).inflateTransition(R.transition.default_transition));
current.setExitTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.no_transition));
newFragment.setSharedElementEnterTransition(TransitionInflater.from(this).inflateTransition(R.transition.default_transition));
newFragment.setEnterTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.no_transition));
}
FragmentManager manager = current.getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.child_fragment, newFragment, tag);
transaction.addToBackStack(tag);
transaction.addSharedElement(sharedView, sharedElementName);
transaction.commit();
}
默认过渡
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeTransform />
<changeBounds />
</transitionSet>
儿童片段
Bundle b = getArguments();
if (b != null) {
String transitionName = b.getString(IntentExtraKeys.TRANSITION_NAME);
Logger.info("opening bundle book fragment:" + transitionName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
image.setTransitionName(transitionName);
}
}
这是问题的样本项目: https://gitlab.com/iskfaisal/transition-issue
Here is the sample project of issue:https://gitlab.com/iskfaisal/transition-issue
推荐答案
什么也没有发生,或者至少看起来好像什么也没有发生,因为您只在 default_transition.xml 中使用了<changeTransform/>
和<changeBounds />
.如果两个Fragments
巧合的边界,则没有任何过渡".
Nothing is happening or at least it looks like nothing is happening because you're using only <changeTransform/>
and <changeBounds />
in your default_transition.xml. If bounds of both Fragments
coincide, there's nothing to "transit".
但是,如果向文件中添加其他动画元素,则过渡实际上是可见的(即使边界重合).
However, if you add additional animation elements to the file then the transition is actually visible (even if bounds coincide).
我创建了一个与您的项目相似的示例项目-BottomNavigationView
,其中的NavHostFragment
包含2
父级Fragments
-DashboardFragment
和HomeFragment
.
I have created a sample project similar to yours - BottomNavigationView
with a NavHostFragment
containing 2
parent Fragments
- DashboardFragment
and HomeFragment
.
最初,DashboardFragment
加载由简单的RecyclerView
组成的DashboardListFragment
.如果单击任何RecyclerView
项,则DashboardFragment
加载DashboardDetailFragment
(DashboardDetailFragment
和DashboardListFragment
中的边界重合).
Initially, DashboardFragment
loads DashboardListFragment
that consists of a simple RecyclerView
. If any RecyclerView
item is clicked then DashboardFragment
loads DashboardDetailFragment
(bounds in DashboardDetailFragment
and DashboardListFragment
coincide).
然后,我几乎重用了您的showFragmentWithTransition(...)
,并尝试单击列表中的每个元素以检查是否可以看到任何过渡-事实并非如此.
Then, I pretty much reused your showFragmentWithTransition(...)
and tried to click each element in the list to check if any transition would be visible - it wasn't.
因此,我通过添加一个简单的<slide/>
元素来修改文件,如下所示:
Hence, I modified the file by adding a simple <slide/>
element as below:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
<slide/>
<changeTransform />
<changeBounds />
</transitionSet>
滑动过渡就在那儿.
我还尝试了其他元素,例如<fade/>
或<explode/>
或其他-所有这些也都很好用.
I also tried other elements like <fade/>
or <explode/>
or others - all of them worked just fine too.
即使看不到过渡,也并不意味着过渡没有发生.您应该尝试其他动画元素才能看到它的效果.
Even if a transition is not visible, it doesn't mean it's not happening. You should try other animation elements to see it work.
由于您提供了指向 github 代码的链接,因此我进行了深入研究,并将其置于工作状态.我会把它留给您,以进一步改进它.
Since you provided a link to your github code, I peeked into it and brought it to the working condition. I'll leave it up to you to improve it further.
因此,基本上,您的HomeFragment
布局文件同时包含RecyclerView
和您的孩子Fragment
的占位符.相反,我将您的HomeFragment
拆分为2
实体-一个用于父级Fragment
HomeFragment
的实体,该实体仅包含任何子级Fragment
和HomeFragmentList
的占位符,而子级Fragment
和HomeFragmentList
包含您以前的父级逻辑.
So, basically, your HomeFragment
layout file contained both RecyclerView
and a placeholder for your child Fragment
at the same time. Instead, I split your HomeFragment
into 2
entities - one for your parent Fragment
HomeFragment
containing only the placeholder for any child Fragment
and HomeFragmentList
containing your previous parent logic.
然后,当选择了图像,替换您与.而且... 有效!
Then, when a picture is selected, HomeFragment
replaces your HomeFragmentList
with a ChildFragment
. And...it works!
通过这种方式,您无需使用Activity
来创建子级Fragment
,它更加独立.
This way, you don't need to use your Activity
for creating a child Fragment
and it's more independent.
下面,我提供了必须修改的相关代码.
Below, I provide the relevant code that had to be modified.
public class HomeFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
HomeFragmentList homeFragmentList = new HomeFragmentList();
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.child_fragment, homeFragmentList, homeFragmentList.getClass().getName());
fragmentTransaction.commit();
}
}
HomeFragmentList(老父母)
public class HomeFragmentList extends Fragment implements AdapterListener {
RecyclerView recyclerView;
private ArrayList<String> images = new ArrayList<>();
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home_list, container, false);
recyclerView = root.findViewById(R.id.recycler_view);
getImages();
LinearLayoutManager manager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
recyclerView = root.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(manager);
AdapterClass adapter = new AdapterClass(this, images);
recyclerView.setAdapter(adapter);
return root;
}
void getImages() {
images.add("https://rukminim1.flixcart.com/image/832/832/book/0/1/9/rich-dad-poor-dad-original-imadat2a4f5vwgzn.jpeg?q=70");
images.add("https://www.seeken.in/wp-content/uploads/2017/06/The-4-Hour-Work-Week.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/06/Managing-Oneself.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/07/How-to-Win-Friends-and-Influence-People.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/07/THINK-LIKE-DA-VINCI-7-Easy-Steps-to-Boosting-your-Everyday-Genius.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/07/How-To-Stop-Worrying-And-Start-Living.jpg");
images.add("https://www.seeken.in/wp-content/uploads/2017/08/THE-INTELLIGENT-INVESTOR.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/08/Awaken-the-Giant-within-How-to-Take-Immediate-Control-of-Your-Mental-Emotional-Physical-and-Financial-Life.jpg");
images.add("https://www.seeken.in/wp-content/uploads/2017/08/E-MYTH-REVISITED.jpeg");
images.add("https://images-na.ssl-images-amazon.com/images/I/41axGE4CehL._SX353_BO1,204,203,200_.jpg");
images.add("https://rukminim1.flixcart.com/image/832/832/book/0/1/9/rich-dad-poor-dad-original-imadat2a4f5vwgzn.jpeg?q=70");
}
@Override
public void itemClicked(int pos, ModelClass object, View view, String transition) {
MainActivity activity = (MainActivity) getActivity();
ChildFragment myFragment = new ChildFragment();
Bundle bundle = new Bundle();
bundle.putString("IMAGE_URL", object.getItem(pos));
bundle.putInt("POSITION", pos);
bundle.putString("TRANSITION_NAME", transition);
myFragment.setArguments(bundle);
Log.i("HOME FRAGMENT-DEBUG", transition + "/" + view.getTransitionName());
showFragmentWithTransition(getParentFragment(), myFragment, ChildFragment.class.getName(), view, transition);
}
public void showFragmentWithTransition(Fragment current, Fragment _new, String tag, View sharedView, String sharedElementName) {
FragmentManager manager = current.getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (sharedView != null && sharedElementName != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
current.setSharedElementReturnTransition(TransitionInflater.from(getContext()).inflateTransition(R.transition.default_transition));
current.setExitTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.no_transition));
_new.setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(R.transition.default_transition));
_new.setEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.no_transition));
transaction.addSharedElement(sharedView, sharedElementName);
Log.i("ACTIVITY-DEBUG", sharedElementName + "/" + sharedView.getTransitionName());
}
}
transaction.replace(R.id.child_fragment, _new, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
}
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/child_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
fragment_home_list.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF">
<androidx.core.widget.NestedScrollView
android:id="@+id/home_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginLeft="9dp"
android:layout_marginRight="9dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical">
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF">
<androidx.appcompat.widget.Toolbar
android:id="@+id/z_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp">
<RelativeLayout
android:id="@+id/header_container"
android:layout_width="match_parent"
android:layout_height="56dp">
<ImageView
android:id="@+id/logo"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="10dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:src="@mipmap/ic_launcher_round"
android:contentDescription="@string/app_name" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/logo"
android:textColor="#000"
android:textSize="16sp"
android:text="Home" />
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
备注
请注意,可以看到<changeTransform/>
和<changeBounds />
的过渡,因为HomeFragmentList
中选定的View
的界限与子Fragment
中的界限不同.
Remark
Please note that the transition for <changeTransform/>
and <changeBounds />
is visible because bounds of a selected View
in HomeFragmentList
are different from that in the child Fragment
.
这篇关于父元素和子片段(嵌套片段)之间的共享元素过渡不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!