Retrieve Variable from onBindViewHolder in Kotlin - android

I am trying to retrieve the variable value in my main class so, I can load this value into my database. I am not sure how to retrieve onBindViewHolder value in MainClas. I am able to display the item in the activity.
Complete Code requested by user.
class TestProjectMenuDetail() : AppCompatActivity() {
var itemName=""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView_main.setBackgroundColor(Color.WHITE)
recyclerView_main.layoutManager= LinearLayoutManager(this)
supportActionBar?.title=navBarTitle
fetchJSON()
}
//Retrieve value from OnBindViewHolder
fun setVariable(ItemName:String)
{
itemName=ItemName
}
}
private class MenuDetailListAdapter(val TestProjectMenudetails:Array<TestProjectMenuDetails>,context: Context): RecyclerView.Adapter<TestProjectDetailMenuViewHolder>()
{
private val TestVar:TestMenuDetail= context as TestMenuDetail
override fun onBindViewHolder(p0: TestProjectDetailMenuViewHolder, p1: Int) {
val TestProjectmenudetail=TestProjectMenudetails.get(p1)
p0?.customView?.itemname.text=TestProjectmenudetail.menu
TestVar.setVariable(TestProjectmenudetail.menu)
}
override fun getItemCount(): Int {
return TestProjectMenudetails.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): TestProjectDetailMenuViewHolder {
val layoutInflater= LayoutInflater.from(p0?.context)
val customView=layoutInflater.inflate(R.layout.activity_TestProject_menu_detail,p0,false)
return TestProjectDetailMenuViewHolder(customView)
}
}
#Suppress("DEPRECATION")
class TestProjectDetailMenuViewHolder(val customView: View, var Menus:TestProjectMenu?=null): RecyclerView.ViewHolder(customView)
{
companion object {
val DISHES_TITLE_NAME="ITEM_NAME"
val intialcount:Int=0
}
init {
customView.setOnClickListener {
}
}
fun AddClick()
{
val intent=Intent(customView.context,TestProjectMenuList::class.java)
customView.context.startActivity(intent)
}
}

In MainClas declare a variable to hold the value you want to pass from onBindViewHolder like activityVar and then create a method:
fun setVariable(myVariable: Int) {
activityVar = myVariable
}
replace Int with the proper data type.
Change your adapter class's header like this:
private class TestDetailListAdapter(context: Context, val ItemTestdetails:Array<ItemTestDetails>)
so you need to pass to it first the context of your activity by passing this
and inside onBindViewHolder add this:
val mainclas: MainClas = context as MainClas
mainclas.setVariable(variablename)
replace variablename with the name of the variable you want to pass

Related

null cannot be cast to non-null type RecyclerView with stateFlow

I got some categories from an api and trying to show them on a recycler view but it doesn't work for some reason.
Although the data appears correctly in the logcat, it is sent as null to the Category adapter.
This is the Main Activity (where I'm trying to show the data):
`
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val TAG = "MEALZ"
private lateinit var binding: ActivityMainBinding
private val viewModel:MealsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val adapter = CategoryAdapter(this)
binding.categoriesRv.adapter = adapter
viewModel.getMeals()
lifecycleScope.launch {
viewModel.categories.collect {
adapter.setData(it?.categories as List<Category>)
Log.d(TAG, "onCreate: ${it?.categories}")
}
}
}
}
`
This is Recycler Category Adapter :
`
class CategoryAdapter(private val context: Context?) :
RecyclerView.Adapter<CategoryAdapter.CategoryViewHolder>() {
private var categoryList: MutableList<Category?> = mutableListOf<Category?>()
inner class CategoryViewHolder(itemView: CategoryLayoutBinding) :
RecyclerView.ViewHolder(itemView.root) {
val name = itemView.categoryNameTv
val img = itemView.categoryIv
val des = itemView.categoryDesTv
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
val binding = CategoryLayoutBinding.inflate(LayoutInflater.from(context), parent, false)
return CategoryViewHolder(binding)
}
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
var category = categoryList[position]
holder.name.text = category?.strCategory
holder.des.text = category?.strCategoryDescription
Glide.with(context as Context).load(category?.strCategoryThumb).into(holder.img)
}
override fun getItemCount(): Int {
return categoryList.size
}
fun setData(CategoryList: List<Category>) {
this.categoryList.addAll(CategoryList)
notifyDataSetChanged() //to notify adapter that new data change has been happened to adapt it
}
}
`
This is the View Model class:
#HiltViewModel
class MealsViewModel #Inject constructor(private val getMealsUseCase: GetMeals): ViewModel() {
private val TAG = "MealsViewModel"
private val _categories: MutableStateFlow<CategoryResponse?> = MutableStateFlow(null)
val categories: StateFlow<CategoryResponse?> = _categories
fun getMeals() = viewModelScope.launch {
try {
_categories.value = getMealsUseCase()
} catch (e: Exception) {
Log.d(TAG, "getMeals: ${e.message.toString()}")
}
}
}
you create your _categories with null as initial value, so first value of categories flow will be null and only second one will contain fetched data. As a workaround, you can check that data is not null:
viewModel.categories.collect {
if (it != null) {
adapter.setData(it?.categories as List<Category>)
Log.d(TAG, "onCreate: ${it?.categories}")
}
}
or introduce some kind of "loading" state

Why do I get this error when creating an Intent and haw to pass data from Firebase that is in RecyclerView on second screen

I marked the parts that were added (added to code) after the moment
when the application was working, the data was successfully downloaded
from the database. I may be mistakenly trying to pass this information
to another screen. I tried to find a video that connects to the
database and forwards that data of recicler on another screen, but
without success, or they are in Java, which I understand less.
MySecondActivity
class BookDescription : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_book_description)
var books = intent.getSerializableExtra("noti") as Book //added to code
Glide.with(this).load(books.imageUrl).into(bookImg2)// added to code
nameTxt2.text = books.name //added to code
autorTxt2.text = books.writer //added to code
}
}
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var adapter : Adapter
private val viewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpRecyclerView()
}
private fun setUpRecyclerView(){
adapter = Adapter(this){
startBookDescription()
}
recycle.layoutManager = GridLayoutManager(this, 2)
recycle.adapter = adapter
observerData()
}
fun observerData(){
viewModel.fetchUserData().observe(this,Observer{
adapter.setListdata(it)
adapter.notifyDataSetChanged()
})
}
private fun startBookDescription(){
val intent = Intent (this, BookDescription::class.java )
startActivity(intent)
}
}
Class Adapter with inner class Holder
class Adapter(private val context: Context,
private val onItemCliked: () -> Unit ) : RecyclerView.Adapter<Adapter.Holder>() {
private var datalist = mutableListOf<Book>()
fun setListdata(data: MutableList<Book>){
datalist = data
}
inner class Holder(itemView : View) : RecyclerView.ViewHolder(itemView){
fun bindView(book: Book, onItemClicked: () -> Unit){
Glide.with(context).load(book.imageUrl).into(itemView.bookImg)
itemView.nameTxt.text = book.name
itemView.autorTxt.text= book.writer
itemView.setOnClickListener { onItemClicked.invoke() }
itemView.bookImg.setOnClickListener(View.OnClickListener { //added
val intent = Intent(context, BookDescription::class.java)//added to code
intent.putExtra("noti", book)//added to code
context.startActivity(intent)//added to code
})
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(context).inflate(R.layout.book_format, parent,
false )
return Holder(view)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val book = datalist[position]
holder.bindView(book, onItemCliked)
}
override fun getItemCount(): Int {
return if (datalist.size> 0){
datalist.size
}else{
0
}
}
}
The problem is here:
intent.putExtra("noti", book)
The book variable is of type Book, which is apparently neither a Parcelable or Serializable class. You must implement one of these two interfaces in the Book class in order to add it to an Intent or Bundle.
Assuming Book is made up of simple data types (String, Int, etc), then you can use the #Parcelize annotation to easily implement Parcelable. More here: https://developer.android.com/kotlin/parcelize
In your bindView() method, you have this block of code:
val intent = Intent(context, BookDescription::class.java)//added to code
intent.putExtra("noti", book)//added to code
context.startActivity(intent)//added to code
})
However, you don't actually do anything with this Intent; you start your activity from another place:
private fun startBookDescription(){
val intent = Intent (this, BookDescription::class.java )
startActivity(intent)
}
You will have to pass the Book instance to this method (via invoke(book)). This will require a corresponding type change to the click listener parameter of your adapter.

Using Kotlin to update ActionBar in Android RecyclerViewAdapter

I have a RecyclerViewAdapter for a shopping cart application written in Kotlin. When users change the quantity of items in their cart I would like to update the title of the ActionBar with the current cart total.
I've looked at various code using callbacks, listeners, and passing context to try to access the ActionBar from the RecyclerViewAdapter but I don't know if these are applicable to Kotlin nor do they make too much sense having always kept all of my code within the RecyclerViewAdapter (database lookups, writes, CardView updates, etc.)
Here is the highly abbreviated code for my calling code and the RecyclerViewAdapter (but I hope that I have all of the important components):
class ItemRecyclerViewAdapter(private val itemArray: MutableList<ItemObject>, private val contextABC: Context) : RecyclerView.Adapter<ItemRecyclerViewAdapter.ViewHolder>() {
inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
private val cardViewAdd: Button
init {
cardViewAdd = v.findViewById<View>(R.id.cardview_item_Add) as Button
// Add one to Qty Ordered
cardViewAdd.setOnClickListener { updateItemQty(adapterPosition, itemArray[adapterPosition].qtyOrdered + 1, v) }
}
}
private fun updateItemQty(arrayPosition: Int, pQtyOrdered: Int, v: View) {
itemArray[arrayPosition].qtyOrdered = pQtyOrdered
notifyItemChanged(arrayPosition)
}
}
class CartActivity : BaseActivity() {
internal var cartItemsList: MutableList<ItemObject> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.cart_master)
cartItemsList = DatabaseAdapter.cartItems
cart_RecyclerView.setHasFixedSize(true)
cart_RecyclerView.layoutManager = LinearLayoutManager(this)
rvAdapter = ItemRecyclerViewAdapter(cartItemsList, this)
cart_RecyclerView.adapter = rvAdapter
}
}
Can anyone get me going in the correct direction? I certainly appreciate the help!
[Edit]
With the great help of Birju Vachhani, here is my working code:
class ItemRecyclerViewAdapter(private val itemArray: MutableList<ItemObject>,
private val contextABC: Context, val updateActionBarTitle: (ItemObject) -> Unit = {})
: RecyclerView.Adapter<ItemRecyclerViewAdapter.ViewHolder>() {
inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
private val cardViewAdd: Button
init {
cardViewAdd = v.findViewById<View>(R.id.cardview_item_Add) as Button
// Add one to Qty Ordered
cardViewAdd.setOnClickListener { updateItemQty(adapterPosition, itemArray[adapterPosition].qtyOrdered + 1, v) }
}
}
private fun updateItemQty(arrayPosition: Int, pQtyOrdered: Int, v: View) {
itemArray[arrayPosition].qtyOrdered = pQtyOrdered
notifyItemChanged(arrayPosition)
updateActionBarTitle(itemArray[arrayPosition])
}
}
class CartActivity : BaseActivity() {
internal var cartItemsList: MutableList<ItemObject> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.cart_master)
cartItemsList = DatabaseAdapter.cartItems
cart_RecyclerView.setHasFixedSize(true)
cart_RecyclerView.layoutManager = LinearLayoutManager(this)
rvAdapter = ItemRecyclerViewAdapter(cartItemsList, this){item->
supportActionBar?.setTitle("hi")
}
cart_RecyclerView.adapter = rvAdapter
}
}
You can do it by using Kotlin's lambda functions like this:
Adapter:
class ItemRecyclerViewAdapter(
private val itemArray: MutableList<ItemObject>,
val onItemAdded: (ItemObject) -> Unit = {}
) :
RecyclerView.Adapter<ItemRecyclerViewAdapter.ViewHolder>() {
override fun getItemCount(): Int {
return itemArray.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.cardViewAdd.setOnClickListener {
updateItemQty(position, itemArray[position].qtyOrdered + 1)
onItemAdded(itemArray[position])
}
}
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val cardViewAdd: Button = v.findViewById(R.id.cardview_item_Add)
fun bind(item:ItemObject){
// bind your data with view here
}
}
fun updateItemQty(arrayPosition: Int, pQtyOrdered: Int) {
itemArray[arrayPosition].qtyOrdered = pQtyOrdered
notifyItemChanged(arrayPosition)
}
}
In Your Activity:
class CartActivity : BaseActivity() {
internal var cartItemsList: MutableList = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.cart_master)
cartItemsList = DatabaseAdapter.cartItems
cart_RecyclerView.setHasFixedSize(true)
cart_RecyclerView.layoutManager = LinearLayoutManager(this)
rvAdapter = ItemRecyclerViewAdapter(cartItemsList, this){item->
// here you can access your ActionBar as perform actions on it
}
cart_RecyclerView.adapter = rvAdapter
}
}

How to get a variable from another class in kotlin?

I want to get a variable from an activity and use it in another class.
This variable will be filled by an user in a editText that is called editTextSerie
override fun searchSeries(listener: OnDataListener) {
val retrofit = Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://api.themoviedb.org/3/")
.build()
val client = retrofit.create(MovieDBApiInterface::class.java)
val objetoClasse1 = SearchActivity()
var nomeS = objetoClasse1.editTextSerie.text.toString().trim()
val responseCall = client.searchSeries("API_KEY", "pt-BR", nomeS)
responseCall.enqueue(object : Callback<AllSeriesResponse> {
override fun onResponse(call: Call<AllSeriesResponse>?, response1: Response<AllSeriesResponse>?) {
listener.onSuccess(response1!!.body()!!.results)
}
override fun onFailure(call: Call<AllSeriesResponse>?, t: Throwable?) {
listener.onFailure(t!!.message.toString())
}
})
}
This function "searchSeries" is from the class "Series".
I want to get the "editTextSerie" from another class called "Search Activity",
so i created the variable "nomeS" to receive the value of it.
class SearchActivity : AppCompatActivity() {
var botaoSearch: AppCompatImageButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
botaoSearch = findViewById(R.id.btn_search)
botaoSearch?.setOnClickListener {
var nomeSerie = editTextSerie.text.toString().trim()
}
}
}
I want to receive this value (value of editTextSerie comes from the XML of SearchActivity ) and use it at responseCall with the "nomeS" variable
What is OnDataListener? Not really sure it is interface or abstract class, so I' ll write some pseudo code.
First change your function searchSeries's params to
searchSeries(text: String, listener: OnDataListener)
So in the class Series, you can get the data in your function searchSeries:
override fun searchSeries(text: String, listener: OnDataListener) {
// ...
// you can get the "text" string
}
Then edit your SearActivity's listener:
class SearchActivity : AppCompatActivity() {
var botaoSearch: AppCompatImageButton? = null
// create class "Series"
val series = Series()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
botaoSearch = findViewById(R.id.btn_search)
botaoSearch?.setOnClickListener {
var nomeSeries = editTextSerie.text.toString().trim()
searchSeries(nomeSeries)
}
}
private fun searchSeries(text: String) {
series.searchSeries(text, object : OnDataListener {
override onSuccess(a0: ...) {
}
override onFailure(message: String) {
}
})
}
}
If OnDataListener is a abstract class:
series.searchSeries(text, object : OnDataListener() {
override onSuccess(a0: ...) {
}
override onFailure(message: String) {
}
})

Navigation Bar Title is Empty in Kotlin

I am trying to display navigation bar title in my TestProjectList class activity but the value is empty so, I am not able to see the Navigation bar tile. I am not sure why its showing empty Value. Your help is appreciated.
Model Class:
class TestProject(val name: String,val location: String)
Main Class:
private class ItemDetailAdapter(val TestProjectList:Array<TestProject>): RecyclerView.Adapter<ItemDetailViewHolder>()
{
override fun onBindViewHolder(p0: ItemDetailViewHolder, p1: Int) {
val TestProject=TestProjectList.get(p1)
p0?.customView?.TestProjectName?.text=TestProject.name
val TestProjectPicture=p0?.customView?.itemPicture
Picasso.get().load(TestProject.location).into(TestProjectPicture)
}
override fun getItemCount(): Int {
return TestProjectList.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ItemDetailViewHolder {
val layoutInflater=LayoutInflater.from(p0?.context)
val customView=layoutInflater.inflate(R.layout.items_details,p0,false)
return ItemDetailViewHolder(customView)
}
}
class ItemDetailViewHolder(val customView:View,var Title: TestProject?=null):RecyclerView.ViewHolder(customView)
{
companion object {
val ITEM_TITLE_KEY="TestProject"
}
init {
customView.setOnClickListener {
val intent= Intent(customView.context,TestProjectMenuList::class.java)
intent.putExtra(ITEM_TITLE_KEY,Title?.name)
print("Printting Title :$Title?.name")
println("Hello Test $ITEM_TITLE_KEY")
customView.context.startActivity(intent)
println("Test")
}
}
TestProjectList Class:
val navBarTitle=intent.getStringExtra(MainClass.ItemDetailViewHolder.ITEM_TITLE_KEY)
supportActionBar?.title=navBarTitle
When you are creating your viewholder in the adapter return ItemDetailViewHolder(customView) you aren't passing any value for the parameter Title. You aren't setting it up latter either, but you are populating the intent with intent.putExtra(ITEM_TITLE_KEY,Title?.name). In this case the value you will always retrieve from the intent will be null.
Model Class:
class TestProject(val name: String,val location: String)
Main Class:
private class ItemDetailAdapter(val TestProjectList:Array<TestProject>): RecyclerView.Adapter<ItemDetailViewHolder>()
{
override fun onBindViewHolder(p0: ItemDetailViewHolder, p1: Int) {
val TestProject=TestProjectList.get(p1)
p0?.customView?.TestProjectName?.text=TestProject.name
val TestProjectPicture=p0?.customView?.itemPicture
Picasso.get().load(TestProject.location).into(TestProjectPicture)
//Below code solved the Title Problem
p0?.Title=TestProject
}
override fun getItemCount(): Int {
return TestProjectList.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ItemDetailViewHolder {
val layoutInflater=LayoutInflater.from(p0?.context)
val customView=layoutInflater.inflate(R.layout.items_details,p0,false)
return ItemDetailViewHolder(customView)
}
}
class ItemDetailViewHolder(val customView:View,var Title: TestProject?=null):RecyclerView.ViewHolder(customView)
{
companion object {
val ITEM_TITLE_KEY="TestProject"
}
init {
customView.setOnClickListener {
val intent= Intent(customView.context,TestProjectMenuList::class.java)
intent.putExtra(ITEM_TITLE_KEY,Title?.name)
print("Printting Title :$Title?.name")
println("Hello Test $ITEM_TITLE_KEY")
customView.context.startActivity(intent)
println("Test")
}
}
TestProjectList Class:
val navBarTitle=intent.getStringExtra(MainClass.ItemDetailViewHolder.ITEM_TITLE_KEY)
supportActionBar?.title=navBarTitle

Categories

Resources