2

我一直在使用 ViewPager 使用选项卡布局一段时间,但它已被弃用。我按照官方文档迁移到 ViewPager2,一切正常,但每个片段都出现以下错误:

无效 ID 0x00000001 无效 ID 0x00000001 无效 ID 0x00000001

每次我在片段之间滑动时,这些都会增加。没有崩溃,但每次我重新打开活动时,数字都会增加到 Invalid ID 0x00000002 等等。

我迷失了解决这个错误的解决方案,但该应用程序仍然适用于新的 ViewPager2。有什么想法可以解决错误垃圾邮件吗?

活动代码:

//Responsible for adding the 4 tabs: NewLoot, ActiveLoot, SharedLoot, FinishedLoot
    private void setupViewPager(){
        SectionsPagerAdapter adapter = new SectionsPagerAdapter(getSupportFragmentManager(),getLifecycle());
        adapter.addFragment(new NewLootFragment());//index 0
        adapter.addFragment(new ActiveLootFragment());//index 1
        adapter.addFragment(new FinishedLootFragment());//index 2
        ViewPager2 viewPager = (ViewPager2) findViewById(R.id.container);
        viewPager.setAdapter(adapter);
        final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        new TabLayoutMediator(tabLayout, viewPager,
                new TabLayoutMediator.TabConfigurationStrategy() {
                    @Override
                    public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                        //tab.setText("OBJECT " + (position + 1));
                    }
                }
        ).attach();
        tabLayout.getTabAt(0).setIcon(R.drawable.ic_search);
        tabLayout.getTabAt(1).setIcon(R.drawable.ic_arrow);
        tabLayout.getTabAt(2).setIcon(R.drawable.ic_action_name);

        tabLayout.getTabAt(0).getIcon().setColorFilter(getResources().getColor(R.color.red), PorterDuff.Mode.SRC_IN);
        tabLayout.getTabAt(1).getIcon().setColorFilter(getResources().getColor(android.R.color.black), PorterDuff.Mode.SRC_IN);
        tabLayout.getTabAt(2).getIcon().setColorFilter(getResources().getColor(android.R.color.black), PorterDuff.Mode.SRC_IN);

        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                tab.view.getTab().getIcon().setColorFilter(getResources().getColor(R.color.red), PorterDuff.Mode.SRC_IN);
            }           @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                tab.view.getTab().getIcon().setColorFilter(getResources().getColor(android.R.color.black), PorterDuff.Mode.SRC_IN);
            }           @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });

    }

SectionPagerAdapter 代码:

//Class that stores fragments for tabs
public class SectionsPagerAdapter extends FragmentStateAdapter {
    private static String TAG = "SectionsPagerAdapter";

    private final ArrayList<Fragment> mFragmentList = new ArrayList<>();

    public SectionsPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle){
        super(fragmentManager, lifecycle);
    }

    //public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
    //    super(fragmentManager, lifecycle);
    //}

    public void addFragment(Fragment fragment){
        mFragmentList.add(fragment);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getItemCount() {
        return mFragmentList.size();
    }
}

布局活动:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Loot.LootActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Top Section (Toolbar) -->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/relLayout0">

            <include
                android:id="@+id/topViewBarHelper"
                layout="@layout/snippet_top_profilebar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

        </RelativeLayout>

        <!-- Top Section (Toolbar) -->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/relLayout1"
            android:layout_below="@+id/relLayout0">

            <include layout="@layout/layout_top_tabs"/>

        </RelativeLayout>

        <!-- Middle Section (Body) -->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/relLayout2"
            android:layout_below="@+id/relLayout1">

            <include layout="@layout/layout_center_viewpager"/>

        </RelativeLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/relLayout3">

            <!-- Bottom Section (Navigation) -->
            <include layout="@layout/layout_bottom_navigation_view"/>

        </RelativeLayout>

    </RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

布局中心查看器:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layoutDirection="ltr">
        </androidx.viewpager2.widget.ViewPager2>

    </RelativeLayout>

</merge>

布局顶部选项卡:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentTop="true">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/AppBarLayout">

            <com.google.android.material.tabs.TabLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/tab_layout"
                android:background="@drawable/white_grey_border_bottom"
                app:tabIndicatorColor="@color/red"
                app:tabIndicatorHeight="4dp">
            </com.google.android.material.tabs.TabLayout>

        </com.google.android.material.appbar.AppBarLayout>

    </RelativeLayout>

</merge>
4

1 回答 1

2

更新

因此,在我通过以下方式更新我的 Android Studio IDE 和无效缓存后,问题似乎消失了:

文件 -> 使缓存无效/重新启动...

另外值得注意的是,我现在使用的是Android Studio 北极狐 | 2020.3.1 Canary 10 Build #AI-203.7148.57.2031.7194378,于 2021 年 3 月 9 日构建

升级后,我删除了所有以前的模拟器,以确保问题出在我的代码上。到目前为止,日志已经尽可能干净了。如果有变化会再次更新。

原帖

当我在 YouTube 上看到Android Developers的视频时,我正试图自己解决这个问题。

我看着它并按照教练概述的步骤进行操作。在那个视频中,大约2:50时,教练说:

“......而不是使用 viewPager1 中的 getItem,您现在将使用 createFragment,是的,我们希望您为特定位置创建一个新片段。”

这句话让我相信他是在建议我们为特定位置创建一个新片段,而不是存储和重用片段。

因此,我停止将片段列表传递给我的 FragmentStateAdapter 实现,而是在 Kotlin 中使用when 表达式,您可以在java中使用switch/case作为相同的替代方案。

我的示例只有两个片段,但我希望你能明白:

    class MyAdapter(activity: AppCompatActivity) : FragmentStateAdapter(activity) {
        override fun getItemCount(): Int {
            return 2 // I just wanted 2 fragments in my app
        }
    
        override fun createFragment(position: Int): Fragment {
            return when(position){
                0 -> Fragment1()
                1 -> Fragment2()
                else -> Fragment1() // this case should not happen but you can log it to debug console or do nothing {}!
            }
        }
    }

我的 TabLayoutMediator 看起来像这样:

    TabLayoutMediator(tabLayout, viewPager, true ){ tab, position ->
        tab.text = when(position){
            0 -> "Fragment1"
            1 -> "Fragment2"
            else -> "Unknown" // again, this case should not happen!
        }
    }.attach()

然而,尽管 INVALID ID 消失了一段时间,但没过多久它又回来并开始填充我的调试控制台。这很奇怪,因为近一个月以来我一直以同样的方式使用 view pager2,这是我第一次遇到错误,即今天。

我注意到我的模拟器在错误出现之前闪烁了一次,我附上了一些在屏幕闪烁之前出现的日志并且 Invalid ID 开始出现,以防万一有人能找到发生了什么。

>mServiceHandler:Landroid/app/IntentService$ServiceHandler; (greylist, linking, allowed)
2021-03-15 21:08:42.046 9623-9623/com.kwaso.recorder W/.kwaso.recorde: Accessing hidden field Landroid/app/IntentService;->mRedelivery:Z (greylist-max-o, linking, denied)
2021-03-15 21:08:42.046 9623-9623/com.kwaso.recorder W/.kwaso.recorde: Accessing hidden field Landroid/app/IntentService;->mRedelivery:Z (greylist-max-o, linking, denied)
2021-03-15 21:08:42.057 9623-9623/com.kwaso.recorder W/.kwaso.recorde: Class android.os.PowerManager$WakeLock failed lock verification and will run slower.
2021-03-15 21:08:42.077 9623-9623/com.kwaso.recorder V/StudioProfiler: Profiler initialization complete on agent.
2021-03-15 21:08:42.079 9623-9737/com.kwaso.recorder V/StudioProfiler: Acquiring Application for Events
2021-03-15 21:08:42.083 9623-9738/com.kwaso.recorder W/InputMethodManager: InputMethodManager.getInstance() is deprecated because it cannot be compatible with multi-display. Use context.getSystemService(InputMethodManager.class) instead.
    java.lang.Throwable
        at android.view.inputmethod.InputMethodManager.getInstance(InputMethodManager.java:1234)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.tools.profiler.support.profilers.EventProfiler$InputConnectionHandler.run(EventProfiler.java:261)
        at java.lang.Thread.run(Thread.java:923)
2021-03-15 21:08:42.084 9623-9738/com.kwaso.recorder W/InputMethodManager: InputMethodManager.peekInstance() is deprecated because it cannot be compatible with multi-display. Use context.getSystemService(InputMethodManager.class) instead.
    java.lang.Throwable
        at android.view.inputmethod.InputMethodManager.peekInstance(InputMethodManager.java:1253)
        at android.view.inputmethod.InputMethodManager.getInstance(InputMethodManager.java:1239)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.tools.profiler.support.profilers.EventProfiler$InputConnectionHandler.run(EventProfiler.java:261)
        at java.lang.Thread.run(Thread.java:923)
2021-03-15 21:08:45.429 9623-9623/com.kwaso.recorder D/FilesFragment: onViewCreated: 
2021-03-15 21:08:45.464 9623-9740/com.kwaso.recorder D/UtilityFunctions: loadFilesInNewAPI: 
2021-03-15 21:08:50.128 9623-9712/com.kwaso.recorder V/StudioTransport: Transport agent connected to daemon.
2021-03-15 21:08:50.293 9623-9717/com.kwaso.recorder V/StudioTransport: Handling agent command 1100 for pid: 9623.
2021-03-15 21:08:50.293 9623-9717/com.kwaso.recorder V/StudioTransport: JNIEnv not attached
2021-03-15 21:08:50.368 9623-9623/com.kwaso.recorder E/.kwaso.recorde: Invalid ID 0x00000000.

这就是消息开始的时候。

在这一点上,我的猜测是,当片段被创建/重新加载/恢复时,一些资源 ID 没有被清除。此外,该应用程序不会崩溃或滞后,它只是在应用程序启动后 10 秒左右闪过一次屏幕,然后控制台中的混乱随之而来,UI 上什么也没有!

我把它放在这里,以便其他人可以阅读它并可能找到解决方案。

于 2021-03-15T15:52:00.550 回答