Issue overriding onDrawerOpened() - Kotlin - kotlin

I'm working on a Kotlin coded app using the Navigation Drawer Layout with a list of items. When I open the drawer using the icon at the top left I want it to notifyDataSetChanged so that the list is updated from outside the MainActivity. This includes pressing the back button, swiping open the drawer, or clicking the stacked lines icon at the top left
Here's a simplified version of my code, but it's essentially a Navagation Drawer Activity with a recyclerView for the context of my question:
class MainActivity : AppCompatActivity() {
lateinit var drawerLayout: DrawerLayout
lateinit var navView: NavigationView
lateinit var toolbar: Toolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar = findViewById(R.id.app_bar_toolbar)
setSupportActionBar(toolbar)
drawerLayout = findViewById(R.id.drawer_layout)
navView = nav_view
val toggle = ActionBarDrawerToggle(this, drawerLayout, toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
}
I've spent several hours trying anything I can, but primarily I can't get this to work:
override fun onDrawerOpen(view:View){
... myAdapter.notifyDataSetChanged()
super.onDrawerOpen(view)
}
it gives me the hint "overrides nothing" or "unused", and if I try to add it to any of my code in onCreate it states "Modifier 'override' not applicable to local function.
What am I doing wrong? Is there a better way to notify a data change when the drawer is opened?

The Question is old, but to whom has the same problem:
Just Create new class that implements DrawerListener and write your logic inside appropriate method:
private inner class MyDrawerListener(): DrawerListener {
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
}
override fun onDrawerOpened(drawerView: View) {
}
override fun onDrawerClosed(drawerView: View) {
}
override fun onDrawerStateChanged(newState: Int) {
}
}
Now add listener as:
drawerLayout.addDrawerListener(MyDrawerListener())

Related

Why is data being shown when screen rotates in jetpack compose

I'm facing this issue where the data I'm retrieving from an API, https://randomuser.me/api/ at first compose it doesn't load.
But every time I rotate the screen the data updates.
First load
After screen rotation
View
class MainActivity : ComponentActivity() {
private val userViewModel : UserViewModel by viewModels()
private var userList: List<UserModel> = listOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
userViewModel.userModel.observe(this, Observer {
userList = it
})
userViewModel.onCreate()
setContent {
ListUsers(userList = userList)
}
}
}
ViewModel
class UserViewModel : ViewModel() {
val userModel = MutableLiveData<List<UserModel>>()
var getRandomUsersUseCase = RandomUsersUseCase()
fun onCreate() {
viewModelScope.launch {
val result = getRandomUsersUseCase()
if(!result.isNullOrEmpty()){
userModel.postValue(result)
}
}
}
}
Use State to ensure the data changes trigger recomposition of the Composable.
If you use another observable type such as LiveData in Compose, you
should convert it to State before reading it in a composable using
a composable extension function like LiveData.observeAsState().
Changes to your code would be,
val userListState by userViewModel.userModel.observeAsState()
setContent {
ListUsers(userList = userListState)
}
Why does it shows the data during rotation?
When rotating the screen or during any other configuration changes, the activity will be recreated.
More info on that here - Docs
In most cases, you would not require data to be changed when the screen rotates.
If you want to persist the data even after screen rotation, move the code inside onCreate() in your UserViewModel to the init block, like this.
init {
getData()
}
fun getData() {
viewModelScope.launch {
val result = getRandomUsersUseCase()
if(!result.isNullOrEmpty()){
userModel.postValue(result)
}
}
}
If you need to refresh the data on any other event like button click, swipe to refresh, etc, just call the getData() again on the event handler.
P.S: Check correct imports are added as required.
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue

how to navigate to fragment inside recycler view?

I have an activity that is controlled with a navigation component, it has few fragments, inside one of these fragments there is a recyclerView that has some items, when I click on an Item I want it to navigate me to another fragment that has additional information about the item, I don't know how to use navigation component inside a recycelerView, when I type findNavController it has some parameters that am not sure what to put in or if its even the right function, I also tried to do it by code like this:
val fm = (context as AppCompatActivity).supportFragmentManager
fm.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null)
.commit()
by the way this is the code that asks for other parameters:
// it asks for a (fragment) or (activity, Int)
findNavController().navigate(R.id.action_settingsFragment_to_groupUnits)
the problem is when I navigate out of this fragment or use the drawer navigation (nav component for the other fragments), this fragment that I navigated to stays displayed in the screen, I see both fragments at the same time, I assume its a fragment backStack issue but I don't know how to solve it, thanks for the help and your time in advance
You do not need to navigate from RecyclerView item click to AdditionalDetails fragment directly.
You can do this same thing by help of interface.
Steps:
Create an interface with a method declaration.
Extend Interface from the fragment where you are using your RecyclerView and Implement interface method.
Pass this interface with the adapter.
Using the interface from adapter you just pass object when click on item.
Finally from your fragment you just navigate to AdditionalDetails fragment with argument.
Lets see sample code from my current project:
Interface
interface ChatListClickListener {
fun onChatListItemClick(view:View, user: User)
}
Adapter Class
class UserAdapter(val Users: List<User>, val chatListClickListener: ChatListClickListener) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
inner class UserViewHolder(
val recyclerviewUsersBinding: RecyclerviewChatlistBinding
) : RecyclerView.ViewHolder(recyclerviewUsersBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val vh = UserViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.recyclerview_chatlist,
parent,
false
)
)
return vh
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.recyclerviewUsersBinding.user = Users[position]
holder.recyclerviewUsersBinding.root.setOnClickListener{
chatListClickListener.onChatListItemClick(it,Users[position])
}
}
override fun getItemCount(): Int {
return Users.size
}
}
My fragment
class FragmentChatList : Fragment(), ChatListClickListener {
lateinit var binding: FragmentChatListBinding
lateinit var viewModel: ChatListViewModel
lateinit var listener: ChatListClickListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val args: FragmentChatListArgs by navArgs()
binding = FragmentChatListBinding.inflate(layoutInflater, container, false)
val factory = ChatListFactory(args.user)
viewModel = ViewModelProvider(this, factory).get(ChatListViewModel::class.java)
binding.viewModel = viewModel
listener = this
lifecycleScope.launch {
viewModel.addUserWhenUserConnect()
}
viewModel.userList.observe(viewLifecycleOwner, Observer { data ->
binding.rvChatList.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
adapter = UserAdapter(data, listener)
}
})
return binding.root
}
override fun onChatListItemClick(view: View, user: User) {
Toast.makeText(requireContext(), user.name + "", Toast.LENGTH_SHORT).show()
// here you navigate to your fragment....
}
}
I guess this will be helpful.

What is the correct way to use viewLifecycleOwner in MainActivity?

I am trying to figure out how to correctly use the viewLifecycleOwner in MainActivity, I have read and been told lifecycles are used with fragments. However, I am not implementing fragments in my app. When adding observers in the code, I am using "this" in place of viewLifecycleOwner. This would not rise any errors, but will eventually not work as it doesn't bind data properly in the virtual device (when running the app, it only displays a blank page for the app without data or images). So far, what I have in MainActivity is the following code.
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: DrinkViewModel
// Contains all the views
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// Use Data Binding to get reference to the views
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.drinkButton.setOnClickListener {
onDrinkClicked()
}
viewModel.revenue.observe(this, Observer { newRevenue ->
binding.revenueText.text = newRevenue.toString()
})
viewModel.drinksSold.observe(this, Observer { newAmount ->
binding.amountSoldText.text = newAmount.toString()
})
}
}
After EpicPandaForce's comment, I focused on whether I was correctly binding the data and images. I realized I was not. I was mistakenly binding revenue and amountSold as texts. I was also trying to set newRevenue and newAmount to strings. Revenue and amountSold were supposed to be passed as Integers. The following code is the correct one.
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: DrinkViewModel
// Contains all the views
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// Use Data Binding to get reference to the views
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.drinkButton.setOnClickListener {
onDrinkClicked()
}
viewModel.revenue.observe(this, Observer { newRevenue ->
binding.revenue = newRevenue
})
viewModel.drinksSold.observe(this, Observer { newAmount ->
binding.drinkSold = newAmount
})
}
}

New to coding; Can't get button to link to new activity in Kotlin

It opens the first activity on the emulator, but fails on button click. I created the second activity (AddProductActivity), so maybe I did something wrong there. I've tried all the variables I could find online to change how the button operates.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.content_main)
val button = findViewById<Button>(R.id.goToAddProduct)
button.setOnClickListener {
val intent = Intent(this, AddProductActivity::class.java)
startActivity(intent)
}
}
}
class AddProductActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.add_product)
}
}
on content_main.xml:
<Button
android:id="#+id/goToAddProduct"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/add_to_inventory"/>
Make sure you have registered your new activity in the manifest. This is a common mistake by newcomers.
If that is not the case before you click on the button open your logcat view in android studio. Once you click on the button please investigate the crash issue from here.

Kotlin: How do I add an actionbar menu on a tabbed activity?

I have a tabbed activity with fragments. Now I just need to add an actionbar menu. I am guessing I add the action bar in the mainactivity. How can I do this?
I have tried adding an action bar for each fragment but looks like that cannot be done in kotlin. I am new to android development but I was able to perform this task with java. I am converting my small projects to Kotlin which is supposed to be easier than java.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val sectionsPagerAdapter = SectionsPagerAdapter(this, supportFragmentManager)
val viewPager: ViewPager = findViewById(R.id.view_pager)
viewPager.adapter = sectionsPagerAdapter
val tabs: TabLayout = findViewById(R.id.tabs)
tabs.setupWithViewPager(viewPager)
val fab: FloatingActionButton = findViewById(R.id.fab)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.mainmenu, menu)
return true
}
}
}
Actually figured it out. My question was basically senseless. It should have been how to add a menu in an AppBarLayout. My app is using a tabbed activity.
All I had to do was under my main activity layout, I needed to add a toolbar under the Appbarlayout.
then under the Main activity Oncreate, I added
setSupportActionBar(toolbar)
val actionBar = supportActionBar
Then call the oncreateoptionsmenu
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu to use in the action bar
val inflater = menuInflater
inflater.inflate(R.menu.mainmenu, menu)
return super.onCreateOptionsMenu(menu)
}