import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.drawerlayout.widget.DrawerLayout
import com.example.tender.R
import com.example.tender.databinding.FragmentAvailableTenderBinding
import com.example.tender.databinding.FragmentLoginBinding
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.NavigationUI
/**
* A simple [Fragment] subclass.
*/
class AvailableTenderFragment : Fragment() {
private lateinit var drawerLayout: DrawerLayout
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentAvailableTenderBinding.inflate(inflater)
drawerLayout = binding.drawerLayout
**val navController = this.findNavController(R.id.nav_host_fragment)**
**NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)**
NavigationUI.setupWithNavController(binding.navView, navController)
binding.root
}
}
"To many arguments for public fun Fragment.findNavController():NAvcontroller defined in androidx.navigation.fragment" is showing when i hover on findnavcontroller.I am unable to resolve the error on the above bold lines.In this case what should I do?
As per the Navigate to a destination documentation, the androidx.navigation.fragment.findNavController you've imported takes no parameters (it finds the parent NavHostFragment of the current Fragment and doesn't need the ID of the NavHostFragment).
The lines you've written, namely findNavController(R.id.nav_host_fragment) and setupActionBarWithNavController() are methods you'd call in an Activity, not in a Fragment.
Related
I am trying to build an app that consists of 3 fragments displaying 3 different webviews content from the internet.
How to implement the onKeyDown method so that I can go back to the previous page in the webview not the app activities?
Homefragment(one of the 3 fragments)
package com.example.uncensorednews.ui.BBC
import android.os.Bundle
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.uncensorednews.MainActivity
import com.example.uncensorednews.R
import com.example.uncensorednews.WebViewController
import com.example.uncensorednews.databinding.FragmentHomeBinding
abstract class HomeFragment : Fragment() {
private lateinit var myWebView:WebView
private var _binding: FragmentHomeBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
myWebView = root.findViewById(R.id.WebView)
myWebView.loadUrl("https://www.bbc.com/news/world/asia/india")//This will help us to load the url and refer it to the webview
myWebView.webViewClient= WebViewController()//this helps us to retain the user to the app not to the web browser if he clicks on to something
myWebView.settings.javaScriptEnabled=true
/*val textView: TextView = binding.textHome
homeViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}*/
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
MainActivity
package com.example.uncensorednews
import android.graphics.Color
import android.os.Bundle
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.webkit.WebView
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.navigation.NavigationView
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.drawerlayout.widget.DrawerLayout
import androidx.appcompat.app.AppCompatActivity
import com.example.uncensorednews.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var menuItem: MenuItem
private lateinit var spannable: SpannableString
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.appBarMain.toolbar)
binding.appBarMain.fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)//floating button
.setAction("Action", null).show()
}
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_content_main)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
for(i in 0 until menu.size()-1)
{
menuItem=menu.getItem(i)
spannable=SpannableString(menu.getItem(i).title.toString())
spannable.setSpan(ForegroundColorSpan(Color.WHITE),0,spannable.length,0)
menuItem.title = spannable
}
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_main)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
The WebViewController helps to retain the user to stay on the app if a link is further clicked on the webview.
I am using fragments in my android application, when I run the program, the percentage name is displayed incorrectly, but after I switch to another fragment and perform some actions, everything becomes as it should be.
I set the title for the fragment on this line:
(activity as AppCompatActivity).supportActionBar?.title = "Soccer Quiz"
but it only applies when moving to another fragment.
My kotlin code:
package com.example.soccerquiz
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.navigation.Navigation
import com.example.soccerquiz.databinding.FragmentWelcomeScreenBinding
/**
* A simple [Fragment] subclass.
*/
class WelcomeScreenFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding: FragmentWelcomeScreenBinding =
DataBindingUtil.inflate(
inflater, R.layout.fragment_welcome_screen, container, false
)
binding.letsPlayButton.setOnClickListener { view: View ->
Navigation.findNavController(view)
.navigate(R.id.action_welcomeScreenFragment_to_quizFragment)
}
(activity as AppCompatActivity).supportActionBar?.title = "Soccer Quiz"
return binding.root
}
}
https://i.stack.imgur.com/LzNLk.png
https://i.stack.imgur.com/66NXk.png
Please help me with my problem.
It was necessary to change the title of fragment in navigation graph.
No need to install manually, because when you start the program it does not always work, you have to switch between fragments.
I'm trying change some text after load a fragment. It will be location tracker in future. So I'm testing change of the text.
build.gradle module:
android {
buildFeatures{
dataBinding = true
viewBinding = true
}
}
start.xml:
<TextView
android:id="#+id/location"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:text="Current GPS Location"
android:textColor="#FF0000"
android:textSize="30sp"
android:textStyle="bold" />
Start.kt:
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil.setContentView
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.basic.databinding.StartBinding
import android.util.Log
class Start : Fragment() {
private lateinit var binding: StartBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = StartBinding.inflate(layoutInflater)
binding.location.setText("Hello World")
}
}
Application runs without problem but setText Not works. Do you know why?
You are initializing your binding but not adding it to the view
onCreateView(inflater... container...) {
binding = StartBinding.inflate(layoutInflater, container, false)
return binding.root
}
then following the life cycle usage previous to binding, you have to make the change after the view is created
onViewCreated(...) {
binding.location.setText("Hello World")
}
A small comment, your naming doesn't follow conventions:
StartFragment
fragment_start
You should do the binding in onCreateView instead of onCreate as in onCreate() the fragment view is not created yet.
So transfer below in onCreateView
binding = StartBinding.inflate(layoutInflater)
binding.location.setText("Hello World")
For more info check fragment lifecycle
Thanks for answers!
Solution is very simple.
Here is solution for people who will find this question:
class Start : Fragment() {
lateinit var binding : StartBinding //this must be at the begin of your class
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = StartBinding.inflate(layoutInflater,container,false) //this must be here in onCreateView
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textView.setText("Hello World") // and now setText works in onViewCreated
}
}
I'm a relative beginner to android programming and I want to have it so my fragments display the layout files which are assigned to them through a view model. I'll be providing only one fragment and view model pair as the 3 others which I have are practically the same except some names and text is swapped around.
Here's my TimetableFragment.kt
package com.example.intranetapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
class TimetableFragment : Fragment() {
private lateinit var timetableViewModel: TimetableViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
timetableViewModel =
ViewModelProvider(this).get(TimetableViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_news, container, false)
val textView: TextView = root.findViewById(R.id.text_news)
timetableViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
return root
}
}
Here's my TimetableViewModel.kt
package com.example.intranetapp
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class TimetableViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "This is timetable Fragment"
}
val text: LiveData<String> = _text
}
And here's my MainActivity.kt
package com.example.intranetapp
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
class MainActivity : AppCompatActivity() {
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.timetableFragment, R.id.dueworkFragment, R.id.newsFragment, R.id.gradesFragment))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
I'm not getting any errors when running the application, i'm more just wondering how i can make it so my XML file Timetable.XML is shown when creating the viewmodel rather than text in the middle of the screen which says "This is Timetable fragment"
I'm sorry if i come across as a dumb coder, i'm just struggling to fix this issue and this was the first place to come to mind as google searches aren't helping
The problem is here:
private val _text = MutableLiveData<String>().apply {
value = "This is timetable Fragment"
}
Setting the value happens when ViewModel is initializing. But you start observing this LiveData after initializing, so you loss this value.
You should add method in your viewModel:
fun setTextValue() {
_text.value = "This is timetable Fragment"
}
and call it after timetableViewModel.text.observe()
So I'm trying to create a tabbed app in Kotlin and I have chosen the default one they made for you to practice but I can't figure out how to get the buttons working `
package com.example.android_app.ui.home
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.example.android_app.R
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_home.*
class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Program Buttons
logout.setOnClickListener{
val intent = Intent(this, sign_in::class.java)
startActivity(intent)
}
}
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel =
ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
homeViewModel.text.observe(viewLifecycleOwner, Observer {
})
return root
}
}
`
As far as I know this should work. The problem is not in the button but in Intent. My button is already defined and has no errors but there is a red line under Intent even though it's imported. The error message is below.
public constructor Intent(p0: Context!, p1: Class<*>!) defined in android.content.Intent
public constructor Intent(p0: String!, p1: Uri!) defined in android.content.Intent
You need to pass context into the constructor of Intent instead of this.
val intent = Intent(context, sign_in::class.java)