当我从FragB(从活动工具栏启动的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)
因此,从this SO线程中读取后,它说要进行空检查或在onAttach中重新初始化viewModel,但是这里的奇怪行为是,当我启动bottomsheetfragment时,没有调用FragA的生命周期方法(活动工具栏上的FragB),当我在按钮上单击dismiss()时,将仅调用FragB的生命周期方法,并且再次没有调用Viewpager片段的生命周期方法,因此当该片段分离时,现在从哪里我应该重新初始化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")
}
}
答案 0 :(得分:1)
好的。我真的以为这次可以了:
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只是Android建议的Map<Int, ?>
。您可以改为一次在适配器实例中跟踪您分发的片段列表。这样做的好处是,从理论上讲,它的性能更高,而且您将知识保持在本地。从理论上讲,缺点是您持有的框架管理对象的范围可能与框架使用的范围不同。
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) }
}
答案 1 :(得分:0)
应为应用程序上下文启动ViewModels。像这样: someViewModel = activity.let {ViewModelProvider( requireActivity())。get(SomeViewModel :: class.java)}
尽管您没有粘贴代码的适当部分-我怀疑您是将ViewModel生命周期与片段相关联-您可以通过片段内的requireContext()获得该代码。
尝试使用requireActivity()
由于提供了代码,因此进行了更新: userModel = ViewModelProviders.of(this)[UserViewModel :: class.java]
这-是对Fragment的引用。我盲目的猜测写的是正确的。使用requireActivity()