【发布时间】:2015-09-22 15:05:56
【问题描述】:
当您打开我的应用程序时,我首先会在顶部有一个带有各种选项卡的 viewpager,您可以单击它来显示每个选项卡的不同片段。
在名为“TEST”的片段内部是一个回收视图,回收视图中的每个项目都是子片段。
recyclerview 中每个片段的顶部都有一些按钮,因此如果您单击地图按钮,片段可以更改为显示地图等的另一个片段。
在我的 recyclerview 适配器中,我膨胀了以下 xml (example.xml),它代表了 recyclerview 的每一行:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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:id="@+id/groupholder"
android:background="#FFF">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/fragment_container">
</RelativeLayout>
</RelativeLayout>
fragment_container 用来存放一个膨胀的片段。
以下是 RecyclerView.Adapter 类中用于填充 recyclerview 的相关部分:
//the view holder class
class ExampleViewHolder extends RecyclerView.ViewHolder {
RelativeLayout fragmentContainer;
public ExampleViewHolder(View view) {
super(view);
//we are creating a framecontainer for the fragment
fragmentContainer = (RelativeLayout) view.findViewById(R.id.fragment_container);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//inflating the layout that contains the fragment for the ExampleViewHolder
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.example, parent, false);
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceOf ExampleViewHolder) {
//The "f" below is the fragment that gets past into this Recycler.Adapter through the adapter's constructor.
f.getChildFragmentManager().beginTransaction().add(((ExampleViewHolder) holder).fragmentContainer.getId(), new PlaceImageFragment()).commit();
//this must execute immediately.
//This is the part that crashes the app
f.getChildFragmentManager().executePendingTransactions();
}
}
由于commit命令被安排在进程的主线程(http://developer.android.com/reference/android/app/FragmentManager.html#executePendingTransactions())上异步执行,我必须调用executePendingTransactions()来让fragmentmanager立即执行命令。
如果我不调用 executePendingTransactions(),应用程序可以运行,但片段不会立即填充,我还可以在屏幕上看到,因为片段填充非常缓慢或在某些情况下根本不填充。
但是,如果我确实调用了 executePendingTransactions(),则应用程序崩溃并显示错误消息,指出行 executePendingTransactions() 是应用程序崩溃的原因:
java.lang.IllegalArgumentException:未找到 id 0x7f0f0096 的视图 (com.example.simon:id/fragment_container) 对于片段 PlaceImageFragment{52e819f8 #0 id=0x7f0f0096}
有什么想法可以让它发挥作用吗?
编辑:
我认为这给我一个找不到视图的原因是 getChildFragmentManager 正在父片段布局 xml 中查找 id/fragment_container 但它不会在那里,因为父片段只包含 recyclerview xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent"
android:id="@+id/main_recyclerview_holder">
<!--The padding here creates the space at the top of the ad-->
<android.support.v7.widget.RecyclerView
android:id="@+id/main_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
【问题讨论】:
-
...是 getChildFragmentManager 正在父片段布局 xml 中查找 id/fragment_container... - 或者在您尝试做的那一刻事务(在 onBindViewHolder() 中)onCreateViewHolder() 中内置的行/项目视图尚未添加到 RecyclerView 父级(因此它不存在于视图层次结构中)。可能不是你想听到的,但你真的应该避免将碎片放在回收东西的小部件中,特别是因为你真的没有从它们的使用中获得任何真正的好处。
-
但是如果你让我们说 3 个按钮并且每个按钮做不同的事情,比如一个会显示地图,另一个会显示图片,另一个会显示文本等,这不是有益的吗?目前,我通过在另一个视图之上创建视图,然后使用 view.bringToFront 和 setVisibility 将视图置于前面并在用户单击时隐藏其他视图,从而在每个视图(地图、图片、文本)之间切换按钮。
-
这不是有益的吗? - 不,它也会浪费,因为你在每一行中都会有(消耗内存并减慢视图布局/测量)完全没用意见。
RecyclerView.Adapter有一个特殊的系统来处理这种情况,通过为用户提供方法getItemViewType()(您可以告诉适配器您有不同类型的行应该被管理)。 -
同意,但地图、图片和文字之间有链接。我无法使用 getItemViewType() 将链接分开并在另一个下分别显示三个,这就是为什么我目前正在将所有视图相互叠加并使用 view.bringToFront 和 setVisibility 在它们之间切换 - 我认为方式我目前这样做比使用片段更浪费,因为片段旨在在视图之间快速交换,并且如果用户不单击地图按钮,则不必加载地图片段。
-
使用自定义视图,例如RowView,它管理其内部动态——视图翻转、监听器、地图等等——并用它填充回收器。这样您就可以访问视图生命周期,并且可以(最终,如果需要)膨胀片段(例如 MapFragment)而不会出现任何问题。
标签: android android-fragments android-recyclerview fragmentmanager