Recycler View with duplicate rows after refreshing activity - android

After doing some research i found out that i need to clear the list that i am using each time i refresh the activity, after trying it to refresh the list at different places, the result remains the same and i have decided to give my brain a break and ask for help. The refreshing takes place on the PostDialogFragment class
i have circled the recycler view causing the issue
PostAdapter
package com.example.osrscritic.view
import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.osrscritic.databinding.PostRowItemBinding
import com.example.osrscritic.model.Skillvalue
import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.QueryDocumentSnapshot
import com.google.firebase.firestore.QuerySnapshot
class PostsAdapter: RecyclerView.Adapter<PostsViewHolder>() {
val postsMutableList : MutableList<DocumentSnapshot> = mutableListOf()
fun setPostsList(statsList: List<DocumentSnapshot>) {
postsMutableList.addAll(statsList)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostsViewHolder {
val binding = PostRowItemBinding.inflate(LayoutInflater.from(parent.context)
,parent,false)
return PostsViewHolder(binding)
}
override fun onBindViewHolder(holder: PostsViewHolder, position: Int) {
val post = postsMutableList[position]
holder.binding.tvCritic.text = post["critic"].toString()
val posts = post["posts"] as List<String>
var count = 0;
holder.binding.tvActualPost.movementMethod = ScrollingMovementMethod()
for(p in posts) {
holder.binding.tvActualPost.append("${count}: " + p + "\n")
count++
}
}
override fun getItemCount() = postsMutableList.size
}
class PostsViewHolder(val binding: PostRowItemBinding) : RecyclerView.ViewHolder(binding.root)
DisplayUserActivty
package com.example.osrscritic
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.osrscritic.databinding.ActivityDisplayUserBinding
import com.example.osrscritic.view.PostDialogFragment
import com.example.osrscritic.view.PostsAdapter
import com.example.osrscritic.view.StatsAdapter
import com.example.osrscritic.viewmodel.DisplayUserViewModel
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.DocumentSnapshot
import org.koin.androidx.viewmodel.ext.android.viewModel
class DisplayUserActivity : AppCompatActivity() {
private val displayUserViewModel : DisplayUserViewModel by viewModel()
lateinit var binding: ActivityDisplayUserBinding
val statsAdapter = StatsAdapter()
val postsAdapter = PostsAdapter()
lateinit var critiquing: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val extras = intent.extras
critiquing = extras?.getString("c")!!
binding = ActivityDisplayUserBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.rsStatsRv.adapter = statsAdapter
binding.rsStatsRv.layoutManager = LinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false)
binding.rvPosts.adapter = postsAdapter
binding.rvPosts.layoutManager = LinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false)
configureObservers()
displayUserViewModel.getFirebaseRef()
displayUserViewModel.getOSRSPlayer()
binding.ivWritePost.setOnClickListener {
PostDialogFragment.newInstance(critiquing).show(supportFragmentManager
, PostDialogFragment.KEY)
}
}
private fun configureObservers() {
displayUserViewModel.displayUserLiveData.observe(this, {
statsAdapter.setStatsList(it.skillvalues)
binding.tvRunescapeName.text = String.format("Account Name: %s", it.name)
binding.tvCombat.text = String.format("Combat Level: %s", it.combatlevel.toString())
})
displayUserViewModel.loadingState.observe(this, {
when(it){
true -> binding.pgBar.visibility = View.VISIBLE
false -> binding.pgBar.visibility = View.GONE
}
})
displayUserViewModel.errorData.observe(this, {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
})
displayUserViewModel.displayUserPostsLiveData.observe(this, {
postsAdapter.postsMutableList.clear()
it.document(critiquing).collection("posts").get()
.addOnSuccessListener {
postsAdapter.setPostsList(it.documents)
}
})
}
}
PostDialogFragment
package com.example.osrscritic.view
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import androidx.fragment.app.DialogFragment
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.EditText
import android.view.Gravity
import androidx.core.app.ActivityCompat.recreate
import com.example.osrscritic.DisplayUserActivity
import com.example.osrscritic.viewmodel.DisplayUserViewModel
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FieldPath
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.SetOptions
import org.koin.androidx.viewmodel.ext.android.viewModel
class PostDialogFragment: DialogFragment() {
private val displayUserViewModel : DisplayUserViewModel by viewModel()
val currentUser = FirebaseAuth.getInstance().currentUser
lateinit var critiquing: String
val postsAdapter = PostsAdapter()
companion object {
const val KEY: String = "KEY2"
//lateinit var param: String
fun newInstance(text: String): PostDialogFragment {
val args = Bundle()
val postDialogFragment = PostDialogFragment()
args.putString(KEY, text)
postDialogFragment.arguments = args
return postDialogFragment
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
critiquing = arguments?.getString(KEY)!!
Log.d("*******8", critiquing)
val builder = AlertDialog.Builder(activity)
val layout = LinearLayout(activity)
val parms = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
layout.orientation = LinearLayout.VERTICAL
layout.layoutParams = parms
layout.gravity = Gravity.CLIP_VERTICAL
layout.setPadding(2, 2, 2, 2)
val tv = TextView(activity)
tv.text = "Text View title"
tv.setPadding(40, 40, 40, 40)
tv.gravity = Gravity.CENTER
tv.textSize = 20f
val et = EditText(activity)
var etString = ""
et.addTextChangedListener(object: TextWatcher{
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(p0: Editable?) {
Log.d("etText", et.text.toString())
etString = et.text.toString()
}
})
val tv1 = TextView(activity)
tv1.text = "Input Student ID"
val tv1Params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
tv1Params.bottomMargin = 5
//layout.addView(tv1, tv1Params)
layout.addView(
et,
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
)
//param = arguments?.getString(KEY)!!
builder.setTitle("New Post")
builder.setView(layout).setPositiveButton("Done", object: DialogInterface.OnClickListener{
override fun onClick(p0: DialogInterface?, p1: Int) {
activity?.let {
displayUserViewModel.displayUserPostsLiveData.observe(it, {
val post : MutableMap<String, Any> = mutableMapOf()
val posts : MutableList<String> = mutableListOf()
posts.add(etString)
post["critic"] = currentUser?.email!!
post["posts"] = posts
if(it.document(critiquing).collection("posts").document().equals(null)) {
it.document(critiquing).collection("posts")
.document(currentUser?.email!!).set(post)
}
it.document(critiquing).collection("posts")
.document(currentUser?.email!!).update("posts", FieldValue.arrayUnion(etString))
})
}
activity!!.finish()
activity!!.overridePendingTransition( 0, 0);
startActivity(activity!!.intent)
//trying to clear the list here once being refreshed
postsAdapter.postsMutableList.clear()
activity!!.overridePendingTransition( 0, 0);
}
})
builder.setNegativeButton("Cancel", object:DialogInterface.OnClickListener{
override fun onClick(p0: DialogInterface?, p1: Int) {
dismiss()
}
})
return builder.create()
}
}

Overriding those usually solves this kind of issues:
getItemId: return long that uniquely identifies every item. For example, you could use an autoincrementing variable id for each new item object, or some hashcode. This should be paired with setHasStableIds(true). (By default it returns RecyclerView.NO_ID which value is -1).
This helps to make sure RecyclerView e.g. identifies items properly, knows when OnBindViewHolder must be called or to return precise values on getAdapterPosition().
getItemCount: return an int with current number of items. Usually the Array.size() where the data is. (By default it returns 0).
getItemType: If there's multiple view types, return a unique number for each type of ViewHolder (usually starting with 0). (By default it returns 0).

Related

How can pass data from recyclerview to New Activity

hi i want parse my data in to new activity .
I did this but it doesn't work properly and it doesn't transfer data
You can see my code below
my adapter
ItemViewAdapter.kt
package com.example.app.adapter
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.app.R
import com.example.app.models.products.Results
import com.example.app.ui.home.DescriptionActivity
import java.security.AccessController.getContext
class ItemViewAdapter(private val context: Context) :
RecyclerView.Adapter<ItemViewAdapter.MyViewHolder>() {
private var itemList: MutableList<Results> = mutableListOf()
private var page: Int = 0
fun setListItem(item: List<Results>) {
itemList.addAll(item)
notifyItemRangeInserted(page * 10, (page.plus(1) * 10) - 1)
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val title = view.findViewById<TextView>(R.id.tvTitle)
private val code = view.findViewById<TextView>(R.id.tvNumber)
private val quantity = view.findViewById<TextView>(R.id.tvQuantity)
private val price = view.findViewById<TextView>(R.id.tvPrice)
private val available = view.findViewById<TextView>(R.id.tvAvailable)
private val category = view.findViewById<TextView>(R.id.tvCategory)
fun bind(item: Results) {
title.text = item.name
code.text = item.code.toString()
quantity.text = item.quantities[0].quantity.toString()
price.text = item.prices[0].price.toString()
available.text = item.active.toString()
category.text = item.category?.name
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return itemList.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val data = itemList[position]
holder.bind(data)
holder.itemView.setOnClickListener {
val intent = Intent(context, DescriptionActivity::class.java)
intent.putExtra("title", getItemId(position))
intent.putExtra("code", getItemId(position))
intent.putExtra("quantity", getItemId(position))
intent.putExtra("price", getItemId(position))
intent.putExtra("category", getItemId(position))
intent.putExtra("info", getItemId(position))
context.startActivity(intent)
}
}
}
and my description activity
Description.kt
package com.example.app.ui.home
import android.graphics.Color
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.WindowInsetsController
import android.view.WindowManager
import com.example.app.R
import com.example.app.databinding.ActivityDescriptionBinding
#Suppress("DEPRECATION")
class DescriptionActivity : AppCompatActivity() {
private lateinit var binding: ActivityDescriptionBinding
override fun onCreate(savedInstanceState: Bundle?) {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
statusBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this#DescriptionActivity.window.decorView.windowInsetsController
?.setSystemBarsAppearance(
0,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
}
}
}
super.onCreate(savedInstanceState)
binding = ActivityDescriptionBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
getItemIntent()
}
private fun getItemIntent(){
val title = intent.getStringExtra("title")
val code = intent.getStringExtra("code")
val category = intent.getStringExtra("category")
val info = intent.getStringExtra("info")
val price = intent.getStringExtra("price")
val quantity = intent.getStringExtra("quantity")
setData(title,code,category,info,price,quantity)
}
private fun setData(title: String?, code: String?, category: String?, info: String?, price: String?, quantity: String?) {
binding.tvTitle.setText(title)
binding.tvCode.setText(code)
binding.tvCategory.setText(category)
binding.tvDescription.setText(info)
binding.tvPrice.setText(price)
binding.tvQuantity.setText(quantity)
}
}
But the data is not transferred.
If anyone can help me improve the code or offer me a sample code done with api so I can see.
When you set the data, you call getItemId(position) for every single line. This returns a long, not a string (and you put the same long for each attribute, which makes no sense)
intent.putExtra("title", getItemId(position))
intent.putExtra("code", getItemId(position))
but when you retrieve it, you look for a string
val title = intent.getStringExtra("title")
val code = intent.getStringExtra("code")
If you want to get a string, you need to put a string, probably like this (get the item for that row, then put its string attributes):
val data = itemList[position]
//...
intent.putExtra("title", data.name)
intent.putExtra("code", data.code.toString())
or if you want to get the text off the displayed views (have to make them non-private in the binding class):
intent.putExtra("title", binding.title.text.toString())
intent.putExtra("code", binding.code.text.toString())
If you need to pass the entire object or most of its properties, you can add the library
implementation 'com.google.code.gson:gson:2.8.5' //Maybe the version is not the most recent, I just copied and pasted the implementation from the internet
And convert your object to a Json and pass it as a string
intent.putExtra("data", Gson().toJson(data))
and to receive it
val data = intent.getStringExtra("data", null)?.let {
Gson().fromJson(it, Results::Java.class)
}

listOf() unresolved reference

I am creating a memory game application. This is my code. This first code is MemoryBoardAdapter.
```package com.example.memory
import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.recyclerview.widget.RecyclerView
import androidx.cardview.widget.CardView
import com.example.memory.models.BoardSize
import java.lang.Math.min
class MemoryBoardAdapter(private val Context: Context, private val boardSize: BoardSize, private val cardImages: List<Int>) :
RecyclerView.Adapter<MemoryBoardAdapter.ViewHolder>() {
companion object {
private const val MARGIN_SIZE = 10
private const val TAG = "MemoryBoardAdapter"
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardWidth = parent.width/boardSize.getWidth() - (2 * MARGIN_SIZE)
val cardHeight = parent.height/boardSize.getHeight() - (2 * MARGIN_SIZE)
val cardSideLength = min(cardWidth, cardHeight)
val view = LayoutInflater.from(Context).inflate(R.layout.memory_card, parent, false)
val layoutParams = view.findViewById<CardView>(R.id.cardView).layoutParams as ViewGroup.MarginLayoutParams
layoutParams.width = cardSideLength
layoutParams.height = cardSideLength
layoutParams.setMargins(MARGIN_SIZE, MARGIN_SIZE, MARGIN_SIZE, MARGIN_SIZE)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
override fun getItemCount() = boardSize.numCards
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val imageButton = itemView.findViewById<ImageButton>(R.id.imageButton)
fun bind(position: Int) {
imageButton.setImageResource(cardImages[position])
imageButton.setOnClickListener{
Log.i(TAG,"Clisked on position $position")
}
}
}
}```
This is the main activity.
```package com.example.memory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.memory.models.BoardSize
import com.example.memory.utils.DEFAULT_ICONS
class MainActivity : AppCompatActivity() {
private lateinit var rvBoard: RecyclerView
private lateinit var tvNumMoves: TextView
private lateinit var tvNumPairs: TextView
private var boardSize: BoardSize = BoardSize.MEDIUM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rvBoard = findViewById(R.id.rvBoard)
tvNumMoves = findViewById(R.id.tvNumMoves)
tvNumPairs = findViewById(R.id.tvNumPairs)
val chosenImages = DEFAULT_ICONS.shuffled().take(boardSize.getNumPairs())
val randomizedImages = (chosenImages + chosenImages).shuffled()
rvBoard.adapter = MemoryBoardAdapter(this,boardSize,randomizedImages)
rvBoard.setHasFixedSize(true)
rvBoard.layoutManager = GridLayoutManager(this,boardSize.getWidth())
}
}```
My error occurred in this Constants.kt file. Here the method listOf() is not been implemented and showing error. It is asking to rename the reference or to create a function. If I create a function it is showing error in main activity.
```package com.example.memory.utils
import com.example.memory.R
val DEFAULT_ICONS = listOf(
R.drawable.cloud,
R.drawable.dining,
R.drawable.eye,
R.drawable.face,
R.drawable.flight,
R.drawable.friends,
R.drawable.heart,
R.drawable.key,
R.drawable.phone,
R.drawable.rewind,
R.drawable.star,
R.drawable.volume
)```
How to resolve this error??

RecyclerView: No adapter attached; skipping layout - kotlin

I know this problem has been posted but all seemed to be posted in java code. I'm just trying to implement a small example recyclerView. Not sure what I'm doing incorrect, any insight would be great, TIA.
MainActivity.kt
package com.wildcardev.androidtest1
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager
import com.wildcardev.androidtest1.adapters.RecyclerAdapter
import com.wildcardev.androidtest1.databinding.ActivityMainBinding
import com.wildcardev.androidtest1.models.TestDataObj
fun addDataToList(arrayList:ArrayList<TestDataObj>): ArrayList<TestDataObj>{
arrayList.add(TestDataObj("tes1", "test2", "test3"))
arrayList.add(TestDataObj("test2", "testt2", "testt2"))
arrayList.add(TestDataObj("tes12", "test22", "test32"))
arrayList.add(TestDataObj("tes13", "test23", "test33"))
arrayList.add(TestDataObj("tes14", "test24", "test34"))
arrayList.add(TestDataObj("tes15", "test25", "test35"))
return arrayList
}
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
val testList: ArrayList<TestDataObj> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
val list = addDataToList(testList)
Log.d("TEST1","$list")
Log.d("TEST1","${binding.recyclerView}")
binding.recyclerView.layoutManager = LinearLayoutManager(this#MainActivity)
val adapter = RecyclerAdapter(list)
binding.recyclerView.adapter = adapter
}
}
TestDataObj.kt
package com.wildcardev.androidtest1.models
data class TestDataObj(
val title: String,
val description: String,
val date: String
)
RecyclerAdapter.kt
package com.wildcardev.androidtest1.adapters
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.wildcardev.androidtest1.R
import com.wildcardev.androidtest1.models.TestDataObj
class RecyclerViewHolder(view: View): RecyclerView.ViewHolder(view){
var title: TextView = view.findViewById(R.id.title)
var description: TextView = view.findViewById(R.id.description)
var date: TextView = view.findViewById(R.id.date)
}
class RecyclerAdapter(private var list: ArrayList<TestDataObj>): RecyclerView.Adapter<RecyclerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
Log.d("onCreateView", "INHOLDERcreate")
val view = LayoutInflater.from(parent.context).inflate(R.layout.activity_main, parent, false)
return RecyclerViewHolder(view)
}
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
Log.d("ADAPTER", "INBINDfunc")
val item = list[position]
holder.title.text = item.title
holder.description.text = item.description
holder.date.text = item.date
}
override fun getItemCount(): Int {
Log.d("COUNT",".getItemCountcalled")
return list.size
}
}
Try
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
I fixed my issue. When using viewBinding/dataBinding, you need to pass dataBinding to the viewHolder. Unfortunately I did not get this as I was just following tutorials that are using synthetic imports. So I changed my Adapter code to the following
RecyclerAdapter.kt
package com.wildcardev.androidtest1.adapters
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.wildcardev.androidtest1.models.TestDataObj
import com.wildcardev.androidtest1.databinding.ListItemBinding
class RecyclerViewHolder(viewDataBinding: ListItemBinding): RecyclerView.ViewHolder(viewDataBinding.root){
var title: TextView? = viewDataBinding.title
var description: TextView? = viewDataBinding.description
var date: TextView? = viewDataBinding.date
}
class RecyclerAdapter(private var list: ArrayList<TestDataObj>): RecyclerView.Adapter<RecyclerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
Log.d("onCreateView", "INHOLDERcreate")
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return RecyclerViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
Log.d("ADAPTER", "INBINDfunc $holder")
val item = list[position]
holder.title?.text = item.title
holder.description?.text = item.description
holder.date?.text = item.date
}
override fun getItemCount(): Int {
Log.d("COUNT",".getItemCountcalled ${list.size}")
return list.size
}
}

intent.putExtra: None of the following functions can be called with the arguments supplied

I'm trying to pass data from one activity to another through an intent with putExtra. The thing is, I get the error that says: None of the following functions can be called with the arguments supplied.
I'm not sure why it's not working, since the variable that I'm referencing is in the viewholder. Any clue as to what's going on and how to fix it would help a lot.
This is my recycler adapter:
package com.example.newsapp
import android.content.Intent
import android.icu.text.CaseMap
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat.startActivity
import androidx.recyclerview.widget.RecyclerView
import com.example.newsapp.databinding.NewsItemBinding
import com.squareup.picasso.Picasso
class RecyclerAdapter (
private var Titles: List<String>,
private var Images: List<String>,
private var Authors: List<String>,
private var Descriptions: List<String>
) : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>(){
inner class ViewHolder(
view: View
): RecyclerView.ViewHolder(view){
private val binding = NewsItemBinding.bind(view)
val itemTitle: TextView = binding.tvTitle
val itemImage: ImageView = binding.ivNewsImage
fun bind(urlToImage:String){
Picasso.get().load(urlToImage).into(binding.ivNewsImage)
}
init {
itemImage.setOnClickListener{
val intent = Intent(view.context, PostActivity::class.java)
intent.putExtra("title",itemTitle)
view.context.startActivity(intent)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.news_item, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemTitle.text = Titles[position]
val itemImage = Images[position]
holder.bind(itemImage)
}
override fun getItemCount(): Int {
return Titles.size
}
}
The part with the issue is this one:
init {
itemImage.setOnClickListener{
val intent = Intent(view.context, PostActivity::class.java)
intent.putExtra("title",itemTitle) view.context.startActivity(intent)
}
}
This is my main activity:
package com.example.newsapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.newsapp.databinding.ActivityMainBinding
import kotlinx.coroutines.*
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.lang.Exception
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var adapter: RecyclerAdapter
private val newsTitles = mutableListOf<String>()
private val newsImages = mutableListOf<String>()
private val newsAuthors = mutableListOf<String>()
private val newsDescriptions = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
makeAPIRequest()
}
private fun initRecyclerView() {
adapter = RecyclerAdapter( newsTitles, newsImages)
binding.rvNews.layoutManager = LinearLayoutManager(this)
binding.rvNews.adapter = adapter
}
private fun addToList(title:String, image:String, author:String, description:String){
newsTitles.add(title)
newsImages.add(image)
newsAuthors.add(author)
newsDescriptions.add(description)
}
private fun makeAPIRequest() {
val api = Retrofit.Builder()
.baseUrl("https://newsapi.org/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(APIService::class.java)
GlobalScope.launch(Dispatchers.IO){
val response = api.getNews()
val posts = response.body()
try{
if (posts != null) {
for(art in posts.Articles){
Log.i("Main Activity", "Result = $art")
addToList(art.Title,art.urlToImage, art.Author, art.Description)
}
}
withContext(Dispatchers.Main){
initRecyclerView()
}
} catch (e:Exception){
Log.e("Main Activity", e.toString())
}
}
}
}
intent.putExtra("title",itemTitle)
Here, itemTitle is a TextView. You cannot pass a TextView between activities. You could switch to:
intent.putExtra("title",itemTitle.text)
...to put the text from the TextView into the extra.
Also, you might want to consider whether these should be separate activities or just two fragments (or composables) in a single activity.

ViewHolder with onClicklistener Issue with UninitializedPropertyAccessException

I'm a beginner so please bare with me.
I have a viewholder that has an onclicklistener.
The aim of the click is to send a Url string into another fragment using Jetpack Navigation (hopefully i did it right)
the Url is being created within the dataclass itself.
but i keep getting this error:
kotlin.UninitializedPropertyAccessException: lateinit property galleryItem has not been initialized
I tried working around using companion object and other ways, nothing worked... is there a solution for this?
here is the view holder and data class
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.core.os.bundleOf
import androidx.navigation.Navigation
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.saheralsous.android.R
import com.saheralsous.android.database.remote.model.PagingData
class RecyclerViewPhotoAdapter() :
PagingDataAdapter<PagingData.GalleryItem, PhotoHolder>(
diffCallback = DiffCallback
) {
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
getItem(position)?.let {
holder.bind(it)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.photo_item_view,parent,false)
return PhotoHolder(view)
}
}
class PhotoHolder(view: View):
RecyclerView.ViewHolder(view), View.OnClickListener {
private val imageButtom: ImageButton = view.findViewById(R.id.ImageButton)
private lateinit var galleryItem: PagingData.GalleryItem //page. 594
#SuppressLint("ResourceType")
fun bind(galleryItem : PagingData.GalleryItem){
/*
idTextView.text = galleryItem.id
ownerTextView.text = galleryItem.owner
titleTextView.text = galleryItem.title
urlTextView.text = galleryItem.url
*/
galleryItem.url.let { url ->
Glide.with(itemView)
.load(url)
.override(350,350)
.into(imageButtom)
}
}
init {
imageButtom.setOnClickListener(this)
}
override fun onClick(v: View?) {
println("item was clicked")
val bundle = bundleOf("url" to galleryItem.photoPageUri ) <-- here is the issue
Navigation.findNavController(v!!).navigate(
R.id.action_photoGalleryFragment_to_photoPageFragment,
bundle)
}
}
object DiffCallback : DiffUtil.ItemCallback<PagingData.GalleryItem>() {
override fun areItemsTheSame(oldItem: PagingData.GalleryItem, newItem: PagingData.GalleryItem): Boolean {
// Id is unique.
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: PagingData.GalleryItem, newItem: PagingData.GalleryItem): Boolean {
return oldItem == newItem
}
}
data class
import android.net.Uri
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
data class PagingData(
val total : Int =0,
val Page: Int = 0,
val photos : List<GalleryItem>
){
val endOfPage = total == Page
#Keep
data class GalleryItem(
#SerializedName("id")
val id: String,
#SerializedName("owner")
val owner: String,
#SerializedName("title")
var title: String,
#SerializedName("url_s")
val url: String) {
val photoPageUri : Uri <-- here is the value
get() {
return Uri.parse("https://www.flickr.com/photos/")
.buildUpon()
.appendPath(owner)
.appendPath(id)
.build()
}
}
}
As the error states, you haven't initialised the galleryItem instance variable in your PhotoHolder. Add this inside your bind method:
this.galleryItem = galleryItem

Categories

Resources