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.
Related
If I should change it from card to maybe a button for easier code please let me know, but I have tried multiple ways with card view and none seem to be working for me. Im trying to open a new activity when i click the cardview for DashboardTmsActivity.
current error is this
MainActivity.kt
Modifier 'override' is not applicable to 'local function':19
Unresolved reference: activity
Unresolved reference: activity:23
here is my current code.
MainActivity.kt
package com.cameron.armymaintenance
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.cardview.widget.CardView
import com.cameron.armymaintenance.databinding.ActivityDashboardTmsBinding
import com.cameron.armymaintenance.databinding.ActivityMainBinding
class MainActivity : DrawerBaseActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<CardView>(R.id.card1).setOnClickListener {
val card1 = Intent(activity, ActivityDashboardTmsBinding::class.java)
activity?.startActivity(card1)
}
}
}
}
DrawerBaseActivity.kt
package com.cameron.armymaintenance
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
open class DrawerBaseActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
var drawerLayout: DrawerLayout? = null
override fun setContentView(view: View?) {
drawerLayout = layoutInflater.inflate(R.layout.activity_drawer_base, null) as DrawerLayout
val container = drawerLayout!!.findViewById<FrameLayout>(R.id.activityContainer)
container.addView(view)
super.setContentView(drawerLayout)
val toolbar = drawerLayout!!.findViewById<Toolbar>(R.id.toolBar)
setSupportActionBar(toolbar)
val navigationView = drawerLayout!!.findViewById<NavigationView>(R.id.nav_view)
navigationView.setNavigationItemSelectedListener(this)
val toggle = ActionBarDrawerToggle(
this,
drawerLayout,
toolbar,
R.string.menu_drawer_open,
R.string.menu_drawer_open
)
drawerLayout!!.addDrawerListener(toggle)
toggle.syncState()
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
return false
}
private fun allocateActivityTitle(titleString: String?) {
if (supportActionBar != null) {
supportActionBar!!.title = titleString
}
}
open fun onViewCreated(view: View, savedInstanceState: Bundle?) {}
}
DashboardTmsActivity.kt
package com.cameron.armymaintenance
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class DashboardTmsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard_tms)
}
}
So there should be 2 errors if I have not misinterpreted:
Modifier 'override' is not applicable to 'local function':19
Unresolved reference: activity:23
onViewCreated() should not be placed inside onCreate(). If you need to implement onViewCreated(), it should be at the same level with onCreate()
activity is not available in an Activity. It only exists in a Fragment class. You can simply use this in an Activity.
class MainActivity : DrawerBaseActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
// Move to same level for function
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<CardView>(R.id.card1).setOnClickListener {
// Use this in Activity
val card1 = Intent(this#MainActivity, ActivityDashboardTmsBinding::class.java)
startActivity(card1)
}
}
}
Android studio shows errors in the lines
REPOSITORY.insert(note){ onSuccess() }
in the "AddNewNoteFragmentViewModel" and in the lines
viewModel.insert(AppNote(name = name, text = text)){ view?.findNavController()?.navigate(R.id.action_addNewNoteFragment_to_mainFragment)}
in the file "AddNewNoteFragment"
I realized that the error is related to streams, but I do not know how to solve it
If anyone knows, please help, I could not find anything worthwhile on the Internet
AddNewNoteFragment
package com.example.notes.fragments.add_new_note
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
import com.example.notes.R
import com.example.notes.databinding.FragmentAddNewNoteBinding
import com.example.notes.model.AppNote
import com.example.notes.utilits.showToast
class AddNewNoteFragment : Fragment() {
private var _binding: FragmentAddNewNoteBinding? = null
val binding get() = _binding!!
private lateinit var viewModel: AddNewNoteFragmentViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentAddNewNoteBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onStart() {
super.onStart()
initialization()
}
private fun initialization() {
viewModel = ViewModelProvider(this).get(AddNewNoteFragmentViewModel::class.java)
binding.buttonAddNote.setOnClickListener {
val name = binding.inputNameNote.text.toString()
val text = binding.inputTextNote.text.toString()
if (name.isEmpty()){
showToast("Введите имя заметки")
} else{
viewModel.insert(AppNote(name = name, text = text)){
view?.findNavController()?.navigate(R.id.action_addNewNoteFragment_to_mainFragment)
}
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
AddNewNoteFragmentViewModel
package com.example.notes.fragments.add_new_note
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.example.notes.model.AppNote
import com.example.notes.utilits.REPOSITORY
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class AddNewNoteFragmentViewModel(application: Application): AndroidViewModel(application) {
fun insert(note: AppNote,onSuccess:()-> Unit) =
viewModelScope.launch (Dispatchers.IO){
REPOSITORY.insert(note){
onSuccess()
}
}
}
Error screen
enter image description here
Problem solved
In AddNewNoteFragmentViewModel requires Dispatchers.IO to be changed to Dispatchers.Main
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()
MainActivity.kt file - this is the main activity.
How to add an onItemClickListener() in gridview implemented using a custom adapter (here I named CategoryAdapter)? I like to add it to set click listener in the image.
MainActivity.kt
package com.hari.rideit.Controller
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.GridView
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.viewpager.widget.ViewPager
import com.google.android.material.navigation.NavigationView
import com.hari.rideit.Adapters.CategoryAdapter
import com.hari.rideit.Adapters.ViewPageAdapter
import com.hari.rideit.R
import com.hari.rideit.Services.DataService
import kotlinx.android.synthetic.main.content_main.*
import java.util.*
class MainActivity : AppCompatActivity() , NavigationView.OnNavigationItemSelectedListener{
lateinit var adapter:CategoryAdapter
lateinit var toolbar: Toolbar
lateinit var drawerLayout: DrawerLayout
lateinit var navView: NavigationView
lateinit var gridview:GridView
internal lateinit var viewPager:ViewPager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
drawerLayout = findViewById(R.id.drawer_layout)
navView = findViewById(R.id.nav_view)
gridview= findViewById(R.id.contentGridView)
val toggle = ActionBarDrawerToggle(
this, drawerLayout, toolbar, 0, 0
)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
navView.setNavigationItemSelectedListener(this)
adapter= CategoryAdapter(this,DataService.categories)
gridview.adapter= adapter
viewPager=findViewById<View>(R.id.viewPager) as ViewPager
val adapter= ViewPageAdapter(this)
viewPager.adapter=adapter
}
Category Adapter:
this is the custom adapter used to get the image and text in gridview
package com.hari.rideit.Adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import android.widget.TextView
import com.hari.rideit.R
import com.hari.rideit.model.Category
class CategoryAdapter(context:Context,categories:List<Category>):BaseAdapter(){
val context= context
val categories= categories
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val categoryView: View
categoryView = LayoutInflater.from(context).inflate(R.layout.category_layout, null)
val categoryImage: ImageView = categoryView.findViewById(R.id.categoryImage)
val categoryname: TextView = categoryView.findViewById(R.id.categoryText)
val category = categories[position]
categoryname.text = category.title
val resourceId =
context.resources.getIdentifier(category.image, "drawable", context.packageName)
categoryImage.setImageResource(resourceId)
return categoryView
}
override fun getItem(position: Int): Any {
return categories[position]
}
override fun getItemId(position: Int): Long {
return 0
}
override fun getCount(): Int {
return categories.count()
}
}
You can add onClickListener to each item of the GridView. Before returning categoryView, set onClickListener on it.
categoryView.setOnClickListener {
//put your code here
}
If you want to access the clicked position in your MainActivity.kt. Create an Interface in CategoryAdapter class. See the code below.
interface OnItemClickListener{
fun onClick(position:Int)
}
Now pass a variable to CategoryAdapter of type OnItemClickListener.
class CategoryAdapter(context:Context,categories:List<Category>, var listener:OnItemClickListener):BaseAdapter(){
// rest of the code
Now before returning. Before return categoryView, set onClickListener on it and pass the position to the listener
categoryView.setOnClickListener {
listener.onClick(position)
}
Now in your MainActivity.kt file implement OnItemClickListener and override OnClick function.
class MainActivity : AppCompatActivity() , NavigationView.OnNavigationItemSelectedListener, CategoryAdapter .OnItemClickListener {
//rest of the code
override fun onClick(position:Int){
// perform the code.
}
}
You need to change your adapter constructor to:
class CategoryAdapter(context:Context,categories:List<Category>, val listener: View.OnClickListener)
And call it from your Activity like this:
adapter= CategoryAdapter(this,DataService.categories, object: View.OnClickListener{
override fun onClick(v: View?) {
Toast.makeText(context, "Click", Toast.LENGTH_LONG).show()
}
})
Inside your adapter, add this code after "categoryImage.setImageResource(resourceId)":
categoryImage.setOnClickListener {
listener.onClick(categoryImage)
}
convertView.setOnClickListener(){}
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.