I am trying to communicate between MainActivity.kt and sharedviewmodel.kt which is of type viewModel() for WearOS, But when i try to create an instance of the sharedviewmodel.kt in MainActivity.kt by below lines
private lateinit var viewModel: sharedviewmodel
viewModel = ViewModelProvider(this).get(sharedviewmodel::class.java)
when i pass "this" as owner of the ViewModelProvider, i face a error called
Required : ViewModelStoreOwner
Found : MainActivity
Kindly someone help to solve the issue,
Find the MainActivity.kt and sharedviewmodel.kt below.
import android.app.Activity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.watch7_view_model_example.databinding.ActivityMainBinding
class MainActivity : Activity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: sharedviewmodel
var tag = "view_model"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this).get(sharedviewmodel::class.java)
viewModel.azimuth().observe(this, Observer {
Log.d(tag,"data")
})
}
}
sharedviewmodel.kt
package com.example.watch7_view_model_example
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class sharedviewmodel:ViewModel() {
private val azimuth: MutableLiveData<String> = MutableLiveData()
private val elevation: MutableLiveData<String> = MutableLiveData()
fun azimuth(): LiveData<String> = azimuth
fun elevation(): LiveData<String> = elevation
fun azimuth(azimuth_t: String) {
azimuth.value = azimuth_t
}
fun elevation(elevation_t: String) {
elevation.value = elevation_t
}
init {
// Setting default value
azimuth.value = "0"
elevation.value = "0"
}
}`
Related
I am trying to pass array list data to one of my fragments in the nav bar from main activity. Is there a simple way to implement this? Or should I use activity instead of the fragment? I need to pass the array list data to my HomeFragment and use recycler view adapter in the fragment, but now the issue is I don't know how to pass data because I am using bottom navigation view and Android Navigation component.
Main Activity
package com.example.a5t1v2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MenuItem
import android.widget.GridView
import androidx.navigation.NavArgument
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomnavigation.BottomNavigationView
class MainActivity : AppCompatActivity() {
private lateinit var bottomNavigationView: BottomNavigationView
private lateinit var navController: NavController
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var shoppingItemList:MutableList<Item>
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initUI()
val dbHelper = DBHelper(this)
populateDB(dbHelper)
shoppingItemList = dbHelper.getAlItems()
}
private fun populateDB(dbHelper: DBHelper) {
dbHelper.insertItem(Item("Bread",1,"Default", urgent = true, bought = false,null))
dbHelper.insertItem(Item("Chocolate Bar",1,"Small", urgent = false, bought = false,null))
dbHelper.insertItem(Item("Instant noodle",1,"Default", urgent = false, bought = false,null))
dbHelper.insertItem(Item("Juice",2,"Large", urgent = false, bought = false,null))
dbHelper.insertItem(Item("Milk",3,"Large", urgent = true, bought = false,null))
dbHelper.insertItem(Item("Shampoo",1,"Small", urgent = false, bought = true,"24 Aug 2020"))
dbHelper.insertItem(Item("Shower Gel",1,"Large", urgent = false, bought = true,"24 Aug 2020"))
}
private fun initUI() {
appBarConfiguration = AppBarConfiguration(setOf(R.id.urgentListFragment, R.id.homeFragment, R.id.completedListFragment))
bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
navController = findNavController(R.id.fragmentContainerView)
setupActionBarWithNavController(navController,appBarConfiguration)
bottomNavigationView.setupWithNavController(navController)
}
}
An easy and good way would be to use a sharedViewModel.
Create a ViewModel and declare a LiveData in it.
class MainViewModel: ViewModel () {
private val arrayListLiveData = MutableLiveData<ArrayList<Item>>()
}
Inside Activity, create an instance of this ViewModel in the acitivity, and you can send your arrayList from here using post on the liveData
class MainActivity : AppCompatActivity() {
private lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
mainViewModel.arrayListLiveData.post(YOUR_ARRAY_LIST_DATA)
}
}
You can observe the same liveData inside the fragment, and would receive the posted arrayList there, from the Activity
class TestFragment : Fragment(R.layout.test) {
private lateinit var mainViewModel: MainViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
mainViewModel.arrayListLiveData.observe(viewLifecycleOwner) { arrayList ->
//Perform desired operation with the array list
}
}
}
I have a trouble with a error "E/RecyclerView: No adapter attached; skipping layout" for 2 days...
Please help me what causes this error...
I already checked that getItemCount function return 1 result(Because the ProductEntity database only have 1 data)
Thank you in advance!!
[ProductActivity.kt]
package com.example.trymakeapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.trymakeapp.databinding.ActivityProductBinding
import com.example.trymakeapp.databinding.ItemProductBinding
import com.example.trymakeapp.db.AppDatabase
import com.example.trymakeapp.db.ProductDao
import com.example.trymakeapp.db.ProductEntity
class ProductActivity : AppCompatActivity() {
private lateinit var binding: ActivityProductBinding
private lateinit var db: AppDatabase
private lateinit var productDao: ProductDao
private lateinit var productList : ArrayList<ProductEntity>
private lateinit var adapter : ProductRecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityProductBinding.inflate(layoutInflater)
setContentView(binding.root)
db = AppDatabase.getInstance(this)!!
productDao = db.getProductDao()
getAllProductList()
}
private fun getAllProductList() {
Thread {
productList = ArrayList(productDao.getAll())
setRecyclerView()
}.start()
println("#####")
}
private fun setRecyclerView() {
runOnUiThread {
adapter = ProductRecyclerViewAdapter(productList)
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this#ProductActivity)
}
}
}
[ProductRecyclerViewAdapter.kt]
package com.example.trymakeapp
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.trymakeapp.databinding.ItemProductBinding
import com.example.trymakeapp.db.ProductEntity
import java.util.ArrayList
class ProductRecyclerViewAdapter(private val productList : ArrayList<ProductEntity>)
: RecyclerView.Adapter<ProductRecyclerViewAdapter.MyViewHolder>() {
inner class MyViewHolder(binding: ItemProductBinding) :
RecyclerView.ViewHolder(binding.root) {
val product_idx = binding.productIdx
val product_name = binding.productName
val product_price = binding.productPrice
val product_img = binding.productImg
val root = binding.root
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding : ItemProductBinding =
ItemProductBinding.inflate(LayoutInflater.from(parent.context),
parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val productData = productList[position]
holder.product_idx.text = productData.idx
holder.product_name.text = productData.name
holder.product_price.text = productData.price.toString()
}
override fun getItemCount(): Int {
return productList.size
}
}
[MainActivity.kt]
...
...
btn3.setOnClickListener {
val intent = Intent(this, ProductActivity::class.java)
startActivity(intent)
}
Try to move the initialization in the onCreate method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityProductBinding.inflate(layoutInflater)
setContentView(binding.root)
db = AppDatabase.getInstance(this)!!
productDao = db.getProductDao()
binding.recyclerView.layoutManager = LinearLayoutManager(this)
adapter = ProductRecyclerViewAdapter(arrayListOf())
binding.recyclerView.adapter = adapter
getAllProductList()
}
And edit your setRecyclerView method like this (You can remove the adapter and productList global variables):
private fun setRecyclerView(productList: ArrayList<ProductEntity>) {
runOnUiThread {
binding.recyclerView.adapter = ProductRecyclerViewAdapter(productList)
}
}
EDIT
To handle the fact that data are not displayed. Try to use LiveData to observe its changes:
#Dao
interface ProductDao {
#Query("select * from ProductEntity")
fun getAll() : LiveData<List<ProductEntity>>
}
Then observe changes in the getAll method using observe:
private fun getAllProductList() {
productDao.getAll().observe(this) {
setRecyclerView(ArrayList(it))
}
}
sorry for the inconvenience, but this is my first time trying this. I've been trying on doing this app which it has a recyclerView which I intent it to open a chrome page. The thing is, I'm not able to implement the itemOnClickListener despite having tried a lot of videos. Could you point out where is my mistake?
My Adapter:
package com.example.practica1.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.example.practica1.R
import com.example.practica1.data.Video
class MainAdapter(private val context: Context, private val videos: List<Video>): RecyclerView.Adapter<MainAdapter.ViewHolder>(){
private lateinit var mListener: onItemClickListener
interface onItemClickListener{
fun onItemClick(position: Int)
}
fun setOnClickListener(listener: onItemClickListener){
mListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_cartoon, parent, false)
return ViewHolder(view, mListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val video = videos[position]
holder.title.text = video.data.short_title
holder.serieTitle.text = video.data.show.title
holder.durationTitle.text = video.data.video.duration + " seg"
Glide.with(context)
.load(video.data.imagepath)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.override(400, 400)
.centerCrop()
.into(holder.imagePrev)
}
override fun getItemCount() = videos.size
inner class ViewHolder(itemView: View, listener: onItemClickListener) : RecyclerView.ViewHolder(itemView){
val title: TextView = itemView.findViewById(R.id.videoTitle)
val serieTitle: TextView = itemView.findViewById(R.id.serieTitle)
val durationTitle: TextView = itemView.findViewById(R.id.durationTitle)
val imagePrev: ImageView = itemView.findViewById(R.id.photoPreview)
init{
itemView.setOnClickListener{
listener.onItemClick(adapterPosition)
}
}
}
}
The fragment in which I tried to implement the method:
package com.example.practica1.fragments
import android.os.Bundle
import android.util.Log
import android.util.Log.d
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import com.example.practica1.R
import com.example.practica1.adapter.MainAdapter
import com.example.practica1.data.ApiInterface
import com.example.practica1.data.Video
import com.example.practica1.databinding.FragmentMenuBinding
import com.haerul.bottomfluxdialog.BottomFluxDialog
import com.haerul.bottomfluxdialog.BottomFluxDialog.OnInputListener
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class MenuFragment : Fragment() {
private var _binding: FragmentMenuBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentMenuBinding.inflate(inflater, container, false)
val view = inflater.inflate(R.layout.fragment_menu, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val retrofit = Retrofit.Builder()
.baseUrl("https://www.cartoonnetwork.com.co")
.addConverterFactory(GsonConverterFactory.create())
.build()
val api = retrofit.create(ApiInterface::class.java)
showProgress()
api.fetchAllVideos().enqueue(object : Callback<List<Video>> {
override fun onResponse(call: Call<List<Video>>, response: Response<List<Video>>) {
if (response.isSuccessful()) {
d("daniel", "onResponse ${response.body()!![0].id}")
if (response.body()!!.size > 0) {
showData(response.body()!!)
getResult(true, "Sucess")
} else
getResult(false, response.message())
hideProgress()
}
}
override fun onFailure(call: Call<List<Video>>, t: Throwable) {
d("daniel", "onFailure")
getResult(false, "Request Timeout. Please Try Again!")
hideProgress()
}
})
binding.search.setOnClickListener { v -> showDialog(v) }
}
fun showData(videos: List<Video>){
val test: RecyclerView = requireActivity().findViewById(R.id.recycler_viewMenu)
var adap = MainAdapter(requireContext(), videos)
test.apply{
adapter = adap
}
adap.setOnClickListener(object: MainAdapter.onItemClickListener{
override fun onItemClick(position: Int){
d("test", "test")
}
})
}
fun showProgress(){
binding.swipeRefresh.setRefreshing(true)
binding.emptyView.setVisibility(View.GONE)
binding.shimmer.startShimmer()
binding.shimmer.setVisibility(View.VISIBLE)
binding.recyclerViewMenu.setVisibility(View.GONE)
}
fun hideProgress(){
binding.swipeRefresh.setRefreshing(false)
binding.shimmer.stopShimmer()
binding.shimmer.setVisibility(View.GONE)
binding.recyclerViewMenu.setVisibility(View.VISIBLE)
}
fun getResult(status: Boolean, message: String){
if(!status) {
binding.emptyView.setVisibility(View.VISIBLE)
binding.textEmptyErr.setText(message)
}
else
binding.emptyView.setVisibility((View.GONE))
}
private fun showDialog(view: View){
BottomFluxDialog.inputDialog(requireActivity())
.setTextTitle("Input Title")
.setTextMessage("This is an input message")
.setRightButtonText("SUBMIT")
.setInputListener(object : OnInputListener {
override fun onSubmitInput(text: String?) {
val bundle = Bundle()
bundle.putString("search", text)
Navigation.findNavController(view)
.navigate(R.id.action_menuFragment_to_listFragment, bundle)
}
override fun onCancelInput() {}
})
.show()
}
}
Thanks!
There is no such common thin like OnItemClickListener for RecyclerView, so you have to design your own. It shouldn't be hard to implement by hand, though. What you can do, is that you can Just set OnClickListener to the whole item view and delegate it clicks to upper lambda.
class YourAdapter(
private val listener: (Video) -> Unit,
private val videos: List<Video>,
): ... {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
...
holder.itemView.setOnClickListener { v ->
if (position != RecyclerView.NO_POSITION) {
listener(videos[position])
}
}
}
}
There are no noticeable side-effects of swapping View.OnClickListener in onBindViewHolder() method.
Also, few suggestions:
Read about ListAdapterhere. Looks like it perfect fit for your usecase.
Use getAbsoluteAdapterPosition() instead of adapterPosition() method within ViewHolder, as adapters are designed to be combined. Read more about ConcatAdapter here.
You don't need to pass Context to adapter constructor. View already has context, so you can query context by calling viewHolder.itemView.context.
Make sure to use your binding. It looks like you creating your binding in MenuFragment, but later you are getting RecyclerView by id in an old-fashioned way.
Don't create adapters, that require data for creation. Instead, pass and update data as you requested from the backend. RecyclerView.Adapter is designed to be reused.
basically i am trying to create a recycler view and this the main activity
i am getting error at
recyclerView.adapter = adapter
package com.example.newsorning
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.RecyclerView
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.layoutManager = LinearLayoutManager(this)
val items = fetchData()
val adapter = NewsListAdapter(items)
recyclerView.adapter = adapter
}
private fun fetchData():ArrayList<String>{
val list = ArrayList<String>()
for (i in 0 until 100){
list.add("item $i")
}
return list
}
}
this is where i created adpater
//newsListadapter
package com.example.newsorning
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
class NewsListAdapter(private val item:ArrayList<String>):
RecyclerView.Adapter<NewsViewHolder>() {
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): NewsViewHolder {
val view = LayoutInflater.from(p0.context).inflate(R.layout.item_news,p0,false)
return NewsViewHolder(view)
}
override fun getItemCount(): Int {
return item.size
}
override fun onBindViewHolder(p0: NewsViewHolder, p1: Int) {
val currentItem = item[p1]
p0.titleView.text = currentItem
}
}
class NewsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleView : TextView = itemView.findViewById(R.id.titleView)
}
This may be due to you using the old, deprecated support library RecyclerView (android.support.v7) instead of the Jetpack RecyclerView (androidx). Maybe one of the libraries you're using still uses the old support library, so it was in your namespace and you accidentally selected it when choosing which class to import. You need to change the line
import android.support.v7.widget.RecyclerView
to
import androidx.recyclerview.widget.RecyclerView
in both of these files. And you need to enable Jetifier according to these directions.
[![Not able to implement Menbers`package com.manan.musicx
import android.content.Context
import android.media.AudioManager
import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Button
import android.widget.MediaController
import android.widget.SeekBar
class MainActivity : AppCompatActivity() {
lateinit var btnPlay: Button
lateinit var btnPause: Button
lateinit var btnStop: Button
lateinit var player: MediaPlayer
lateinit var sbVolume: SeekBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnPlay = findViewById(R.id.btnPlay)
btnPause = findViewById(R.id.btnPause)
btnStop = findViewById(R.id.btnStop)
sbVolume = findViewById(R.id.sbVolume)
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
player = MediaPlayer.create(this#MainActivity, R.raw.music)
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val curVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
sbVolume.setMax(maxVolume)
sbVolume.setProgress(curVolume)
sbVolume.setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener())
// Play function, play set on click listner
btnPlay.setOnClickListener {
player.start()
}
// Pause function, pause set on click listner
btnPause.setOnClickListener {
player.pause()
}
// Stop function, stop set on click listner
btnStop.setOnClickListener {
player.stop()
}
}
}`]1]1
You should write the Seekbar.OnSeekBarChangeListener as:
sbVolume.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
//onProgressChanged
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
//onStartTrackingTouch
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
//onStopTrackingTouch
}
})
or create a class implementing SeekBar.OnSeekBarChangeListener as:
private class MySeekbarChangeListener : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
//onProgressChanged
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
//onStartTrackingTouch
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
//onStopTrackingTouch
}
}
and use it as:
sbVolume.setOnSeekBarChangeListener(MySeekbarChangeListener())