Как я могу запустить фрагмент из другого фрагмента, который является частью viewPager? - PullRequest
0 голосов
/ 03 марта 2020

Я только начинаю Android разработку в Kotlin, и я не совсем уверен, каков правильный подход к этой ситуации.

По сути, я пытаюсь сделать, это иметь ViewPager, который прокручивает список элементов того же типа. Вид, связанный с каждым элементом, имеет кнопку. Когда я нажимаю кнопку, я хочу запустить новый фрагмент, назовем его iteminfo. Фрагмент iteminfo должен использовать задний стек, чтобы вы могли нажать кнопку «Назад» и вернуться к просмотру элемента, на который вы смотрели, прежде чем нажать кнопку.

То, как я сейчас его настроил, таково: this:

-My MainActivity добавляет () мой фрагмент, содержащий ViewPager, в FragmentManager. (Я использую архитектуру с одним действием.)

-Мой фрагмент ViewPager содержит закрытый внутренний класс с PagerAdapter, который поддерживает список фрагментов элемента. Фрагменты элемента генерируются и добавляются в PagerAdapter при создании фрагмента ViewPager.

- Каждый из фрагментов элемента имеет свою собственную кнопку. Когда фрагмент создан, кнопка получает onClickListener.

Вот где я зашел в тупик ...

Пока мне удалось получить Отображаемый фрагмент iteminfo и исчезновение фрагмента элемента, но мой метод был довольно дерзким: я выполнил транзакцию фрагмента, чтобы добавить () фрагмент iteminfo к элементу root моего item_fragment. xml, а затем просто установил видимость другие элементы там ушли. Косметически это приводит меня туда, однако, если я нажму кнопку «Назад», приложение просто закроется. Так что это не поможет.

Я читал, что при переключении между представлениями фрагментов целесообразно использовать один пустой FrameLayout в файле. xml в качестве контейнера фрагментов, а затем добавить / удалите фрагменты из этого, используя FragmentManager. Это имеет смысл для меня ...

Однако я не уверен, как удалить () фрагмент, который находится внутри ViewPager. AFAIK, вы не можете установить тег фрагмента, если вы не делаете транзакцию фрагмента. Поэтому, если я хочу выполнить replace (), я не знаю, как ссылаться на фрагмент, который я заменяю.

Так же, как «Радуйся, Мария», я попытался просто заменить весь ViewPager фрагмент (так как это добавлено в MainActivity через транзакцию фрагмента, я смог пометить его), но по какой-то причине это просто приводит к NPE. Имеет смысл, что дочерний фрагмент не должен быть в состоянии заменить его родительский фрагмент, хотя я не на 100% уверен, почему это NPE.

В любом случае, любое просветление, которое вы можете доставить мне, очень ценится.

Редактировать: ниже добавлен соответствующий код.

MainActivity.kt:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val currentFragment = supportFragmentManager
            .findFragmentById(R.id.fragment_container)

        if (currentFragment == null) {
            val fragment = ItemPagerFragment.newInstance()
            supportFragmentManager
                .beginTransaction()
                .add(R.id.fragment_container, fragment, "viewPager")
                .commit()
        }
    }
}

activity_main. xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</FrameLayout>

ItemPagerFragment.kt:

class ItemPagerFragment: Fragment() {

    private lateinit var viewPager: ViewPager
    private lateinit var pagerAdapter: ItemPagerAdapter

    private val itemListViewModel:  ItemListViewModel by lazy {
        ViewModelProviders.of(this).get(ItemListViewModel::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val view = inflater.inflate(R.layout.fragment_item_pager, container, false)

        viewPager = view.findViewById(R.id.item_viewpager) as ViewPager
        pagerAdapter = ItemPagerAdapter(getChildFragmentManager())

        val items = itemListViewModel.items
        for (item in items){
            pagerAdapter.addFrag(ItemFragment.newInstance(item))
        }

        viewPager.adapter = pagerAdapter

        return view
    }

    private inner class ItemPagerAdapter(fm: FragmentManager)
        : FragmentStatePagerAdapter(fm)
    {

        private val itemFragmentList: MutableList<ItemFragment> = ArrayList()

        override fun getItem(i: Int): Fragment {
            return itemFragmentList[i]
        }

        override fun getCount() = itemFragmentList.size

        fun addFrag(f: ItemFragment){
            itemFragmentList.add(f)
        }

    }

    companion object {
        fun newInstance(): ItemPagerFragment {
            return ItemPagerFragment()
        }
    }

}

frag_item_pager. xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager.widget.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

ItemFragment.kt:

class ItemFragment(i: Item): Fragment() {

    private var item: Item = i;
    private lateinit var infoButton: Button

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_item, container, false)

        infoButton = view.findViewById(R.id.info_button) as Button

        infoButton.setOnClickListener{
            val fragment: InfoFragment =  InfoFragment.newInstance(item)
            val fragmentManager: FragmentManager = childFragmentManager
            val fragmentTransaction = fragmentManager.beginTransaction()

            // this is where I have been having no success
            // and know my approach is certainly wrong/confused
            fragmentTransaction.replace(R.id.item_frame, fragment)
            fragmentTransaction.addToBackStack(null)
            fragmentTransaction.commit()
        }

        return view
    }

    companion object {
        fun newInstance(i: Item): ItemFragment {
            return ItemFragment(i)
        }
    }

}

frag_item. xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_frame"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="8dp">

    <Button
        android:id="@+id/info_button"
        android:layout_gravity="center|bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/some_string"
        />

</FrameLayout>

InfoFragment.kt и фрагмент_инфо. xml может быть чем угодно, неважно, что они для целей моего вопроса.

1 Ответ

0 голосов
/ 03 марта 2020

Если я правильно понимаю вашу проблему, решение здесь довольно простое.

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

Суть в том, что все действия должны выполняться через Activity. Те. у вас должен быть интерфейс, который будет реализовывать ваша активность, и фрагменты получат свой экземпляр из контекста (например, в методе фрагмента onAttach ()). Когда вы нажимаете на кнопку, вы просто передаете свое событие в Activity. И Activity уже создает новый фрагмент, тем самым сохраняя backstack.

Важно понимать, что при работе с фрагментами они не должны ничего знать о существовании друг друга. Все операции должны выполняться через абстракцию и выполняться вашим parentView (Activity, ParentFragmen, et c.)

...