在 BottomNavigationView 中切换选项卡时出现延迟

Posted

技术标签:

【中文标题】在 BottomNavigationView 中切换选项卡时出现延迟【英文标题】:Lag when switching tabs in BottomNavigationView 【发布时间】:2020-06-09 16:42:12 【问题描述】:

我有一个包含BottomNavigationView 的Activity,这个bottomnav 帮助Activity 显示三个片段。这些片段加载良好,我使用 AsyncTask 来完成每一个繁重的操作,而在 UI 线程中,我会显示一个 ProgressBar,直到所有内容加载。 我的片段有一个奇怪的行为:我第一次加载片段时需要一些时间才能实际显示它,而不是使用进度条立即显示它。 这件事只发生在第一次,而且只发生在这个片段中。 片段代码只包含这个:

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) 
        super.onActivityCreated(savedInstanceState);
        new LoadData(getView(), getContext()).execute();
    

private class LoadData extends AsyncTask<Void, Void, Void> 

        private View v;
        private Context context;

        public LoadData(View v, Context context) 
            items = new ArrayList<>();
            this.v = v;
            this.context = context;
        

        @Override
        protected Void doInBackground(Void... voids) 
            setItems(context); //Heavy operation
            adapter = new DashAdapter(items, context);
            return null;
        

        @Override
        protected void onPreExecute() 
            super.onPreExecute();
            //shows progressbar
            progress = v.findViewById(R.id.DFProgress);
            progress.setVisibility(View.VISIBLE);
        

        @Override
        protected void onPostExecute(Void aVoid) 
            super.onPostExecute(aVoid);
            setPager();
            //sets viewPager and hides progressbar
            progress.setVisibility(View.GONE);
        
    

在下面的gif中,如果你看一下底部的bottomnavigationview,你可以看到显示fragment需要时间。但是在尝试第二次加载片段后,它按预期加载。

我怎样才能使片段以正确的方式加载?

【问题讨论】:

您是否在 Fragment 的 onCreateonCreateView 或其他生命周期方法中做其他事情? 是的,我设置了其他 UI 元素 可以分享setPager()的代码吗? 【参考方案1】:

我遇到了同样的问题。我有两个选择。

    调用 LoadData 时使用 postdelay 或 首先手动添加所有片段。您自己管理 navigationItemSelected。

像这样:

val firstFragment: Fragment = FirstFragment()
val secondFragment: Fragment = SecondFragment()
val thirdFragment: Fragment = ThirdFragment()

val navView: BottomNavigationView = findViewById(R.id.nav_view)

var active = firstFragment
fm.beginTransaction().add(R.id.nav_host_fragment, thirdFragment, "3").hide(thirdFragment).commit()
fm.beginTransaction().add(R.id.nav_host_fragment, secondFragment, "2").hide(secondFragment).commit()
fm.beginTransaction().add(R.id.nav_host_fragment, firstFragment, "1").commit()

navView.setOnNavigationItemReselectedListener   
navView.setOnNavigationItemSelectedListener  item ->
       when (item.itemId) 
           R.id.navigation_first -> 
               fm.beginTransaction().hide(active).show(firstFragment).commit()
               active = firstFragment

           
           R.id.navigation_second -> 
               fm.beginTransaction().hide(active).show(secondFragment).commit()
               active = secondFragment
           
           R.id.navigation_third -> 
               fm.beginTransaction().hide(active).show(thirdFragment).commit()
               active = thirdFragment
           
       
        true
   

并在您的 nav_host_fragment 中删除这些行:

app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"

【讨论】:

你能举个例子说明如何使用 PostDelay 吗? 有一个例子here。但我认为你应该使用第二种选择。 但第二个选项正是我在主要活动中所做的 感谢 3 - 6 小时后,您的解决方案是唯一有效的解决方案【参考方案2】:

您可以使用喷气背包导航进行简单的底栏导航

使用 Jetpack 导航的简单底部导航:

让我们首先通过在应用的 build.gradle 文件中添加以下行来将 Jetpack Navigation 库包含在您的应用中:

def nav_version = "2.1.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

我们首先创建一个简单的底部导航流程。为此,您需要首先在单个活动布局文件中添加 NavHostFragment。将此添加到 FrameLayout 标签内的 activity_main.xml 文件中。

<fragment
  android:id="@+id/fragNavHost"
  android:name="androidx.navigation.fragment.NavHostFragment"
  android:layout_
  android:layout_
  app:defaultNavHost="true"
  app:navGraph="@navigation/bottom_nav_graph" />

您将看到一条错误消息“无法解析符号@navigation/bottom_nav_graph。”

    <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/bottom_nav_graph.xml"
    app:startDestination="@id/homeFragment2">

    <fragment
        android:id="@+id/homeFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/searchFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.SearchFragment"
        android:label="fragment_search"
        tools:layout="@layout/fragment_search" />

    <fragment
        android:id="@+id/notificationsFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.NotificationsFragment"
        android:label="fragment_notifications"
        tools:layout="@layout/fragment_notifications" />

    <fragment
        android:id="@+id/profileFragment2"
        android:name="com.wajahatkarim3.bottomnavigationdemo.ProfileFragment"
        android:label="fragment_profile"
        tools:layout="@layout/fragment_profile" />
</navigation>

是时候在我们的活动类中添加一些代码了。打开 MainActivity.kt 文件,并在其中创建一个方法 setupViews()。在活动的 onCreate() 中调用它。在 setupVeiws() 方法中添加这些行。

    fun setupViews()

    // Finding the Navigation Controller
    var navController = findNavController(R.id.fragNavHost)

    // Setting Navigation Controller with the BottomNavigationView
    bottomNavView.setupWithNavController(navController)

    // Setting Up ActionBar with Navigation Controller
    // Pass the IDs of top-level destinations in AppBarConfiguration
    var appBarConfiguration = AppBarConfiguration(
        topLevelDestinationIds = setOf (
            R.id.homeFragment,
            R.id.searchFragment,
            R.id.notificationsFragment,
            R.id.profileFragment
        )
    )
    setupActionBarWithNavController(navController, appBarConfiguration)

【讨论】:

这并不能真正解决问题【参考方案3】:

当我尝试@Kasım Özdemir 的回答时,每次启动活动时,底部导航视图中的第一项都会产生初始涟漪效果。(因为我使用的是材质底部导航视图,它具有默认涟漪效果。当我第一次点击时,UI 也不可见,但我认为这是因为我使用与 @Kasım Özdemir 不同的方法来更改我在单击项目时的活动片段。

我不想从第一个项目开始我的活动,而是在导航视图中使用中间项目,在下面的例子中是“FragmentTwo”。所以涟漪效应是无关紧要的。

所以,我只是附加片段而不是添加然后隐藏它,现在没有涟漪,这是如何代码在 Kotlin 中看起来...

    val firstFragment: Fragment = FragmentOne()
    val middleFragment: Fragment = FragmentTwo()
    val thirdFragment: Fragment = FragmentThree()
    var fragment: Fragment? = null
    var bnv: BottomNavigationView? = null

    bnv = findViewById(R.id.bottom_navigation)
    bnv!!.selectedItemId = R.id.middle_page

  //fragments attached other than the fragment linked with middle item    
    supportFragmentManager.beginTransaction().attach(firstFragment).commit()
    supportFragmentManager.beginTransaction().attach(thirdFragment).commit()

    supportFragmentManager.beginTransaction().add(R.id.activity_main_container, middleFragment())
        .commit()
 bnv!!.setOnItemSelectedListener  item ->

            when (item.itemId)     
                R.id.first_page -> fragment = firstFragment
                R.id.middle_page -> fragment = middleFragment
                R.id.third_page -> fragment = thirdFragment    
            
    
            if (item.isChecked)
                false
            

            else 
                supportFragmentManager.beginTransaction()
                    .setCustomAnimations(R.anim.fade_in, R.anim.fade_out).replace(
                        R.id.activity_main_container,
                        fragment!!
                    ).commit()
                true
            
        

因此,使用附加也可以正常工作,而不是添加然后隐藏它...有关更多信息,您可以check this answer

(如果您还没有,请尝试使用导航组件进行底部导航,like this。在某些情况下更方便,因为它自己处理默认的 backStack 管理。)

【讨论】:

请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。

以上是关于在 BottomNavigationView 中切换选项卡时出现延迟的主要内容,如果未能解决你的问题,请参考以下文章

Android - 从方形位图中切出一个圆圈

从背景中切出的透明文本

从背景中切出的透明文本

Python:如何从图像中切出具有特定颜色的区域(OpenCV,Numpy)

MATLAB / Octave:从图像中切出很多圆圈

如何在 BottomNavigationView 的片段上打开搜索界面?