【问题标题】:Fragment getChildFragmentManager.executePendingTransactions() error: No view found片段 getChildFragmentManager.executePendingTransactions() 错误:未找到视图
【发布时间】: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


【解决方案1】:

尝试在 onBidViewHolder() 中更改您的片段并使用 recyclerViewAdapter.notifyItemChanged(position) 启动它。如果您将逻辑从片段更改为 getItemViewType(),那将是更好的解决方案。

编辑:

当您尝试在动态创建的视图上设置片段时,您的问题开始了,当您调用 executePendingTransactions() 时仍然没有视图(视图对象已创建但仍不在屏幕上)此问题可以通过以下方式解决:

((ExampleViewHolder) holder).fragmentContainer.post(new Runnable() {
            @Override
            public void run() {
                f.getChildFragmentManager().executePendingTransactions();
            }
        });

所以只有在创建视图时才会调用此代码。 但是你仍然有它的 id 的问题,因为 recyclerView 的所有项目都有相同的 id,你可以通过以下方式避免这种情况:

int i = ((ExampleViewHolder) holder).fragmentContainer.generateViewId();
        ((ExampleViewHolder) holder).fragmentContainer.setId(i);

        f.getChildFragmentManager().beginTransaction().add(i, new PlaceImageFragment()).commit();

但此代码只能从 API 级别 17 开始工作。 或者您可以创建自己的静态 ID(不好的解决方案)。

而且我仍然看不到使用 getItemViewType() 逻辑的问题 - 您可以创建 3 种不同的视图类型,其中包含按钮的副本,这些按钮将是不同的对象,但它们会调用相同的 onClickListeners。

有关动态创建的视图和片段的更多信息,请查看: How do I add a Fragment to an Activity with a programmatically created content view

【讨论】:

  • 我无法将逻辑从片段更改为 getItemViewType,因为图片、文本和地图按钮本质上是相互关联的。请您阅读问题下方的评论以获取更多详细信息。
猜你喜欢
  • 2015-05-05
  • 1970-01-01
  • 2013-09-20
  • 1970-01-01
  • 1970-01-01
  • 2015-08-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多