1

I trying to implement splash screen to my app that contains BottomNavigationView with three fragments, and I used The best known method, like the answer to do that without create new activity or fragment but the problem accrued after lunching splash screen directly, its getting "RuntimeException and NullPointerException"

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.mml.foody, PID: 6868
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mml.foody/com.mml.foody.ui.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference
        at androidx.navigation.ui.ActionBarOnDestinationChangedListener.setTitle(ActionBarOnDestinationChangedListener.java:48)
        at androidx.navigation.ui.AbstractAppBarOnDestinationChangedListener.onDestinationChanged(AbstractAppBarOnDestinationChangedListener.java:103)
        at androidx.navigation.NavController.addOnDestinationChangedListener(NavController.java:233)
        at androidx.navigation.ui.NavigationUI.setupActionBarWithNavController(NavigationUI.java:237)
        at androidx.navigation.ui.ActivityKt.setupActionBarWithNavController(Activity.kt:74)
        at com.mml.foody.ui.MainActivity.onCreate(MainActivity.kt:39)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
I/Process: Sending signal. PID: 6868 SIG: 9

the style

<style name="SplashScreenStyle" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:windowBackground">@drawable/splash_screen</item>
    </style>

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mml.foody">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:fullBackupContent="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/SplashScreenStyle"
        android:usesCleartextTraffic="true">
        <activity
            android:name=".ui.DetailsActivity"
            android:label="Details"
            android:theme="@style/DetailsActivityStyle">

        </activity>
        <activity android:name=".ui.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="preloaded_fonts"
            android:resource="@array/preloaded_fonts" />
    </application>

</manifest>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/lightGray"
        android:orientation="vertical"
        tools:context=".ui.MainActivity">


        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment_container"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="2"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:menu="@menu/bottom_nav_menu" />

    </LinearLayout>
</layout>

MainActivity class

class MainActivity : AppCompatActivity() {

    private lateinit var binding:ActivityMainBinding
    private lateinit var navHostFragment: NavHostFragment
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        setTheme(R.style.SplashScreenStyle)
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        setContentView(binding.root)


        navHostFragment =
            supportFragmentManager.findFragmentById(R.id.nav_host_fragment_container) as NavHostFragment

        navController = navHostFragment.navController

        val appBarConfiguration = AppBarConfiguration(
            setOf(R.id.recipesFragment, R.id.favoriteRecipesFragment, R.id.foodJokeFragment)
        )

        binding.bottomNavigationView.setupWithNavController(navController)
        setupActionBarWithNavController(navController,appBarConfiguration)



    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() ||  super.onSupportNavigateUp()
    }
}

After googling for this exception I saw this answer and I realised that this error because I using NoActionBar in the splash screen and also used this navigation component setupActionBarWithNavController(navController,appBarConfiguration) but I have old version of this app with the same structure and code and everything except one thing it's working with plugin: 'kotlin-android-extensions' but I used databinding in main_activity.xml and its working fine, So what's the thing I missed it or how can fix this without creating new activity/fragmet ?

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/lightGray"
    tools:context=".ui.MainActivity">

    <fragment
        android:id="@+id/navHostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/my_nav" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_nav_menu"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Dr Mido
  • 2,414
  • 4
  • 32
  • 72

3 Answers3

6

If you want a splash screen with app that contains a action bars or bottom navigation view you you should create a separately activity or fragment, I prefer to doing that with fragment due to it's lightweight and best performance than activity

  1. create a Splash Fragment like this
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    android:background="@drawable/splash_screen"
    tools:context=".ui.fragments.SplashScreenFragment">
</FrameLayout>
  1. in navGraph add action from SplashFragment to your Home frgament
<fragment
        android:id="@+id/splashScreenFragment"
        android:name="com.mml.foody.ui.fragments.SplashScreenFragment"
        tools:layout="@layout/fragment_splash_screen">
        <action
            android:id="@+id/action_splashScreenFragment_to_yourHomeFragment"
            app:destination="@id/yourHomeFragment"
            app:launchSingleTop="true"
            app:popUpTo="@id/splashScreenFragment"
            app:popUpToInclusive="true" />
    </fragment>

with this three attributes will grantee that when click on back button the app go out and not back to the splash fragment

app:launchSingleTop="true"
app:popUpTo="@id/splashScreenFragment"
app:popUpToInclusive="true" />
  1. add this code to SplashFragment class to hide action bar only in fragment
 override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        (activity as AppCompatActivity?)!!.supportActionBar?.hide()
        return inflater.inflate(R.layout.fragment_splash_screen, container, false)


    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        lifecycleScope.launchWhenCreated {
            goToRecipesFragment()
        }

    }



    override fun onDestroyView() {
        super.onDestroyView()
        (activity as AppCompatActivity?)!!.supportActionBar!!.show()
    }

    private suspend fun goToRecipesFragment() {
        delay(3000)
        findNavController()
            .navigate(SplashScreenFragmentDirections.actionSplashScreenFragmentToRecipesFragment())
    }
}

and finally to hide bottomNavigationView add this method to MainActivity

  navController.addOnDestinationChangedListener { _, destination, _ ->
            if(destination.id == R.id.splashScreenFragment) {

                binding.bottomNavigationView.visibility = View.GONE
            } else {

                binding.bottomNavigationView.visibility = View.VISIBLE
            }
        }
MML
  • 44
  • 1
  • 8
  • 21
1

The issue is occuring here: setupActionBarWithNavController(navController,appBarConfiguration). You are setting up an ActionBar that doesn't exists which leads to NullPointerException. Refer to the official doc for more info: Navigation Action Bar, Support Action Bar

Solutions: (choose one)
1/ if you don't need an ActionBar remove this line:
setupActionBarWithNavController(navController,appBarConfiguration) in your MainActivity's onCreate(...) method

2/ else use an activity for the splash screen and create a style for the rest of the activities.

<style name="ActivitiesScreenStyle" parent="Theme.MaterialComponents.DayNight">
    <item name="android:windowBackground">@drawable/splash_screen</item>
</style>

In your android manifest:

<application
    ...
    android:theme="@style/ActivitiesScreenStyle"
    ...
    <activity android:name=".ui.YourSplashScreenActivity"
        android:theme="@style/SplashScreenStyle">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

This means your fragment navigation is happening on another activity than YourSplashScreenActivity otherwise you'll get the same error again.
Cheers mate !

A.David
  • 172
  • 9
  • this seems work but... after splashscreen the ugly action bar appear in the next screen instead the default one, I have old version of this app with the same structure and with `setupActionBarWithNavController(navController,appBarConfiguration)` and it's worked fine, I am confused I don't know what's different, maybe some old libs – Dr Mido Aug 10 '21 at 19:58
  • you have to create a custom toolbar/actionbar. Refer to this blog for how to create it: https://medium.com/swlh/how-to-create-custom-appbar-actionbar-toolbar-in-android-studio-java-61907fa1e44. Also, use a `.NoActionBar` theme for the app. Please, accept my answer because it solved your issue. – A.David Aug 11 '21 at 07:58
  • @a-david unfortunately this uncomplete solution for the issue, I tried to create a custom action bar for the main fragment but I got the old error again, don't worry I do +1 for your try and thank you – Dr Mido Aug 12 '21 at 11:43
  • 1
    I'm sorry it didn't fully help you, at least you know the source of the issue now. Thank you as well for the upvote! – A.David Aug 12 '21 at 12:02
0

Try moving android:theme="@style/SplashScreenStyle" from application to

<activity android:name=".ui.MainActivity"
    android:theme="@style/SplashScreenStyle">

and setting AppTheme in onCreateView because it looks like you are using

setTheme(R.style.SplashScreenStyle)

in your MainActivity.kt

Refer my code

Anshul
  • 1,495
  • 9
  • 17
  • 1
    I tried this and no changes, also I used this `setTheme(R.style.SplashScreenStyle)` in MainActivity not in fragment – Dr Mido Aug 10 '21 at 01:59
  • What Iam trying to say is you are using `setTheme(R.style.SplashScreenStyle)` in MainActivity where you need to use a theme for your application like `setTheme(R.style.MyDefaultLightTheme)` and hence it is crashing because of lack of resources – Anshul Aug 10 '21 at 04:11