I try to combine drawer layout with viewapger2 and Navhostfragment. When I clicked the drawer layout icon, NavigationView (that include my menu and header) is opened. whenever ı try to select one item from drawer layout , ı cant select because drawer layout is automatically close. How can ı fix this problem.
this is my activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.navigation.NavigationView
android:id="#+id/navView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header"
app:menu="#menu/nav_menu" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/my_nav" />
</androidx.drawerlayout.widget.DrawerLayout>
</layout>
fragment_view_pager.xml
<?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="#drawable/beach4"
android:id="#+id/cons"
tools:context=".view.ViewPagerFragment">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="75dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#00ffffff"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/cons"
app:tabBackground="#drawable/tab_pager_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabPaddingEnd="#dimen/tab_padding_end"
app:tabPaddingStart="#dimen/tab_padding_start"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
NavHeader.xml
<?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"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#B3F6FF">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mustafa"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
nav_menu
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/firstItem"
android:title="First Item"/>
<item
android:id="#+id/secondItem"
android:title="Second Item"/>
<item
android:id="#+id/thirdItem"
android:title="Third Item"/>
MainActivity.kt
class MainActivity : AppCompatActivity() {
companion object {
var globalList = ArrayList<Float>()
var globalVar3=1
var globalVar = 1
var globalVar2 = 1
lateinit var sharedPreferences: SharedPreferences
lateinit var toggle: ActionBarDrawerToggle
lateinit var navVieww: NavigationView
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sharedPreferences =
this.getSharedPreferences("com.mustafa.horizontalrecycler", Context.MODE_PRIVATE)
toggle = ActionBarDrawerToggle(
this#MainActivity,
drawer_layout,
R.string.Open,
R.string.Close
)
drawer_layout.addDrawerListener(toggle)
toggle.syncState()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val navView = findViewById<NavigationView>(R.id.navView)
navVieww = navView
navView.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.firstItem -> {
Toast.makeText(applicationContext, "First Item Selected", Toast.LENGTH_LONG)
.show()
println("First Item")
}
R.id.secondItem -> {
Toast.makeText(applicationContext, "Second Item Selected", Toast.LENGTH_LONG)
.show()
println("Second Item")
}
R.id.thirdItem -> {
Toast.makeText(applicationContext, "Third Item Selected", Toast.LENGTH_LONG)
.show()
}
}
true
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (toggle.onOptionsItemSelected(item)) {
globalVar3= globalVar3+1
return true
}
return super.onOptionsItemSelected(item)
}
}
viewPagerFragment.kt
class ViewPagerFragment : Fragment() {
private val args by navArgs<ViewPagerFragmentArgs>()
private lateinit var wordViewModel: WordViewModel
var number = 1
private lateinit var tabLayout: TabLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println("VieewPagerOncreated")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_view_pager, container, false)
wordViewModel = ViewModelProvider(this).get(WordViewModel::class.java)
wordViewModel.readAllData.observe(viewLifecycleOwner, Observer {
//println(it.size)
})
number = sharedPreferences.getInt("pageNumber", 1)
println(number)
val tabLayout = view.findViewById<TabLayout>(R.id.tab_layout)
val viewPager = view.findViewById<ViewPager2>(R.id.view_pager)
val fragments1: ArrayList<Fragment> = arrayListOf(
WeatherListFragment(),
Location1Fragment(),
Location2Fragment(),
Location3Fragment()
)
val fragments2: ArrayList<Fragment> = arrayListOf(
LocationAddFragment()
)
//(i in number..x) x sayısı locationfragment sayısından bir az olcak
//suan için istediğimiz gibi oldu
for (i in number..2) {
fragments1.removeLast()
}
//fragments1.add(fragments2.get(0))
val adapter =
ViewPagerAdapter(fragments1, requireActivity().supportFragmentManager, lifecycle)
viewPager.adapter = adapter
if (MainActivity.globalVar > 1) {
viewPager.setCurrentItem(args.currentPage, false)
}
TabLayoutMediator(tabLayout, viewPager)
{ tab, position -> }.attach()
// bu kod ile başlıgın ismini değiştirebiliriz
(activity as AppCompatActivity).supportActionBar?.title = "Ingredient Display"
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
(activity as AppCompatActivity).navView.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.firstItem -> {
println("viewpagerfragment")
Toast.makeText(requireContext(), "First Item Selected", Toast.LENGTH_LONG)
.show()
println("First Item")
}
R.id.secondItem -> {
Toast.makeText(requireContext(), "Second Item Selected", Toast.LENGTH_LONG)
.show()
println("Second Item")
}
R.id.thirdItem -> {
Toast.makeText(requireContext(), "Third Item Selected", Toast.LENGTH_LONG)
.show()
}
}
true
}
return view
}
}
Related
this is MainActivity
class MainActivity : AppCompatActivity(), FragmentDrawerListener,
BottomNavigationView.OnNavigationItemSelectedListener {
private lateinit var drawerFragment: DrawerFragment
#Inject
lateinit var mainActivityViewModel: MainActivityViewModel
#Inject
lateinit var appPreference: AppPreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
navigation.setOnNavigationItemSelectedListener(this)
displayView()
}
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onDrawerItemSelected(view: View, position: Int) {
displayView(position)
}
private fun displayView() {
drawerFragment =
supportFragmentManager.findFragmentById(R.id.fragment_navigation_drawer) as DrawerFragment
drawerFragment.init(R.id.fragment_navigation_drawer, drawer_layout, toolbar)
displayView(0)
}
private fun displayView(position: Int) {
when (position) {
0 -> {
HomeFragment()
}
5 -> {
val intent = Intent(this, MerchantOnBoardingActivity::class.java)
startActivity(intent)
}
9 -> {
val intent = Intent(this, AppSecurityActivity::class.java)
startActivity(intent)
}
else -> {
val bundle = Bundle()
bundle.putInt("menuItemPosition", position - 1)
val intent = Intent(this, MenuActivity::class.java)
intent.putExtras(bundle)
startActivity(intent)
}
}
}
private fun showDefaultPage() {
val fragment = HomeFragment()
title = "Home"
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.main_content, fragment)
fragmentTransaction.commit()
supportActionBar?.title = title
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
var fragment: Fragment? = null
when (item.itemId) {
R.id.navigation_home -> fragment = HomeFragment()
R.id.navigation_monitor -> Toast.makeText(this, "Monitor", Toast.LENGTH_LONG).show()
R.id.navigation_profile -> Toast.makeText(this, "Profile", Toast.LENGTH_LONG).show()
}
return loadFragment(fragment)
}
private fun loadFragment(fragment: Fragment?): Boolean {
//switching fragment
if (fragment != null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()
return true
}
return false
}
}
This is my xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:local="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
local:popupTheme="#style/ThemeOverlay.AppCompat.Light"
local:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<FrameLayout
android:id="#+id/main_content"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemIconTint="#color/design_default_color_primary"
app:itemTextColor="#color/design_default_color_primary"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/navigation" />
</LinearLayout>
<fragment
android:id="#+id/fragment_navigation_drawer"
android:name="com.egpaid.employeeapp.home.homedashboard.DrawerFragment"
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_gravity="start"
app:layout="#layout/fragment_drawer"
tools:layout="#layout/fragment_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
</RelativeLayout>
using this code i am able to display data but when i try to click on Home button on either from bottom navigation view or from drawber then i am getting
No view found for id 0x7f0900a3 (com.egpaid.employeeapp:id/fragment_container) for fragment HomeFragment{9d6bf5} (7b531975-d1d4-4ad4-b3c7-6f43ae436c6d) id=0x7f0900a3}
I don't know what i am missing i am trying to create custom navigation drawber with bottom navigation view please help me Thanks
I have got problems.
There are 5 fragment in MainActivity. It is D1, D2, D3, D4, D5 Fragment. And there is BottomNavigationView in MainActivity. I want to call DxFragment from D1fragment. The button will be clicked and only the frame will change. But the button navigation bar will not change.
I've been looking for a while. I couldn't find the solution. Can anyone please help?
MAINACTIVITY
class MainActivity : AppCompatActivity() {
private lateinit var bottomNavigationView : BottomNavigationView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bottomNavigationView = findViewById(R.id.nav_view)
navigateFragment(false)
}
public fun loadFragment (fragment: Fragment){
supportFragmentManager.beginTransaction().also { fragmentTransaction ->
fragmentTransaction.replace(R.id.bottom_nav_host_fragment, fragment)
fragmentTransaction.commit()
}
}
public fun navigateFragment ( stateFragment : Boolean) {
bottomNavigationView = findViewById(R.id.nav_view)
bottomNavigationView.setOnNavigationItemSelectedListener { menuItem : MenuItem ->
when{
menuItem.itemId== R.id.navigation_home -> {
loadFragment(D1Fragment())
return#setOnNavigationItemSelectedListener true
}
menuItem.itemId == R.id.navigation_map -> {
loadFragment(D2Fragment())
return#setOnNavigationItemSelectedListener true
}
menuItem.itemId == R.id.navigation_userpage -> {
loadFragment(D3Fragment())
return#setOnNavigationItemSelectedListener true
}
menuItem.itemId == R.id.navigation_fav -> {
loadFragment(D4Fragment())
return#setOnNavigationItemSelectedListener true
}
menuItem.itemId == R.id.navigation_list -> {
loadFragment(D5Fragment())
return#setOnNavigationItemSelectedListener true
}
stateFragment == true -> {
loadFragment(BlankFragment())
return#setOnNavigationItemSelectedListener true
}
else -> {
return#setOnNavigationItemSelectedListener true
}
}
}
}
override fun onSupportNavigateUp(): Boolean {
return Navigation.findNavController(this, R.id.bottom_nav_host_fragment).navigateUp() ||
super.onSupportNavigateUp()
}
}
ACTIVITY_MAIN.XML
some part
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_menu" />
<FrameLayout
android:id="#+id/bottom_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
FRAGMENT_D1.XML
<?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"
tools:context=".D1Fragment">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLICK AND GO BLANKFRAGMENT" />
</FrameLayout>
D1FRAGMENT
class D1Fragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_d1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
button.setOnClickListener {
Toast.makeText(context , "CLICKED" , Toast.LENGTH_SHORT ).show()
Navigation.findNavController(view).navigate(R.id.action_d1Fragment_to_blankFragment)
}
super.onViewCreated(view, savedInstanceState)
}
}
BUTTON CLICK AND ERROR
java.lang.IllegalStateException: View android.widget.FrameLayout{16424ce5 V.E..... ........ 0,0-1080,1365} does not have a NavController set
at androidx.navigation.Navigation.findNavController(Navigation.java:84)
at com.axisting.bottomelledeneme.D1Fragment$onViewCreated$1.onClick(D1Fragment.kt:35)
You only needs to have a principal class,
Example main's activity, it contains the BottomNavigationView and fragment. the fragments have the other element
Example
<?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"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/bottom_nav_menu" />
</androidx.con straintlayout.widget.ConstraintLayout>
You only need to attach the fragment
I have a single activity with many fragments (Using jetpack navigation). On my first fragment, i have a recyclerview. If i scroll on the first fragment and then navigate to the other fragment, the fragment retains the scroll position and i don't want that. An example is as follows:
i.e. Suppose i have two fragments A and B, When my app starts it starts on A. Suppose i start scrolling on A and then navigate to B. My app retains the scroll position on B which is not what i want. I want fragment B to start on top. And then when it returns to fragment A, i want it to retain the scroll position it previously scrolled.
Fragment A.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>
<import type="android.view.View" />
<variable
name="ViewModel"
type="....AccountViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/Layout_Fragment_Account"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
Recyclerview
-->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView_Account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.accountListVisibility? View.VISIBLE : View.GONE}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<!--
Empty Views and group
-->
<androidx.constraintlayout.widget.Group
android:id="#+id/Empty_View"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.accountEmptyViewVisibility?
View.VISIBLE : View.GONE}"
app:constraint_referenced_ids="Empty_View_Illustration,Empty_View_Title,Empty_View_Subtitle" />
<ImageView
android:id="#+id/Empty_View_Illustration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="#+id/Empty_View_Title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="#drawable/il_account" />
<TextView
android:id="#+id/Empty_View_Title"
style="#style/Locky.Text.Title6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="#string/text_title_emptyView_accounts"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="#id/Empty_View_Subtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Empty_View_Illustration"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent=".8" />
<TextView
android:id="#+id/Empty_View_Subtitle"
style="#style/Locky.Text.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="?attr/actionBarSize"
android:text="#string/text_subtitle_emptyView_accounts"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Empty_View_Title"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent=".8" />
<!--
Progress Bar
-->
<include
android:id="#+id/Progress_Bar"
layout="#layout/custom_view_list_loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.loadingStatus? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Fragment A.kt:
class AccountFragment : Fragment() {
private var _binding: FragmentAccountBinding? = null
private var _viewModel: AccountViewModel? = null
private var _lastClickTime: Long = 0
private val binding get() = _binding!!
private val viewModel get() = _viewModel!!
companion object {
const val TAG = "ACCOUNT_FRAGMENT_DEBUG"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentAccountBinding.inflate(inflater, container, false)
// Fetch view model
_viewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
//Bind view model to layout
binding.viewModel = _viewModel
// Bind lifecycle owner
binding.lifecycleOwner = this
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
/* Hides the soft keyboard */
hideSoftKeyboard(binding.root)
/* Observe snack bar events */
observeSnackBarEvent()
/* Observe the account list changes */
observeAccounts()
/* Observe back stack entry result after navigating from sort sheet */
observeBackStackEntryForSortSheet()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_toolbar_filter, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.Toolbar_Filter -> {
navigateToSort()
true
}
else -> false
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
/*
* My Functions
*/
private fun observeBackStackEntryForSortSheet() {
val navController = findNavController()
// After a configuration change or process death, the currentBackStackEntry
// points to the dialog destination, so you must use getBackStackEntry()
// with the specific ID of your destination to ensure we always
// get the right NavBackStackEntry
val navBackStackEntry = navController.getBackStackEntry(R.id.Fragment_Account)
// Create our observer and add it to the NavBackStackEntry's lifecycle
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME
&& navBackStackEntry.savedStateHandle.contains(KEY_ACCOUNTS_SORT)
) {
viewModel.sortChange(
navBackStackEntry.savedStateHandle.get<AccountSort>(
KEY_ACCOUNTS_SORT
)!!
)
navBackStackEntry.savedStateHandle.remove<AccountSort>(KEY_ACCOUNTS_SORT)
}
}
navBackStackEntry.lifecycle.addObserver(observer)
// As addObserver() does not automatically remove the observer, we
// call removeObserver() manually when the view lifecycle is destroyed
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
navBackStackEntry.lifecycle.removeObserver(observer)
}
})
}
private fun observeSnackBarEvent() {
viewModel.showSnackBarEvent.observe(viewLifecycleOwner, Observer {
if (it != null) {
snackBarAction(it)
}
})
}
private fun observeAccounts() {
with(viewModel) {
accounts.observe(viewLifecycleOwner, Observer {
if (it != null) {
//set loading flag to hide loading animation
doneLoading()
//Alternate visibility for account list and empty view
alternateAccountListVisibility(it.size)
//Submit the cards
initiateAccounts().submitList(it)
}
})
}
}
private fun initiateAccounts(): AccountAdapter {
val adapter = AccountAdapter(
AccountClickListener {
navigateToSelectedAccount(it)
},
AccountOptionsClickListener { view, card ->
view.apply {
isEnabled = false
}
createPopupMenu(view, card)
})
binding.RecyclerViewAccount.apply {
this.adapter = adapter
setHasFixedSize(true)
}
return adapter
}
private fun createPopupMenu(view: View, account: Account) {
requireContext().createPopUpMenu(
view,
R.menu.menu_moreoptions_account,
PopupMenu.OnMenuItemClickListener {
when (it.itemId) {
R.id.Menu_CopyUsername -> copyToClipboardAndToast(account.username)
R.id.Menu_CopyPass -> copyToClipboardAndToast(account.password)
R.id.Menu_ShowPass -> triggerSnackBarEvent(account.password)
else -> false
}
}, PopupMenu.OnDismissListener {
view.apply {
isEnabled = true
}
})
}
private fun navigateToSort() {
if (SystemClock.elapsedRealtime() - _lastClickTime >= 800) {
_lastClickTime = SystemClock.elapsedRealtime()
navigateTo(AccountFragmentDirections.actionFragmentAccountToBottomSheetFragmentAccountFilter())
}
}
private fun navigateToSelectedAccount(account: Account) {
navigateTo(
AccountFragmentDirections.actionFragmentAccountToFragmentViewAccount(
account
)
)
}
private fun snackBarAction(message: String) {
binding.LayoutFragmentAccount.snackbar(message) {
action(getString(R.string.button_snack_action_close)) { dismiss() }
}
viewModel.doneShowingSnackBar()
}
private fun triggerSnackBarEvent(message: String): Boolean {
viewModel.setSnackBarMessage(message)
return true
}
private fun copyToClipboardAndToast(message: String): Boolean {
copyToClipboard(message)
toast(getString(R.string.message_copy_successful))
return true
}
Fragment B.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>
<variable
name="Account"
type="....Account" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/Layout_Credential_View"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/Account_Logo"
imageUrl="#{Account.logoUrl}"
loadingResource="#{#drawable/ic_image_loading}"
errorResource="#{#drawable/ic_account_placeholder}"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="#drawable/ic_account_placeholder" />
<TextView
android:id="#+id/Account_Name"
style="#style/Locky.Text.Title5.Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:textAlignment="center"
android:text="#{Account.accountName}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Account_Logo"
tools:text="This can be a very very very long title toooooo" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView_Credentials_Field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="8dp"
android:nestedScrollingEnabled="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Account_Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Fragment B.kt
class ViewAccountFragment : Fragment() {
private var _binding: FragmentViewAccountBinding? = null
private var _viewModel: ViewAccountViewModel? = null
private lateinit var _account: Account
private val binding get() = _binding!!
private val viewModel get() = _viewModel!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Fetch the layout and do the binding
_binding = FragmentViewAccountBinding.inflate(inflater, container, false)
//Instantiate view model
_viewModel = ViewModelProvider(this).get(ViewAccountViewModel::class.java)
binding.lifecycleOwner = this
//Fetch the account clicked on the previous screen
_account = ViewAccountFragmentArgs.fromBundle(requireArguments()).parcelcredaccount
with(_account) {
//Bind the account to the layout for displaying
binding.account = this
//Submit the account details to the recyclerview
initiateCredentialsFieldList().submitList(viewModel.fieldList(this))
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
/* Hides the soft keyboard */
hideSoftKeyboard(binding.root)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_credentials_actions, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.Action_Duplicate -> {
/*
* We set the account id to empty here
* When the add screen receives it, it wil perceive it as a new account that needs to be
* added to the database
*/
navigateToEditScreen(_account.copy(accountID = generateUniqueID()))
true
}
R.id.Action_Edit -> {
navigateToEditScreen(_account)
true
}
R.id.Action_Delete -> {
deleteConfirmationDialog(_account.accountName)
true
}
else -> false
}
}
private fun initiateCredentialsFieldList(): CredentialsViewAdapter {
val credentialsAdapter =
CredentialsViewAdapter(
CopyClickListener { data ->
copyToClipboardAndToast(data)
},
ViewClickListener { data ->
snackBarAction(data)
})
binding.RecyclerViewCredentialsField.apply {
adapter = credentialsAdapter
setHasFixedSize(true)
}
return credentialsAdapter
}
private fun deleteAndNavigateBackToAccountList() {
with(_account) {
viewModel.delete(accountID)
toast(getString(R.string.message_credentials_deleted, accountName))
findNavController().popBackStack()
}
}
navigation.xml
<fragment
android:id="#+id/Fragment_Account"
android:name="....AccountFragment"
android:label="Accounts"
tools:layout="#layout/fragment_account">
<action
android:id="#+id/action_Fragment_Account_to_Fragment_View_Account"
app:destination="#id/Fragment_View_Account" />
<action
android:id="#+id/action_Fragment_Account_to_BottomSheet_Fragment_Account_Filter"
app:destination="#id/BottomSheet_Fragment_Account_Filter" />
</fragment>
MainActivity.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">
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/Drawer_Main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.main.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/Layout_Coordinator_Main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/Toolbar_Main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorOnSurface"
android:outlineAmbientShadowColor="#color/colorShadowColor"
android:outlineSpotShadowColor="#color/colorShadowColor">
<TextView
android:id="#+id/Toolbar_Main_Title"
style="#style/Locky.Text.Title6.Toolbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/app_name" />
</com.google.android.material.appbar.MaterialToolbar>
<androidx.core.widget.NestedScrollView
android:id="#+id/Nested_Scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true">
<fragment
android:id="#+id/Navigation_Host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/navigation_drawer_main" />
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/FAB_Search"
style="#style/Locky.FloatingActionButton.Mini"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="85dp"
app:layout_anchor="#id/FAB_Add"
app:layout_anchorGravity="top|center_horizontal"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="#drawable/ic_search" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/FAB_Add"
style="#style/Locky.FloatingActionButton.Normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="#drawable/ic_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/Navigation_View"
style="#style/Locky.Widget.Custom.NavigationView"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:clipToPadding="false"
android:paddingStart="0dp"
android:paddingEnd="16dp"
app:headerLayout="#layout/drawer_header"
app:itemTextAppearance="#style/Locky.Text.Body.Drawer"
app:menu="#menu/menu_drawer_main" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var _binding: ActivityMainBinding
private lateinit var _viewModel: MainActivityViewModel
private lateinit var _appBarConfiguration: AppBarConfiguration
//Fragments that can navigate with the drawer
private val _navigationFragments = setOf(
R.id.Fragment_Card,
R.id.Fragment_Account,
R.id.Fragment_Device
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
_viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
_binding.lifecycleOwner = this
//Set the support action bar to the toolbar
setSupportActionBar(_binding.ToolbarMain)
//Remove the default actionbar title
supportActionBar?.setDisplayShowTitleEnabled(false)
/* Updates the app settings*/
updateAppSettings()
//Setup the navigation components
navigationUISetup()
//Load FABs
listenerForAddFab()
listenerForSearchFab()
//Scroll changes to adjust toolbar elevation accordingly
setUpNestedScrollChangeListener()
}
override fun onOptionsItemSelected(item: MenuItem) =
item.onNavDestinationSelected(findNavController(R.id.Navigation_Host)) || super.onOptionsItemSelected(
item
)
override fun onSupportNavigateUp() =
findNavController(R.id.Navigation_Host).navigateUp(_appBarConfiguration)
override fun finish() {
super.finish()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
private fun navigationUISetup() {
//Fetch the Nav Controller
val navController = findNavController(R.id.Navigation_Host)
//Setup the App Bar Configuration
_appBarConfiguration = AppBarConfiguration(_navigationFragments, _binding.DrawerMain)
//Use Navigation UI to setup the app bar config and navigation view
NavigationUI.setupActionBarWithNavController(this, navController, _appBarConfiguration)
NavigationUI.setupWithNavController(_binding.NavigationView, navController)
//Set the mini FABs with navigation to navigate to fragments accordingly.
Navigation.setViewNavController(_binding.FABAdd, navController)
Navigation.setViewNavController(_binding.FABSearch, navController)
//Add on change destination listener to navigation controller to handle fab visibility
navigationDestinationChangeListener_FAB(navController)
//Add on change destination listener to navigation controller to handle screen title visibility
navigationDestinationChangeListener_ToolbarTitle(navController)
}
private fun setUpNestedScrollChangeListener() =
_binding.NestedScroll.setOnScrollChangeListener { _, _, scrollY, _, _ ->
if (scrollY > 0) {
_binding.ToolbarMain.elevation = 12F
} else {
_binding.ToolbarMain.elevation = 0F
}
}
private fun navigationDestinationChangeListener_ToolbarTitle(navController: NavController) {
navController.addOnDestinationChangedListener { _, nd, _ ->
when (nd.id) {
R.id.Fragment_Account -> updateToolbar(getString(R.string.text_title_screen_accounts))
R.id.Fragment_Card -> updateToolbar(getString(R.string.text_title_screen_cards))
R.id.Fragment_Device -> updateToolbar(getString(R.string.text_title_screen_devices))
R.id.Fragment_Settings -> updateToolbar(getString(R.string.text_title_screen_settings))
R.id.Fragment_Profile -> updateToolbar(getString(R.string.text_title_screen_profile))
R.id.Fragment_About -> updateToolbar(getString(R.string.text_title_screen_about))
R.id.Fragment_Donate -> updateToolbar(getString(R.string.text_title_screen_donate))
else -> {
//Show the toolbar
updateToolbar(null)
}
}
}
}
private fun navigationDestinationChangeListener_FAB(navController: NavController) {
navController.addOnDestinationChangedListener { nc, nd, _ ->
when (nd.id) {
nc.graph.startDestination,
R.id.Fragment_Card,
R.id.Fragment_Device -> {
_binding.DrawerMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
//Show all the FABs
showFABs()
}
else -> {
_binding.DrawerMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
//Hide all the FABs
hideFABs()
}
}
}
}
private fun getFadeNavOptions(): NavOptions? {
return NavOptions.Builder()
.setEnterAnim(R.anim.anim_fade_in)
.setExitAnim(R.anim.anim_fade_out)
.build()
}
private fun hideFABs() {
_binding.FABSearch.hide()
_binding.FABAdd.hide()
}
private fun showFABs() {
_binding.FABSearch.show()
_binding.FABAdd.show()
showFABFromSlidingBehavior(_binding.FABSearch, _binding.FABSearch.isVisible)
showFABFromSlidingBehavior(_binding.FABAdd, _binding.FABAdd.isVisible)
}
private fun showFABFromSlidingBehavior(fab: FloatingActionButton, isVisible: Boolean) {
val layoutParams: ViewGroup.LayoutParams = fab.layoutParams
if (layoutParams is CoordinatorLayout.LayoutParams) {
val behavior = layoutParams.behavior
if (behavior is HideBottomViewOnScrollBehavior) {
if (isVisible) {
behavior.slideUp(fab)
} else {
behavior.slideDown(fab)
}
}
}
}
I have attached a gif to demontstrate the issue here:
In the GIF i navigate from 3 fragments (Fragment A > Fragment B > Fragment C)
Is there anything i am doing wrong here ?
you have the same layoutmanager for both fragments, when you populate your different fragments; the same layoutmanager is called. Which then tries to restore the same position thinking its the same recyclerview, which is kind of a feature when you think about it.
from the docs:
Called when the RecyclerView is ready to restore the state based on a
previous RecyclerView. Notice that this might happen after an actual
layout, based on how Adapter prefers to restore State. See
RecyclerView.Adapter.getStateRestorationPolicy()
which means what we need is not to restore the state which can be done by passing
PREVENT to RecyclerView.Adapter.StateRestorationPolicy
solution1: in your fragment B adapter just call adapter.stateRestorationPolicy = PREVENT
solution2: create a different layoutmanager for fragment B in case you want to restore position for something else.
EDIT :: QA :: how can i set the view to be on top (Near Status Bar) :
Well, since you are populating your fragments inside a NestedScrollView you should call NestedScrollView.scrollTo(0, 0); when you navigate to the required fragment probably by waiting on a callback from addOnDestinationChangedListener inside your MainActivity.kt
I have these activity, fragments, its viewmodels, and their adapter. I can already call the next fragment on click of a recyclerview item, but the new fragment overlays on the first fragment.
Refer to screenshot below:
Next screenshot is the old fragment view:
As for the mainactivity:
class MainActivity : AppCompatActivity(), RecyclerViewClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home,
R.id.navigation_messages,
R.id.navigation_notifications,
R.id.navigation_account
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.add(R.id.fragment_home, HomeFragment.newInstance(), "dormList")
.commit()
}
}
override fun onRecyclerViewItemClick(view: View, dorms: Dorms) {
val detailsFragment = dormDetailsFragment.newInstance(dorms)
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_home, detailsFragment, "Dorm Details")
.addToBackStack(null)
.commit()
}
}
HomeFragment:
class HomeFragment : Fragment(), RecyclerViewClickListener {
private lateinit var factory: HomeViewModelFactory
private lateinit var viewModel: HomeViewModel
private var callback : RecyclerViewClickListener? = null
companion object {
fun newInstance(): HomeFragment {
return HomeFragment()
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
if(context is RecyclerViewClickListener) callback = context
else throw ClassCastException("$context must implement Callback")
}
override fun onDetach() {
super.onDetach()
callback = null
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val api = DormsAPI()
val repository = DormRepository(api)
factory = HomeViewModelFactory(repository)
viewModel = ViewModelProviders.of(this, factory).get(HomeViewModel::class.java)
viewModel.getDorms()
viewModel.dorms.observe(viewLifecycleOwner, Observer { dorms ->
recyclerViewDorms.also{
it.layoutManager = LinearLayoutManager(requireContext())
it.setHasFixedSize(true)
it.adapter = dormAdapter(dorms, this)
}
})
}
override fun onRecyclerViewItemClick(view: View, dorms: Dorms) {
when(view.id){
R.id.button_reserve -> {
// TODO: Go to new account if not signed up, etc...
Toast.makeText(requireContext(), "Reserve button clicked", Toast.LENGTH_LONG).show()
}
R.id.layoutBox -> {
// TODO: Go to Dorm Details
callback?.onRecyclerViewItemClick(view, dorms)
}
}
}
}
Home View Model
class HomeViewModel(private val repository: DormRepository) : ViewModel() {
private lateinit var job: Job
private val _dorms = MutableLiveData<List<Dorms>>()
val dorms: LiveData<List<Dorms>>
get() = _dorms
fun getDorms() {
job = Coroutines.ioThenMain(
{ repository.getDorms() },
{ _dorms.value = it }
)
}
override fun onCleared() {
super.onCleared()
if(::job.isInitialized) job.cancel()
}
}
Interface:
interface RecyclerViewClickListener {
fun onRecyclerViewItemClick(view: View, dorms: Dorms)
}
Details Fragment:
class dormDetailsFragment : Fragment() {
companion object {
private const val DORMS = "model"
fun newInstance(dorms: Dorms): dormDetailsFragment{
val args = Bundle()
args.putSerializable(DORMS, dorms)
val fragment = dormDetailsFragment()
fragment.arguments = args
return fragment
}
}
private lateinit var viewModel: DormDetailsViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val fragmentDormDetailsBinding =
FragmentDormDetailsBinding.inflate(inflater,container,false)
val model = arguments!!.getSerializable(DORMS) as Dorms
fragmentDormDetailsBinding.dormDetails = model
return fragmentDormDetailsBinding.root
}
}
Home Fragment Layout
<?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:id="#+id/fragment_home">
<TextView
android:id="#+id/text_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/refreshLayout">
<androidx.recyclerview.widget.RecyclerView
tools:listitem="#layout/layout_home"
android:id="#+id/recyclerViewDorms"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Details Layout
<?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>
<import type="android.view.View" />
<variable
name="dormDetails"
type="com.pptt.roomy.data.models.Dorms" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.pptt.roomy.ui.home.dormDetails.DormDetailsFragment"
android:id="#+id/DormDetailsFrag">
<ImageView
app:image="#{dormDetails.image}"
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:background="#drawable/propertysample"
/>
<TextView
android:text="#{String.valueOf(dormDetails.dormPrice)}"
tools:text="Php 2500"
android:id="#+id/textViewPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="6dp"
android:layout_marginStart="10dp"
android:textSize="20sp"
android:textStyle="normal"
android:textColor="#000000"
app:layout_constraintTop_toBottomOf="#id/image"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:text="#{dormDetails.dormName}"
tools:text="Dorm ni Jupa"
android:id="#+id/textViewPropertyName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="1dp"
android:layout_marginStart="10dp"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#000000"
app:layout_constraintTop_toBottomOf="#id/textViewPrice"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:text="#{dormDetails.dormType}"
tools:text="1 BR with Dining and Kitchen"
android:id="#+id/textViewRoomType"
android:layout_below="#id/textViewPropertyName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:layout_marginLeft="40dp"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="#+id/textViewPropertyName"
app:layout_constraintLeft_toLeftOf="parent"/>
<TextView
android:text="#{dormDetails.dormAddress}"
android:id="#+id/textViewAddress"
android:layout_marginBottom="5dp"
tools:text="455 San Jose II St., Brgy. 425, Sampaloc, Manila"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Small"
android:padding="5dp"
android:layout_marginLeft="40dp"
android:layout_width="wrap_content"
android:textAlignment="center"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/textViewRoomType"
app:layout_constraintLeft_toLeftOf="parent"/>
<TextView
android:text="#{dormDetails.dormDetails}"
android:id="#+id/textViewDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="A very long textarea to contain dorm description. Should be multiline"
android:padding="5dp"
android:layout_marginLeft="20dp"
app:layout_constraintTop_toBottomOf="#id/textViewAddress"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Anything else that's needed will be edited for later.
Remove these lines:
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.add(R.id.fragment_home, HomeFragment.newInstance(), "dormList")
.commit()
}
You're adding one HomeFragment via the NavHostFragment and another manually. You don't need to manually add Fragment when using Navigation.
You should also be updating your onRecyclerViewItemClick to use navigate() as per the Navigate to a destination documentation:
override fun onRecyclerViewItemClick(view: View, dorms: Dorms) {
val navController = findNavController(R.id.nav_host_fragment)
// If you're using Safe Args, use the ID generated from
// the navigation graph and make sure you have
// an argument of the correct type
navController.navigate(
HomeFragmentDirections.actionHomeToDetails(dorms))
}
You might find it helpful to look at the Pass data between destinations documentation to see how to create an <argument> in your graph for your Dorms object and how to set up Safe Args to generate the Directions class for you.
When my app runs there is a blank white space between my custom action bar and my list view and I would like to remove it but can't find any class method or xml setting to do that.
Here is my Kotlin code EnglandFragment.ktusing the list view:
class EnglandFragment : Fragment() {
// Access a Cloud Firestore instance from your Activity
val db = FirebaseFirestore.getInstance()
lateinit var adapter : ArrayAdapter<String>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_england, container, false)
(requireActivity() as CountriesActivity).initializeCustomActionBar(R.drawable.england_flag, R.string.title_counties)
var counties : ArrayList<String>
val docRef = db.collection("UKSites").document("England")
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
counties = document.get("Counties") as ArrayList<String>
adapter = ArrayAdapter(requireContext(), R.layout.list_item_view, counties)
groupListView.adapter = adapter
groupListView.setOnItemClickListener { parent, view, position, id ->
Toast.makeText(requireContext(), "Row selected: $position", Toast.LENGTH_SHORT ).show()
}
} else {
Log.d("Debug", "No such document")
}
}
.addOnFailureListener { exception ->
Log.d("Debug", "get failed with ", exception)
}
return root
}
}
Here is my xml code fragment_england.xml for the list view:
<?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">
<ListView
android:id="#+id/groupListView"
android:layout_width="0dp"
android:layout_height="0dp"
android:headerDividersEnabled="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is the Kotlin code for CountriesActivity.kt:
class CountriesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_countries)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
navView.setItemIconTintList(null);
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_england, R.id.navigation_scotland, R.id.navigation_wales, R.id.navigation_nireland
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
fun initializeCustomActionBar(imageViewResID: Int, textViewResID: Int) {
val actionBar: ActionBar? = this.supportActionBar
actionBar?.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM)
actionBar?.setDisplayShowCustomEnabled(true)
actionBar?.setCustomView(R.layout.custom_action_bar)
val view : View? = actionBar?.customView
var imageView : ImageView? = view?.findViewById(R.id.flagImageView)
imageView?.setImageResource(imageViewResID)
var textView : TextView? = view?.findViewById(R.id.countiesTextView)
textView?.text = getString(textViewResID)
actionBar?.setCustomView(view)
}
}
and here is the xml code for activity_countries.xml:
<?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"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
<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:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
The problem is in the line android:paddingTop="?attr/actionBarSize"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
</androidx.constraintlayout.widget.ConstraintLayout>
You can debug the view with layout inspector. To open the Layout Inspector, do the following:
Run your app on a connected device or emulator.
Click Tools > Layout Inspector.
In the Choose Process dialog that appears, select the app process you want to inspect
and click ok
For more info:
https://developer.android.com/studio/debug/layout-inspector