【问题标题】:ViewPager2 causes crash by confusing two instances of FragmentStateAdapterViewPager2 通过混淆 FragmentStateAdapter 的两个实例导致崩溃
【发布时间】:2020-09-11 16:45:20
【问题描述】:

我在我的应用程序中的片段中使用ViewPager2TabLayout(我们称其为主要片段)。这些页面本身就是片段,每个都按格式表示音乐项目列表,例如CD 页面、黑胶唱片页面等。FragmentStateAdapter 包含通过观察LiveData 对象更新的格式列表。

我为用户添加了一个选项,可以在 DialogFragment 中添加格式(即页面),这会在某些情况下使应用程序崩溃。如果在用户打开音乐项目时使用它——从而让主片段经历 onPause、onStop 等生命周期阶段——然后返回主片段,再次打开音乐项目并添加新的格式,应用程序崩溃,并且 logcat 显示这是因为 虽然它从最近更新的FragmentStateAdapter 获取选项卡/页面的数量,但它所经历的格式列表本身来自以前的 FragmentStateAdapter生命周期。因此,当用于设置标签文本的TabLayoutMediator 回调遇到IndexOutOfBoundsException 时,应用会崩溃。

我希望我已经设法足够清楚地解释了这个问题。 如果有任何遗漏,请告诉我。

以下是相关代码:

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
//        Log.d(TAG, "onViewCreated called");
        viewPager = view.findViewById(R.id.view_pager);
        tabLayout = view.findViewById(R.id.tab_layout);

        mViewModel = new ViewModelProvider(this).get(UserItemsViewModel.class);
        viewPager.setAdapter(new ItemListFragmentAdapter(requireActivity(), mType));
        new TabLayoutMediator(tabLayout, viewPager,
                (tab, position) -> {

//                    Log.d(TAG, "position: " + position);
//                    Log.d(TAG, String.valueOf(listFragmentAdapter.mItems));
                    if (position == 0) {
                        tab.setText("All");
                    } else {
//                        Log.d(TAG, "position: " + position);
//                        Log.d(TAG, "onActivityCreated: " + listFragmentAdapter);
                        tab.setText(((ItemListFragmentAdapter)viewPager.getAdapter()).getItemAt(position - 1).getName());
                    }
                }
        ).attach();

    }

class ItemListFragmentAdapter extends FragmentStateAdapter {

//        private static final String TAG = ItemListFragmentAdapter.class.getSimpleName();

        private final String mType;
        private AtomicReference<List<FormatItem>> mItems;

        ItemListFragmentAdapter(FragmentActivity activity, String type) {
            super(activity);
            mType = type;
            mItems = new AtomicReference<>();

            mViewModel.getFormatsLD().observe(activity, new Observer<List<FormatItem>>() {
                @Override
                public void onChanged(List<FormatItem> formatItems) {
                    mItems.set(formatItems);
                    notifyDataSetChanged();


                }
            });
        }




        @NonNull
        @Override
        public Fragment createFragment(int position) {
            Log.d(TAG, "createFragment called");
            if (mType.equals(ARG_COLLECTION)) {
                return CollectionFragment.newInstance(position);
            } else if (mType.equals(ARG_WISHLIST)) {
                return WishlistFragment.newInstance(position);
            } else {
                throw new IllegalArgumentException();
            }
        }


        @Override
        public int getItemCount() {
            Log.d(TAG, "getItemCount: " + this);
            if(mItems.get() != null){
                Log.d(TAG, "getItemCount: size - " + mItems.get().size());
            }
            return mItems.get() == null ? 1 : mItems.get().size() + 1;
        }

        FormatItem getItemAt(int i){
            Log.d(TAG, "i: " + i);
            Log.d(TAG, "size: " + mItems.get().size());
            Log.d(TAG, "list: " + mItems.get());
            Log.d(TAG, "getItemAt: " + this);
            return mItems.get() == null ? null : mItems.get().get(i);
        }


    }

【问题讨论】:

    标签: android android-tablayout android-viewpager2


    【解决方案1】:

    最终我自己想通了。问题是我从适配器的构造函数中观察到了LiveData 格式。这使得notifyDataSetChanged() 方法可以通知适配器的确切实例。但是 ViewPager 已经从onViewCreated 中新建的适配器中获取了页数。我通过将 LiveData 观察者移动到 onViewCreated 并通知当前与 ViewPager 关联的适配器来修复它:

     mViewModel = new ViewModelProvider(this).get(UserItemsViewModel.class);
            viewPager.setAdapter(new ItemListFragmentAdapter(requireActivity(), mType));
            new TabLayoutMediator(tabLayout, viewPager,
                    (tab, position) -> {
    
                        if (position == 0) {
                            tab.setText("All");
                        } else {
                            tab.setText(((ItemListFragmentAdapter) viewPager.getAdapter()).getItemAt(position - 1).getName());
                        }
                    }
            ).attach();
    
            mViewModel.getFormatsLD().observe(getViewLifecycleOwner(), new Observer<List<FormatItem>>() {
                @Override
                public void onChanged(List<FormatItem> formatItems) {
                    ItemListFragmentAdapter adapter = (ItemListFragmentAdapter) viewPager.getAdapter();
                    if (adapter != null) {
                        ((ItemListFragmentAdapter) viewPager.getAdapter()).setItems(formatItems);
                        adapter.notifyDataSetChanged();
                    }
                }
            });
    

    【讨论】:

      猜你喜欢
      • 2018-05-08
      • 2021-06-29
      • 1970-01-01
      • 1970-01-01
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 2019-12-10
      • 2014-10-04
      相关资源
      最近更新 更多