首页 > 解决方案 > onDetach() 没有调用从一个片段移动到另一个片段

问题描述

当我从 FragB(从 ativity 的工具栏启动的 bottomsheetfragment)到 FragA(从活动的 viewpager)获取值并通过接口调用 FragA 的方法以使用新数据再次访问 API 时,它给了我以下异常:

java.lang.IllegalStateException:无法为分离的片段创建 ViewModelProvider

在将数据从 FragB 获取到活动后,我使用以下方法从活动中调用 FragA 的方法:

val getFragment = pagerAdapter.getItem(viewPager.currentItem)

        if(getFragment is GrowthStoryFragment){
            getFragment.myfilterOptions(countryId, dateRange, specs)

因此,在阅读了这个SO 线程后,它说进行空检查或重新初始化 onAttach 中的 viewModel 但这里的奇怪行为是,当我从活动工具栏启动 bottomsheetfragment(FragB) 时,没有调用 FragA 的生命周期方法当我在按钮单击上关闭() FragB 时,仅调用 FragB 的生命周期方法,并且再次没有调用 viewpager 片段 FragA 的生命周期方法,因此当片段分离时,现在我应该从哪里重新初始化 viewModel和其他情况?

请帮助我理解这种情况。

更新:这是活动代码:

class ViewDetailsActivity : BaseActivity(), FilterOptionsDialogFragment.onApplyEventListener, BusinessUnitDialogFragment.afterDoneClick {


    private lateinit var pagerAdapter: ViewDetailsFragmentAdapter
    private var myFragmentFlag = 0

    private var TAG = "ViewDetailsActivity"

    override fun getContentView(): Int = R.layout.activity_view_details

    override fun onViewReady(savedInstanceState: Bundle?, intent: Intent?) {


        tabLayout!!.addTab(tabLayout!!.newTab().setText("Growth Story"))
        tabLayout!!.addTab(tabLayout!!.newTab().setText("Share Story"))
        tabLayout!!.addTab(tabLayout!!.newTab().setText("Purchase Dynamics"))
        tabLayout!!.addTab(tabLayout!!.newTab().setText("Brand Health Track"))
        tabLayout.tabMode = TabLayout.MODE_SCROLLABLE
        tabLayout.tabGravity = Gravity.CENTER
        tabLayout.setTabTextColors(Color.parseColor("#a1a1a1"), Color.parseColor("#ff8a00"))

        pagerAdapter = ViewDetailsFragmentAdapter(supportFragmentManager, tabLayout!!.tabCount)
        viewPager.adapter = pagerAdapter
        viewPager.isFocusableInTouchMode = true

        scrollListener()

        viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))

        tabLayout!!.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab) {
                viewPager!!.currentItem = tab.position
                when (tab.position) {
                    0 -> {
                        myFragmentFlag = 0
                        left_scroll.visibility = View.GONE
                        right_scroll.visibility = View.VISIBLE
                    }
                    1 -> {
                        myFragmentFlag = 1
                        right_scroll.visibility = View.VISIBLE
                    }
                    2 -> {
                        myFragmentFlag = 2
                        left_scroll.visibility = View.VISIBLE
                        right_scroll.visibility = View.VISIBLE
                    }
                    3 -> {
                        myFragmentFlag = 3
                        left_scroll.visibility = View.VISIBLE
                        right_scroll.visibility = View.GONE
                    }
                }
            }

            override fun onTabUnselected(tab: TabLayout.Tab) {

            }

            override fun onTabReselected(tab: TabLayout.Tab) {

            }
        })

        tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen._16ssp))
        tvTitle.setText("Category Deep Dive")
        ivBack.setOnClickListener(View.OnClickListener {
            finish()
        })
        ivLogo.setOnClickListener(View.OnClickListener {
            val addFrag = FilterOptionsDialogFragment.newInstance()
            addFrag.show(supportFragmentManager, "add")
        })

        vd_edit_icon.setOnClickListener({
            val buFrag = BusinessUnitDialogFragment.newInstance()
            buFrag.show(supportFragmentManager, "add")
        })

        vd_edit_icon.visibility = View.VISIBLE
    }

    fun scrollListener() {
        left_scroll.setOnClickListener(View.OnClickListener {
            val itemnum = viewPager.currentItem
            viewPager.currentItem = itemnum - 1
        })

        right_scroll.setOnClickListener(View.OnClickListener {
            val itemnum = viewPager.currentItem
            viewPager.currentItem = itemnum + 1
        })
    }

    override fun someEvent(countryId: String?, dateRange: String?, specs: String?) {
        val getFragment = pagerAdapter.getItem(viewPager.currentItem)

        if(getFragment is GrowthStoryFragment){
            getFragment.myfilterOptions(countryId, dateRange, specs)
        }else if(getFragment is ShareStoryFragment){
            getFragment.myfilterOptions(countryId, dateRange, specs)
        }else if(getFragment is PurchaseDynamicsFragment){
            getFragment.myfilterOptions(countryId, dateRange, specs)
        }else if(getFragment is BrandHealthFragment){
            getFragment.myfilterOptions(countryId, dateRange, specs)
        }
    }

    override fun onDoneClicked(item: String) {

        val getFragment = pagerAdapter.getItem(viewPager.currentItem)

        if(getFragment is GrowthStoryFragment){
            getFragment.getBuName(item)
        }else if(getFragment is ShareStoryFragment){
            getFragment.getBuName(item)
        }else if(getFragment is PurchaseDynamicsFragment){
            getFragment.getBuName(item)
        }else if(getFragment is BrandHealthFragment){
            getFragment.getBuName(item)
        }

    }

}

片段适配器:

import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter

class ViewDetailsFragmentAdapter(supportFragmentManager: FragmentManager,internal var totalTabs: Int): FragmentStatePagerAdapter(supportFragmentManager) {
    override fun getItem(position: Int): Fragment? {
        when (position) {
            0 ->  return GrowthStoryFragment()
            1 -> return ShareStoryFragment()
            2 -> return PurchaseDynamicsFragment()
            3 -> return BrandHealthFragment()
            else -> return null
        }
    }

    // this counts total number of tabs
    override fun getCount(): Int {
        return totalTabs
    }

}

片段 A:

class GrowthStoryFragment : Fragment() {

    private val TAG = "GrowthStoryFragment"
    private  lateinit var disposable : Disposable
    private lateinit var responseSpinner : List<RespCat>
    private lateinit var responseFirstBarChart : List<RespBrand>
    private lateinit var RespDon : List<RespDon>
    private lateinit var responseSecondBarChart : List<RespDist>

    companion object{

        private lateinit var myApplicationContext : Context
        private var countryID = "1"
        private var date = "MAT TY"
        private var spec  = "val"

        private var businessUnitID = "2"
        private var category = "Fresh Milk"
        private var firstReportTypeId = "1"       //fixed for growth story and share story
        private var isGroup = "false"             //fixed to false
    }

    private lateinit var userModel : UserViewModel

    private val backendApi = WinRetrofitHelper.winApiInstance()

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        Log.e(TAG, "OnAttach")
        userModel = ViewModelProviders.of(this)[UserViewModel::class.java]

    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        Log.e(TAG, "OnCreateView")
        return inflater.inflate(R.layout.fragment_growth_story, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.e(TAG, "OnViewCreated")
        myApplicationContext = context!!.applicationContext
        getSpinnerResponse(businessUnitID, isGroup,firstReportTypeId)
//        getSuperRegionName(countryID, date,spec," ",businessUnitID, category, firstReportTypeId, isGroup)

        growth_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
            override fun onNothingSelected(parent: AdapterView<*>?) {

            }

            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val item  = parent?.getItemAtPosition(position) as RespCat
                category = item.nameValue
                Log.e(TAG,"Category name is: " + category)
                getSuperRegionName(countryID, date,spec," ",businessUnitID, category, firstReportTypeId, isGroup)
            }
        }

    }

    private fun getSpinnerResponse(businessUnitID: String, isGroup: String, firstReportTypeId: String){

        userModel.getResponseGrowthSpinner(businessUnitID, isGroup, firstReportTypeId)
        userModel.responseGrowthSpinner.observe(this,
                Observer {
                    Utils.debugger("FRAG ", "$it")
                    growth_spinner.adapter = GrowthSpinnerAdapter(it)
                })
    }

    private fun getSuperRegionName(countryID: String, date: String, spec: String, superMarket: String,businessUnitID: String, category: String, firstReportTypeId: String, isGroup: String) {

        userModel.getResponseSuperRegion(countryID)
        userModel.responseSuperRegion.observe(this,
                Observer {
                    Utils.debugger("FRAG ", "$it")
                    getDataFromApi(countryID, date, spec, it!!.get(0).nameValue, businessUnitID, category, firstReportTypeId, isGroup)
                })
    }

    private fun getColorID(position: Int): Int {
        try {
            val rnd = Random
            when (position) {
                0 -> return R.color.brand_almarai
                1 -> return R.color.brand_alsafi
                2 -> return R.color.brand_nadec
                3 -> return R.color.brand_sadafco
                4 -> return R.color.brand_nestle
                5 -> return R.color.brand_amul
                6 -> return R.color.brand_nada
            }
            return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256))
        }catch (e :Exception){
            e.printStackTrace()
        }
        return 1
    }

     fun myfilterOptions(countryId: String?, dateRange: String?, specs: String?){
         userModel = ViewModelProviders.of(this)[UserViewModel::class.java]
         getSuperRegionName(countryId!!,dateRange!!,specs!!.toLowerCase()," ",businessUnitID, category, firstReportTypeId, isGroup)
        Log.e(TAG, "Growth Story Fragment:" +countryId!!+" "+dateRange!!+" "+specs!!.toLowerCase()+ " "+businessUnitID+ " "+category+ " "+firstReportTypeId+ " "+isGroup)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "Ondestroy")
        disposable.dispose()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.e(TAG, "OnCreate")
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Log.e(TAG, "OnActivitycreated")
    }

    override fun onPause() {
        super.onPause()
        Log.e(TAG, "OnPause")
    }

    override fun onStart() {
        super.onStart()
        Log.e(TAG, "OnStart")
    }

    override fun onResume() {
        super.onResume()
        Log.e(TAG, "OnResume")
    }

    override fun onStop() {
        super.onStop()
        Log.e(TAG, "OnStop")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.e(TAG, "Ondestroyview")
    }

    override fun onDetach() {
        super.onDetach()
        Log.e(TAG, "OnDetach")
    }

}

FragB代码:

class FilterOptionsDialogFragment : BottomSheetDialogFragment(), View.OnClickListener {

    private var myResp: List<RespBu>? = null
    private lateinit var myView: View
    private lateinit var customList: ArrayList<RespBu>
    private var dateRange: String = ""
    private var specrange: String = ""
    private lateinit var onmyApplyEventListener: onApplyEventListener
    private var TAG = "FilterOptionsDialogFragment"

    val pref: AppPreference by lazy {
        AppPreference.getInstance(context!!)
    }

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

    override fun onCreateView(inflater: LayoutInflater,
                              @Nullable container: ViewGroup?,
                              @Nullable savedInstanceState: Bundle?): View? {
        myView = inflater.inflate(R.layout.filter_options_layout, container, false)
        Log.e(TAG, "OnCreateView")
        // get the views and attach the listener

        val backendApi = WinRetrofitHelper.winApiInstance()
        val request = backendApi.getBUCountry()

        request.enqueue(object : Callback<List<RespBu>> {
            override fun onFailure(call: Call<List<RespBu>>?, t: Throwable?) {

            }

            override fun onResponse(call: Call<List<RespBu>>?, response: Response<List<RespBu>>?) {
                val spinner = myView.findViewById<Spinner>(R.id.filter_options_spinner)

                spinner.adapter = Spinner_filter_options(getNewList(response?.body()))
                if(pref.getString("BuID") != null && !pref.getString("BuID").equals("")){

                    if(pref.getBoolean(Site.BUSINESS_UNIT_FRONT)!=null && pref.getBoolean(Site.BUSINESS_UNIT_FRONT))
                        filter_options_spinner.setSelection(pref.getString("BuID").toInt()-1)
                    else
                        filter_options_spinner.setSelection(pref.getString("BuID").toInt()-1)
                }
            }
        })

        return myView
    }

    override fun onStart() {
        super.onStart()
        Log.e(TAG, "OnStart")
    }

    private fun getNewList(mylist: List<RespBu>?): List<RespBu> {
        if(pref.getBoolean(Site.BUSINESS_UNIT_FRONT)!=null && pref.getBoolean(Site.BUSINESS_UNIT_FRONT))
            return mylist!!
        else{
            customList = ArrayList()
            customList.add(RespBu("0", 0, "Global", 0))
            for (item in mylist.orEmpty()) {
                customList.add(item)
            }
            return customList
        }

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        try {

            if (pref.getString("dateName") != null && !pref.getString("dateName").equals("")) {
                if (pref.getString("dateName").equals("YTD"))
                    date_ytd.isChecked = true
                else
                    date_mat.isChecked = true
            }

            if (pref.getString("specName") != null && !pref.getString("specName").equals("")) {
                if (pref.getString("specName").equals("VAL"))
                    spec_val.isChecked = true
                else
                    spec_vol.isChecked = true
            }

            val dateradiogroup = view.findViewById<RadioGroup>(R.id.date_radio_group)
            val specradiogroup = view.findViewById<RadioGroup>(R.id.spec_radio_group)

            view.findViewById<ImageView>(R.id.view_close).setOnClickListener(View.OnClickListener {
                dismiss()
            })

            view.findViewById<Button>(R.id.apply).setOnClickListener(View.OnClickListener {
                val dateRadioBtn = view.findViewById<RadioButton>(dateradiogroup.checkedRadioButtonId)
                val specRadioBtn = view.findViewById<RadioButton>(specradiogroup.checkedRadioButtonId)
                val respBu = view.findViewById<Spinner>(R.id.filter_options_spinner).selectedItem as RespBu

                val buName = respBu.keyValue.toString()
                val dateName = dateRadioBtn.text.toString()
                val specName = specRadioBtn.text.toString()

                pref.saveString("BuID", filter_options_spinner.selectedItemId.toString())
                pref.saveString("dateName", dateName)
                pref.saveString("specName", specName)
                pref.saveString("BuName", respBu.keyValue.toString())

                onmyApplyEventListener.someEvent(buName, dateName + " TY", specName)
                Log.e("Filter item", respBu.nameValue + " " + dateRadioBtn.text)
                dismiss()
            })


            view.findViewById<Spinner>(R.id.filter_options_spinner).onItemSelectedListener = object : OnItemSelectedListener {
                override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {

                }

                override fun onNothingSelected(parent: AdapterView<*>?) {

                }
            }
        }catch (e: Exception){
            e.printStackTrace()
        }
    }

    override fun onClick(p0: View?) {

    }

    interface onApplyEventListener {
        fun someEvent(countryId: String?, dateRange: String?, specs: String?)
    }

    override fun onAttach(activity: Activity?) {
        super.onAttach(activity)
        Log.e(TAG, "OnAttach")
        onmyApplyEventListener = activity as onApplyEventListener
    }



    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.e(TAG, "onCreate")
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Log.e(TAG, "OnActivitycreated")
    }

//    override fun onAttach(context: Context?) {
//        super.onAttach(context)
//        onmyApplyEventListener = context as onApplyEventListener
//        Log.e(TAG, "OnAttach")
//    }

    override fun onPause() {
        super.onPause()
        Log.e(TAG, "OnPause")
    }

    override fun onResume() {
        super.onResume()
        Log.e(TAG, "OnResume")
    }

    override fun onStop() {
        super.onStop()
        Log.e(TAG, "OnStop")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.e(TAG, "Ondestroyview")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "Ondestroy")
    }

    override fun onDetach() {
        super.onDetach()
        Log.e(TAG, "OnDetach")
    }

}

标签: androidandroid-fragments

解决方案


行。我真的认为我这次拥有它:

ViewDetailsFragmentAdapter#getItem每次都返回一个新实例。当您稍后调用时#getItem,您将获得一个未初始化的片段实例,该实例当前也未附加到任何Activity. 结果,您所做的任何事情都不会得到您正在寻找的东西。通过确保每次为给定的页面类型交回完全相同的实例,您应该是安全的。

您已经提到FragmentManager#getFragments返回一个列表,其中包含您之前初始化的片段。FragmentManager您可以通过从给定知道的片段中按类型获取所需的片段来利用这一点:


class ViewDetailsFragmentAdapter(supportFragmentManager: FragmentManager,internal var totalTabs: Int): FragmentStatePagerAdapter(supportFragmentManager) {

    override fun getItem(position: Int): Fragment? {
        return when (position) {
            0 ->  existing<GrowthStoryFragment>() ?: GrowthStoryFragment()
            1 -> existing<ShareStoryFragment>() ?: ShareStoryFragment()
            2 -> existing<PurchaseDynamicsFragment>() ?: PurchaseDynamicsFragment()
            3 -> existing<BrandHealthFragment>() ?: BrandHealthFragment()
            else -> return null
        }
        }

    private inline fun <reified T> existing(): T? =
        supportFragmentManager.getFragments().firstOrNull { it is T } as T?

SparseArray只是Map<Int, ?>Android 建议的。您可以改为跟踪您在适配器实例中分发的片段列表。这里的好处是它理论上性能更高,并且您将知识保持在本地。理论上的缺点是您持有的框架管理对象的范围可能与框架使用的范围不同。


class ViewDetailsFragmentAdapter(supportFragmentManager: FragmentManager,internal var totalTabs: Int): FragmentStatePagerAdapter(supportFragmentManager) {
    private val pages: SparseArray<Fragment> by lazy(:: { SparseArray(totalTabs) }

    override fun getItem(position: Int): Fragment? {
        return pages.get(position) ?:
            when (position) {
                0 -> GrowthStoryFragment()
                1 -> ShareStoryFragment()
                2 -> PurchaseDynamicsFragment()
                3 -> BrandHealthFragment()
                else -> null
            }.also { pages.put(position, it) }
        }

推荐阅读