Ошибка фрагмента getChildFragmentManager.executePendingTransactions(): представление не найдено

Когда вы открываете мое приложение, я начинаю с просмотра пейджера с различными вкладками вверху, на которые вы можете щелкнуть, и которые отображают разные фрагменты на вкладке.

Внутри фрагмента под названием «TEST» находится recyclerview, где каждый элемент в recyclerview является дочерними фрагментами.

тест

Есть несколько кнопок, которые находятся поверх каждого фрагмента в 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();
        }

    }

Поскольку команда фиксации запланирована для асинхронного выполнения в основном потоке процесса (http://developer.android.com/reference/android/app/FragmentManager.html#executePendingTransactions()), я должен вызвать executePendingTransactions(), чтобы диспетчер фрагментов немедленно выполнил команду.

Если я не вызываю executePendingTransactions(), приложение работает, но фрагменты не заполняются сразу, и я также вижу это на экране, так как фрагменты заполняются очень медленно или в некоторых случаях вообще не заполняются.

Однако, если я вызову executePendingTransactions(), приложение вылетает с ошибкой, в которой говорится, что строка executePendingTransactions() была причиной сбоя приложения:

java.lang.IllegalArgumentException: не найдено представление для идентификатора 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>

person Simon    schedule 22.09.2015    source источник
comment
... состоит в том, что getChildFragmentManager просматривает в макете родительских фрагментов xml идентификатор/fragment_container... - или в тот момент, когда вы пытаетесь выполнить транзакцию (в onBindViewHolder()) представление строк/элементов, встроенное в onCreateViewHolder(), еще не добавлено к родительскому элементу RecyclerView (поэтому его нет в иерархии представлений). Вероятно, это не то, что вы хотите услышать, но вам действительно следует избегать размещения фрагментов в виджетах, которые перерабатывают материал, тем более что вы действительно не получаете никаких реальных преимуществ от их использования.   -  person user    schedule 24.09.2015
comment
Но если у вас есть, скажем, 3 кнопки, и каждая кнопка делает что-то свое, например, одна показывает карту, другая показывает изображение, третья показывает текст и т. д., разве это не выгодно? В настоящее время я переключаюсь между каждым представлением (картой, изображением, текстом), создавая представления одно поверх другого, а затем используя view.bringToFront и setVisibility, чтобы вывести представление на передний план и скрыть другие представления, когда пользователь щелкает кнопка.   -  person Simon    schedule 24.09.2015
comment
было бы это не выгодно? - нет, это также будет расточительно, так как у вас будет в каждой строке (потребление памяти и замедление макета представления/измерения) совершенно бесполезные представления. RecyclerView.Adapter имеет специальную систему для работы с этим сценарием, предлагая пользователю метод getItemViewType() (где вы можете указать адаптеру, что у вас есть разные типы строк, которыми нужно управлять).   -  person user    schedule 24.09.2015
comment
Согласен, но есть связь между картой, картинкой и текстом. Я не могу разделить ссылку и отобразить три отдельно друг под другом, используя getItemViewType(), поэтому я сейчас рисую все представления друг над другом и использую view.bringToFront и setVisibility для переключения между ними - я думаю, что путь В настоящее время я делаю это более расточительно, чем использование фрагментов, поскольку фрагменты предназначены для быстрого переключения между представлениями, и фрагмент карты не нужно загружать, если пользователь не нажимает кнопку карты.   -  person Simon    schedule 24.09.2015
comment
Используйте настраиваемый вид, например. RowView, который управляет своей внутренней динамикой — перелистыванием просмотров, слушателями, картами и т. д. — и заполняет этим переработчик. Таким образом, у вас есть доступ к жизненному циклу представления и вы можете (в конечном итоге, если это необходимо) раздувать фрагменты (скажем, MapFragment) без проблем.   -  person natario    schedule 25.09.2015
comment
происходит ли создание newPlaceFragment в тестовом фрагменте? я имею в виду, создается ли он в тестовом фрагменте   -  person Elltz    schedule 01.10.2015
comment
Тест — это основной фрагмент, связанный с вьюпейджером. NewPlaceFragment — это фрагмент, который находится в recyclerview в тестовом фрагменте.   -  person Simon    schedule 01.10.2015


Ответы (1)


Попробуйте изменить свой фрагмент в onBidViewHolder() и инициировать его с помощью recyclerViewAdapter.notifyItemChanged(position). И если вы измените свою логику с фрагментов на getItemViewType(), это будет лучшим решением.

Редактировать:

Ваша проблема началась, когда вы попытались установить фрагмент в динамически созданном представлении, и когда вы вызываете executePendingTransactions(), представления по-прежнему нет (объект представления создан, но все еще не на экране), эту проблему можно решить:

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

поэтому этот код будет вызываться только при создании представления. Но у вас все еще есть проблема с его идентификатором, потому что все элементы recyclerView имеют идентичные идентификаторы, вы можете избежать этого:

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

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

но этот код может работать только начиная с уровня API 17. Или вы можете создать свой собственный статический идентификатор (плохое решение).

И я до сих пор не вижу проблем с использованием логики getItemViewType() - вы можете создать 3 разных типа представления с копиями ваших кнопок на них, эти кнопки будут разными объектами, но они будут вызывать одни и те же onClickListeners.

Для получения дополнительной информации о динамически созданных представлениях и фрагментах см.: programmatically-created-content-v">Как добавить фрагмент в действие с программно созданным представлением содержимого

person q21forfun    schedule 30.09.2015
comment
Я не могу изменить логику с фрагмента на getItemViewType, поскольку кнопки изображения, текста и карты неразрывно связаны друг с другом. Пожалуйста, вы можете прочитать комментарий ниже вопроса для получения более подробной информации. - person Simon; 30.09.2015