Android 当与共享相同片段的 TabLayout/ViewPager 设置时,片段中的 Kotlin 回收器视图未加载正确的数据 class
Android Kotlin recycler view in the fragment is not loaded with the right data when set up with TabLayout/ ViewPager sharing same fragment class
我正在使用 Kotlin 开发 Android 应用程序。我正在构建一个 TabLayout,每个选项卡处理一个片段。这是布局
每个选项卡将共享同一个片段 class,其中有一个回收站视图。回收站视图的数据将根据通过包传递给片段的参数加载。
这是我的寻呼机适配器 class 将与 activity
中的视图寻呼机一起设置
class EventListPagerAdapter (fragmentManager: FragmentManager): FragmentPagerAdapter(fragmentManager)
{
var fragments: ArrayList<Fragment> = ArrayList<Fragment>()
init {
var currentFragment: Fragment = EventListFragment()
var currentBundle: Bundle = Bundle()
currentBundle.putInt(EventListFragment.KEY_TYPE, ApplicationController.EVENT_TYPE_CURRENT)
currentFragment.arguments = currentBundle
this.fragments.add(currentFragment)
var futureFragment: Fragment = EventListFragment()
var futureBundle: Bundle = Bundle()
futureBundle.putInt(EventListFragment.KEY_TYPE, ApplicationController.EVENT_TYPE_FUTURE)
futureFragment.arguments = futureBundle
this.fragments.add(futureFragment)
var pastFragment: Fragment = EventListFragment()
var pastBundle: Bundle = Bundle()
pastBundle.putInt(EventListFragment.KEY_TYPE, ApplicationController.EVENT_TYPE_PAST)
pastFragment.arguments = pastBundle
this.fragments.add(pastFragment)
}
override fun getItem(position: Int): Fragment {
return this.fragments.get(position)
}
override fun getCount(): Int {
return this.fragments.size
}
override fun getPageTitle(position: Int): CharSequence? {
when (position) {
0 -> return ApplicationController.instance.getString(R.string.event_list_tab_current)
1 -> return ApplicationController.instance.getString(R.string.event_list_tab_future)
}
return ApplicationController.instance.getString(R.string.event_list_tab_past)
}
}
如您所见,我为每个选项卡使用相同的片段,但通过包传递不同的变量。
这是带有回收器视图实现的片段class
class EventListFragment: Fragment()
{
@Inject
lateinit var eventService: IEventService
lateinit var eventList: ArrayList<EventModel>
lateinit var eventListAdapter: EventListAdapter
var eventType: Int? = 0
val TAG = "EVENT_LIST_FRAGMENT"
companion object{
val KEY_TYPE = "key_type"
lateinit var eventListObservable: Observable<List<EventModel>>
lateinit var eventListObserver: Observer<List<EventModel>>
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_event_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
this.initialise()
super.onViewCreated(view, savedInstanceState)
}
private fun initialise() {
ApplicationController.instance.appComponent.inject(this)
eventType = arguments?.getInt(KEY_TYPE, 0)
Log.i(TAG, "event type is $eventType")
//initialising the RxKotlin observable and observer
eventListObservable = Observable.empty()
eventListObserver = setUpEventListObserver()
eventListObservable.subscribe(eventListObserver)
//initialising the event list recycler view
eventList = ArrayList<EventModel>()
event_list_rc_list.layoutManager = GridLayoutManager(activity, 1)
eventListAdapter = EventListAdapter(eventList)
event_list_rc_list.adapter = eventListAdapter
eventService.getEvents(eventType as Int)
}
private fun setUpEventListObserver(): Observer<List<EventModel>> {
return object : Observer<List<EventModel>> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: List<EventModel>) {
eventList.addAll(t)
eventListAdapter.notifyDataSetChanged()
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
}
}
}
如您所见,我的片段 class 正在使用 Rx observable 和 Rx observer。 eventService 属性 负责为回收站视图加载数据。现在我正在使用 class 的伪造版本来加载数据并通过 Rx 观察器将数据发送回片段。
class FakeEventService: IEventService
{
override fun getEvents(eventType: Int) {
Handler().postDelayed({
EventListFragment.eventListObserver.onNext(listOf(
EventModel(11, "Event 1"),
EventModel(12, "Event 2"),
EventModel(13, "Event 3"),
EventModel(14, "Event 4"),
EventModel(15, "Event 5"),
EventModel(16, "Event 6"),
EventModel(17, "Event 7")
))
}, 1000)
}
}
根据我的代码,每个选项卡下的每个回收器视图都应该加载相同的数据。但它没有按预期工作。这就是我得到的。
当前选项卡
此选项卡下的回收站视图中没有加载任何数据
未来选项卡
此选项卡下的回收站视图正在加载两次数据:
"Event 1"
"Event 2"
"Event 3"
"Event 4"
"Event 5"
"Event 6"
"Event 7"
"Event 1"
"Event 2"
"Event 3"
"Event 4"
"Event 5"
"Event 6"
"Event 7"
过去的选项卡
此选项卡下的回收站视图加载了正确的数据。 (这个按预期工作)
以下是事件类型的值
val EVENT_TYPE_CURRENT: Int = 1
val EVENT_TYPE_FUTURE: Int = 2
val EVENT_TYPE_PAST: Int = 3
我的代码有什么问题?为什么第一个选项卡没有加载任何数据,为什么第二个选项卡加载了两次数据?
片段适配器中的IIRC,当前位置的片段和下一个片段被实例化,一个接一个。
在您的片段实现中:
lateinit var eventListObservable: Observable<List<EventModel>>
lateinit var eventListObserver: Observer<List<EventModel>>
包含在伴随对象中。它们链接到 class 而不是实例。
创建未来片段时,它将设置可观察对象,这将覆盖当前片段实例的值。
我的建议:
class FakeEventService: IEventService{
private val observableCurrent = Observable.just(listOf(
EventModel(11, "Event Current 1"),
EventModel(12, "Event Current 2"),
EventModel(13, "Event ... 3"),
EventModel(14, "Event 4"),
EventModel(15, "Event 5"),
EventModel(16, "Event 6"),
EventModel(17, "Event 7")
).delay(1, TimeUnit.SECOND)
)
private val observableFuture = Observable.just(listOf(
EventModel(11, "Event Future 1"),
EventModel(12, "Event Future 2"),
EventModel(13, "Event Future 3"),
EventModel(14, "Event ... 4"),
EventModel(15, "Event 5"),
EventModel(16, "Event 6"),
EventModel(17, "Event 7")
).delay(1, TimeUnit.SECOND)
)
override fun getEvents(eventType: Int): Observable<List<EventModel>> {
return when(eventType){
EventType.CURRENT -> observableCurrent
EventType.FUTURE -> observableFuture
}
}
然后在您的片段中,您可以删除您的可观察代码并执行
var disposableSubscription: Disposable? = null
override fun onStart(){
super.onStart()
//Please add an onError listener if the call could fail.
disposableSubscription = eventService.getEvents(eventType).subscribe{ events ->
eventList.addAll(t)
eventListAdapter.notifyDataSetChanged()
}
}
override onStop(){
super.onStop()
disposableSubscription?.dispose()
disposableSubscription = null
}
我正在使用 Kotlin 开发 Android 应用程序。我正在构建一个 TabLayout,每个选项卡处理一个片段。这是布局
每个选项卡将共享同一个片段 class,其中有一个回收站视图。回收站视图的数据将根据通过包传递给片段的参数加载。
这是我的寻呼机适配器 class 将与 activity
中的视图寻呼机一起设置class EventListPagerAdapter (fragmentManager: FragmentManager): FragmentPagerAdapter(fragmentManager)
{
var fragments: ArrayList<Fragment> = ArrayList<Fragment>()
init {
var currentFragment: Fragment = EventListFragment()
var currentBundle: Bundle = Bundle()
currentBundle.putInt(EventListFragment.KEY_TYPE, ApplicationController.EVENT_TYPE_CURRENT)
currentFragment.arguments = currentBundle
this.fragments.add(currentFragment)
var futureFragment: Fragment = EventListFragment()
var futureBundle: Bundle = Bundle()
futureBundle.putInt(EventListFragment.KEY_TYPE, ApplicationController.EVENT_TYPE_FUTURE)
futureFragment.arguments = futureBundle
this.fragments.add(futureFragment)
var pastFragment: Fragment = EventListFragment()
var pastBundle: Bundle = Bundle()
pastBundle.putInt(EventListFragment.KEY_TYPE, ApplicationController.EVENT_TYPE_PAST)
pastFragment.arguments = pastBundle
this.fragments.add(pastFragment)
}
override fun getItem(position: Int): Fragment {
return this.fragments.get(position)
}
override fun getCount(): Int {
return this.fragments.size
}
override fun getPageTitle(position: Int): CharSequence? {
when (position) {
0 -> return ApplicationController.instance.getString(R.string.event_list_tab_current)
1 -> return ApplicationController.instance.getString(R.string.event_list_tab_future)
}
return ApplicationController.instance.getString(R.string.event_list_tab_past)
}
}
如您所见,我为每个选项卡使用相同的片段,但通过包传递不同的变量。
这是带有回收器视图实现的片段class
class EventListFragment: Fragment()
{
@Inject
lateinit var eventService: IEventService
lateinit var eventList: ArrayList<EventModel>
lateinit var eventListAdapter: EventListAdapter
var eventType: Int? = 0
val TAG = "EVENT_LIST_FRAGMENT"
companion object{
val KEY_TYPE = "key_type"
lateinit var eventListObservable: Observable<List<EventModel>>
lateinit var eventListObserver: Observer<List<EventModel>>
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_event_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
this.initialise()
super.onViewCreated(view, savedInstanceState)
}
private fun initialise() {
ApplicationController.instance.appComponent.inject(this)
eventType = arguments?.getInt(KEY_TYPE, 0)
Log.i(TAG, "event type is $eventType")
//initialising the RxKotlin observable and observer
eventListObservable = Observable.empty()
eventListObserver = setUpEventListObserver()
eventListObservable.subscribe(eventListObserver)
//initialising the event list recycler view
eventList = ArrayList<EventModel>()
event_list_rc_list.layoutManager = GridLayoutManager(activity, 1)
eventListAdapter = EventListAdapter(eventList)
event_list_rc_list.adapter = eventListAdapter
eventService.getEvents(eventType as Int)
}
private fun setUpEventListObserver(): Observer<List<EventModel>> {
return object : Observer<List<EventModel>> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: List<EventModel>) {
eventList.addAll(t)
eventListAdapter.notifyDataSetChanged()
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
}
}
}
如您所见,我的片段 class 正在使用 Rx observable 和 Rx observer。 eventService 属性 负责为回收站视图加载数据。现在我正在使用 class 的伪造版本来加载数据并通过 Rx 观察器将数据发送回片段。
class FakeEventService: IEventService
{
override fun getEvents(eventType: Int) {
Handler().postDelayed({
EventListFragment.eventListObserver.onNext(listOf(
EventModel(11, "Event 1"),
EventModel(12, "Event 2"),
EventModel(13, "Event 3"),
EventModel(14, "Event 4"),
EventModel(15, "Event 5"),
EventModel(16, "Event 6"),
EventModel(17, "Event 7")
))
}, 1000)
}
}
根据我的代码,每个选项卡下的每个回收器视图都应该加载相同的数据。但它没有按预期工作。这就是我得到的。
当前选项卡
此选项卡下的回收站视图中没有加载任何数据
未来选项卡
此选项卡下的回收站视图正在加载两次数据:
"Event 1"
"Event 2"
"Event 3"
"Event 4"
"Event 5"
"Event 6"
"Event 7"
"Event 1"
"Event 2"
"Event 3"
"Event 4"
"Event 5"
"Event 6"
"Event 7"
过去的选项卡
此选项卡下的回收站视图加载了正确的数据。 (这个按预期工作)
以下是事件类型的值
val EVENT_TYPE_CURRENT: Int = 1
val EVENT_TYPE_FUTURE: Int = 2
val EVENT_TYPE_PAST: Int = 3
我的代码有什么问题?为什么第一个选项卡没有加载任何数据,为什么第二个选项卡加载了两次数据?
片段适配器中的IIRC,当前位置的片段和下一个片段被实例化,一个接一个。
在您的片段实现中:
lateinit var eventListObservable: Observable<List<EventModel>>
lateinit var eventListObserver: Observer<List<EventModel>>
包含在伴随对象中。它们链接到 class 而不是实例。
创建未来片段时,它将设置可观察对象,这将覆盖当前片段实例的值。
我的建议:
class FakeEventService: IEventService{
private val observableCurrent = Observable.just(listOf(
EventModel(11, "Event Current 1"),
EventModel(12, "Event Current 2"),
EventModel(13, "Event ... 3"),
EventModel(14, "Event 4"),
EventModel(15, "Event 5"),
EventModel(16, "Event 6"),
EventModel(17, "Event 7")
).delay(1, TimeUnit.SECOND)
)
private val observableFuture = Observable.just(listOf(
EventModel(11, "Event Future 1"),
EventModel(12, "Event Future 2"),
EventModel(13, "Event Future 3"),
EventModel(14, "Event ... 4"),
EventModel(15, "Event 5"),
EventModel(16, "Event 6"),
EventModel(17, "Event 7")
).delay(1, TimeUnit.SECOND)
)
override fun getEvents(eventType: Int): Observable<List<EventModel>> {
return when(eventType){
EventType.CURRENT -> observableCurrent
EventType.FUTURE -> observableFuture
}
}
然后在您的片段中,您可以删除您的可观察代码并执行
var disposableSubscription: Disposable? = null
override fun onStart(){
super.onStart()
//Please add an onError listener if the call could fail.
disposableSubscription = eventService.getEvents(eventType).subscribe{ events ->
eventList.addAll(t)
eventListAdapter.notifyDataSetChanged()
}
}
override onStop(){
super.onStop()
disposableSubscription?.dispose()
disposableSubscription = null
}