Search code examples
androidandroid-fragmentsfragmentandroid-toolbarandroid-navigation-graph

Setting up a toolbar with NavigationUI


I am trying to set up a toolbar for my simple Navigation UI which has neither bottom navigation nor drawer.

Here is the code for my Main Activity layout.

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorTopNavigation"
        android:theme="@style/Theme.AppCompat.Light"
        app:title="Testing"
        />

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

</androidx.appcompat.widget.LinearLayoutCompat>

Code for MainActivity

class MainActivity :  AppCompatActivity(),
    ReaderFragment.OnFragmentInteractionListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar = findViewById(R.id.toolbar) as Toolbar

        setSupportActionBar(toolbar)
        val navHostFragment: NavHostFragment = nav_host_fragment as NavHostFragment
        NavigationUI.setupWithNavController(toolbar, navHostFragment.navController)

    }

    override fun onAttachFragment(fragment: Fragment) {
        super.onAttachFragment(fragment)
        if (fragment is ReaderFragment) {
        } else if (fragment is ChapterDetailsFragment) {
        }

    }

    override fun onFragmentInteraction(uri: String) {
        Toast.makeText(this, uri, Toast.LENGTH_SHORT).show()
    }
}

Navigation graph is

<?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/nav_graph"
    app:startDestination="@id/reader_fragment">
    <fragment
        android:id="@+id/reader_fragment"
        android:name="com.example.quran.ReaderFragment"
        android:label="Reader"
        tools:layout="@layout/reader_fragment">
        <action
            android:id="@+id/action_chapters_to_chapterDetails"
            app:destination="@id/chapterDetailsFragment" />
    </fragment>
    <fragment
        android:id="@+id/chapterDetailsFragment"
        android:name="com.example.quran.ChapterDetailsFragment"
        android:label="fragment_chapter_details"
        tools:layout="@layout/fragment_chapter_details" />
</navigation>

and reader fragment is

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Activities that contain this fragment must implement the
 * [ReaderFragment.OnFragmentInteractionListener] interface
 * to handle interaction events.
 * Use the [ReaderFragment.newInstance] factory method to
 * create an instance of this fragment.
 */

class ReaderFragment : Fragment(), View.OnClickListener, QuranElementClickListener {

    private lateinit var quranElementsListView: RecyclerView
    private lateinit var layoutManager: LinearLayoutManager
    private lateinit var adapter: QuranTextAdapter


    private var param1: String? = null
    private var param2: String? = null
    private var interactionListener: OnFragmentInteractionListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.reader_fragment, container, false)


        val dbAccess = DatabaseAccess.getInstance(context)
        dbAccess.open()
        val quranElements = dbAccess.allElements

        quranElementsListView = view.findViewById(R.id.quranElementsListView)
        layoutManager = LinearLayoutManager(context)
        quranElementsListView.layoutManager = layoutManager

        adapter = QuranTextAdapter(quranElements, this)
        quranElementsListView.adapter = adapter
        return view
    }

    override fun onQuranElementClick(quranElement: QuranElement, position: Int) {
        print(quranElement.arabicText)
        findNavController().navigate(R.id.action_chapters_to_chapterDetails)
    }

    override fun onClick(v: View?) {
        print("This view is clicked")
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is OnFragmentInteractionListener) {
            interactionListener = context
        } else {
            throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener")
        }
    }

    override fun onDetach() {
        super.onDetach()
        interactionListener = null
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     *
     *
     * See the Android Training lesson [Communicating with Other Fragments]
     * (http://developer.android.com/training/basics/fragments/communicating.html)
     * for more information.
     */
    interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        fun onFragmentInteraction(uri: String)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment ReaderFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            ReaderFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

I have already set the a customized toolbar by setting

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorTopNavigation</item>
        <item name="colorPrimaryDark">@color/colorTopNavigation</item>
        <item name="colorAccent">@color/colorAccent</item>

    </style>

Now if I run the project it will show only the toolbar with Reader text and Reader fragment is not loaded and nothing is shown in the recycler view.

enter image description here

but if I just comment the toolbar in the activity layout file and change the onCreate method in activity to following

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//        val toolbar = findViewById(R.id.toolbar) as Toolbar
//        setSupportActionBar(toolbar)
//        val navHostFragment: NavHostFragment = nav_host_fragment as NavHostFragment
//        NavigationUI.setupWithNavController(toolbar, navHostFragment.navController)


        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .add(ReaderFragment(), ReaderFragment::javaClass.toString())
                .commit()
        }
    }

then recycler view is visible like this

enter image description here

But i wanted both the recycler view and the toolbar and i also wanted to customize the toolbar a little bit like custom font. Can someone guide me on that.

Thanks


Solution

  • Add android:orientation="vertical" to your main activity like below:

    <?xml version="1.0" encoding="utf-8"?>  
    <androidx.appcompat.widget.LinearLayoutCompat 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:orientation="vertical"  
    tools:context=".MainActivity">  
    <androidx.appcompat.widget.Toolbar   
    android:id="@+id/toolbar"   
    android:layout_width="match_parent"  
    android:layout_height="?attr/actionBarSize"  
    android:background="@color/colorTopNavigation"  
    android:theme="@style/Theme.AppCompat.Light"   
    app:title="Testing" />   
    <fragment android:id="@+id/nav_host_fragment"  
    android:name="androidx.navigation.fragment.NavHostFragment"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    app:defaultNavHost="true"  
    app:navGraph="@navigation/nav_graph" />  
    </androidx.appcompat.widget.LinearLayoutCompat