Firstly apologies for what i'm sure is a newby mistake somewhere. Just started learning Kotlin and likely thrown myself in the deep end, but i've got some understanding of what is happening.
My Context:
I'm using a button to load JSON data from a file on my server, before displaying it into a Recycler View.
The data loads fine into the recycler view list item nicely, but:
My Problem:
In order for the recyclerview to display the JSON data (which i'm getting with Volley), the button needs pressing twice.
On first press, nothing happens. Second press, the data updates.
I change the json on my server, press the button - nothing happens - recyclerview remains unchanged.
Press it again, and the recyclerview updates.
MainClass.kt
class MainActivity : AppCompatActivity() {
private var requestQueue: RequestQueue? = null
val serviceList: ArrayList<Service> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.parsebtn)
val recycleradapter = Itemadapter(this, serviceList)
val recyclerview = findViewById<RecyclerView>(R.id.recycler)
recyclerview.adapter = recycleradapter
recyclerview.setHasFixedSize(true)
button.setOnClickListener {
jsonParse()
recycleradapter.notifyDataSetChanged()
}
requestQueue = Volley.newRequestQueue(this)
}
private fun jsonParse() {
val url = "MYSERVERIP/test.json"
val request = JsonObjectRequest(Request.Method.GET, url, null, Response.Listener {
response ->try {
serviceList.clear()
val jsonArray = response.getJSONArray("services")
for (i in 0 until jsonArray.length()) {
val serviceRow = jsonArray.getJSONObject(i)
val serviceOrigin = serviceRow.getString("origin")
val serviceDestination = serviceRow.getString("destination")
val serviceID = serviceRow.getString("train uid")
serviceList.add(Service(serviceOrigin, serviceDestination, serviceID))
}
} catch (e: JSONException) {
e.printStackTrace()
}
}, {
error -> error.printStackTrace()
})
requestQueue?.add(request)
}
}
Any help much appreciated. Thanks.
Adapter Class:
class Itemadapter (private val context: Context, private val dataset: ArrayList<Service>):
RecyclerView.Adapter<Itemadapter.ItemViewHolder>()
{
class ItemViewHolder(private val view: View): RecyclerView.ViewHolder(view){
val txtSOrigin: TextView = view.findViewById(R.id.serviceOrigin)
val txtSDest: TextView = view.findViewById(R.id.serviceDestination)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val adapterLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return ItemViewHolder(adapterLayout)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = dataset[position]
holder.txtSOrigin.text = item.sOrigin
holder.txtSDest.text = item.sDest
}
override fun getItemCount() = dataset.size
}
And then the service dataclass:
data class Service(var trainid: String, var sOrigin: String, var sDest: String)
REASON: This happens because the notifyDataSetChanged() is called as sequentially and actually then you don't have any data inside the serviceList, so it is more suitable to call that method after call has completed and a response has received
SOLUTION:
Conceptually, you should understand how asynchronous programming works.You misunderstood asynchronous programming with sequential execution and thus the above mistake
What now?
Move your recycleradapter.notifyDataSetChanged() to the place where you receive the response from the server like so in the Response.Listener interface instance
try {
serviceList.clear()
val jsonArray = response.getJSONArray("services")
for (i in 0 until jsonArray.length()) {
val serviceRow = jsonArray.getJSONObject(i)
val serviceOrigin = serviceRow.getString("origin")
val serviceDestination = serviceRow.getString("destination")
val serviceID = serviceRow.getString("train uid")
serviceList.add(Service(serviceOrigin, serviceDestination, serviceID))
recycleradapter.notifyDataSetChanged() // Here
}
} catch (e: JSONException) {
e.printStackTrace()
}
Also you can show certain kind of progress mechanism like a bar or a shimmer layout when the button is pressed and hide the visibility when the data is received from the server
Making HTTP request with Volley is asynchronous, so there is a big probability that the list of services is populated after notifying the adapter. So, you should refresh the RecyclerView when you actully got the result from the server:
response ->try {
serviceList.clear()
val jsonArray = response.getJSONArray("services")
for (i in 0 until jsonArray.length()) {
val serviceRow = jsonArray.getJSONObject(i)
val serviceOrigin = serviceRow.getString("origin")
val serviceDestination = serviceRow.getString("destination")
val serviceID = serviceRow.getString("train uid")
serviceList.add(Service(serviceOrigin, serviceDestination, serviceID))
}
runOnUiThread {
recycleradapter.notifyDataSetChanged()
}
} catch (e: JSONException) {
e.printStackTrace()
}
Related
I try to make a recycler view with the name and picture of the pokemon, but it doesn't work, I don't understand why, the requests are not done because the pokemon list is never filled. I make a loop for calls because what I want is the name and picture of the pokemon, because when I ask for a list of pokemon objects only bring the name and the url of the complete object and is the best way that has occurred to me, I would appreciate if you can give me a hand, thank you very much.
This is the code of my mainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initRecycler()
obtenerPokemons()
}
private fun initRecycler(){
pokemonAdapter = PokemonAdapter(pokemons)
linearLayoutManager = LinearLayoutManager(this)
binding.recyclerpokemon.apply {
setHasFixedSize(true)
layoutManager = linearLayoutManager
adapter = pokemonAdapter
}
}
private fun obtenerPokemons() {
for (i in 1..30) {
searchByName(i)
}
pokemonAdapter.setPokemons(pokemons)
}
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://pokeapi.co/api/v2/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private fun searchByName(query:Int){
CoroutineScope(Dispatchers.IO).launch {
val call = getRetrofit().create(PokemonService::class.java).getPokemon("pokemon/$query")
val pokemonsResp = call.body()
runOnUiThread {
if(call.isSuccessful) {
pokemons.add(pokemonsResp)
}else{
Toast.makeText(this#MainActivity, "No encuentro eso", Toast.LENGTH_SHORT).show()
}
}
}
}
}
This is the pokemon object code:
data class Pokemon (
#SerializedName("id" ) var id : Int,
#SerializedName("name" ) var name : String,
#SerializedName("sprites" ) var sprites : Sprites,
)
data class Sprites (
#SerializedName("back_default" ) var backDefault : String,
#SerializedName("front_default" ) var frontDefault : String,
)
And this is the code of my service:
interface PokemonService {
#GET
suspend fun getPokemon(#Url url:String) : Response<Pokemon?>
}
you need to call pokemonAdapter.notifyDataSetChanged() after pokemons.add(pokemonsResp). notifyDataSetChanged() will update the recycler view with the new data.
I am trying to "dynamically" create CardView and then fill TextView with data from an API call. The thing is, to me it seems like the request is not getting called.
I created an adapter class based on Create dynamically lists with RecyclerView which is working fine and I am adding as many windows as needed. And then in the MainActivity, I am trying to do an API request with Volley. Before I created manually TextView and fed the results manually into it - which worked fine. But now with this dynamically created CardView nothing is happening. Could anyone try to guide me please?
MainActivity:
class MainActivity : AppCompatActivity() {
// private var message: TextView = findViewById<TextView>(R.id.jsonParse)
lateinit var weatherRV: RecyclerView
lateinit var weatherModelArray: ArrayList<WeatherViewModel>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportActionBar?.hide()
weatherRV = findViewById(R.id.mainView)
weatherModelArray = ArrayList()
val model1 = WeatherViewModel("Open weather", "")
val model2 = WeatherViewModel("Yr.no", "")
weatherModelArray.add(model1)
weatherModelArray.add(model2)
val weatherAdapter = WeatherAdapter(this, weatherModelArray)
val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
weatherRV.layoutManager = linearLayoutManager
weatherRV.adapter = weatherAdapter
apICall(this#MainActivity, model1)
}
}
apiCall:
#RequiresApi(Build.VERSION_CODES.O)
internal fun apICall(context: Context, jsonView: WeatherViewModel) {
var lat: Double? = null
var lon: Double? = null
var url: String? = null
val apiKey: String = "XXXX"
lat = YYY.Y
lon = YYY.Y
url = "https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric&exclude=minutely,hourly"
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(context)
val gsonPretty = GsonBuilder().setPrettyPrinting().create()
// Request a string response from the provided URL.
val stringRequest = StringRequest(
Request.Method.GET, url,
{response ->
val gson = Gson()
val openJSON = gson.fromJson(response.toString(), OpenWeatherJSON::class.java)
val prettyJsonString = gsonPretty.toJson(openJSON)
try {
val dt = openJSON.daily[0].dt
val sdf = SimpleDateFormat("dd-MM-yyyy")
val date = Date(dt * 1000) // snackbar.dismiss()
sdf.format(date) // jsonView.apiResult = sdf.format(date) // jsonView.apiResult.append("\n")
jsonView.apiResult = openJSON.daily[0].temp.toString()
}
catch (e: Exception) {
jsonView.apiResult = e.toString()
}
},
{
jsonView.apiResult = "Error"
})
// Add the request to the RequestQueue.
queue.add(stringRequest) }
Now in the image below you can see, that Result1 is not getting changed into the request response. I can change it with jsonView.text = "something" outside the stringRequest
Update1:
class WeatherAdapter(context: Context, weatherModelArray: ArrayList<WeatherViewModel>):
RecyclerView.Adapter<WeatherAdapter.ViewHolder>() {
private val weatherModelArray: ArrayList<WeatherViewModel>
// Constructor
init {
this.weatherModelArray = weatherModelArray
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeatherAdapter.ViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.card_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: WeatherAdapter.ViewHolder, position: Int) {
val model = weatherModelArray[position]
holder.apiName.text = model.apiName
holder.apiResult.text = model.apiResult
}
override fun getItemCount(): Int {
return weatherModelArray.size
}
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val apiName: TextView
val apiResult: TextView
init {
apiName = itemView.findViewById(R.id.idApiName)
apiResult = itemView.findViewById(R.id.idApiResult)
}
}
}
You need to notify the RecyclerView adapter that the data has changed after you change the data. This means calling notifyDataSetChanged from within the callback. That would look something like this:
internal fun apICall(context: Context, jsonView: WeatherViewModel, adapter: WeatherAdapter) {
//...
val stringRequest = StringRequest(
Request.Method.GET, url,
{response ->
//...
jsonView.apiResult = openJSON.daily[0].temp.toString()
adapter.notifyDataSetChanged()
},
{
jsonView.apiResult = "Error"
adapter.notifyDataSetChanged()
})
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
I am having an issue with reloading data in RecyclerView after the data is updated with a separate URL call. The FristFragment calls LoadDataFromUrl and displays the data in the Recycler View. Each Recycler's view item has a button with OnSetClickListener that calls another URL to update item's name data. After the user updates the item's name and a successful response is received, then the original myResponse data is updated with the new item's name. When the data is updated, I need to reload data in the Recycer View` but I can not figure it out. I spent two days trying to fix it but I can't get it running. Any help would be greatly appreciated!
Data Model:
DataModel.kt
class MyResponse (var Status: String = "", val items: List<Items> = emptyList())
class Items(var ItemId: String = "", var ItemName: String = "")
Code for Recycler View:
Adapter.kt
var recyclerView: RecyclerView? = null
class MainAdapter(val myResponse: MyResponse): RecyclerView.Adapter<CustomViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.my_custom_cell, parent,false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val item = myResponse.items.get(position)
//there is a button for each item in the list that will call a function ButtonPressed from Frist Fragment
holder.view.button.setOnClickListener {
val firstFragment = FirstFragment()
firstFragment.ButtonPressed(item.ItemId,item.ItemName, position)
}
holder.view.textView_ItemID = item.itemId
holder.view.textView_Item_Name = item.itemName
}
class CustomViewHolder(val view: View, var Items: Item? = null): RecyclerView.ViewHolder(view){
}
}
Then I have a fragment Fragment.kt
FirstFragment.kt
class FirstFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.first_fragment, container, false)
return rootView
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
LoadDataFromUrl()
recyclerView.layoutManager = LinearLayoutManager(this.context,
LinearLayoutManager.HORIZONTAL, false)
}
//this function loads data on create view
fun LoadDataFromUrl(){
val url = "some_url"
val payload = "some_data_to_send"
val requestBody = payload.toRequestBody()
val request = Request.Builder()
.method("POST",requestBody)
.url(url)
.build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("error")
}
override fun onResponse(call: Call, response: Response) {
val body = response?.body?.string()
val gson = GsonBuilder().create()
myResponse = gson.fromJson(body, MyResponse::class.java)
activity?.runOnUiThread {
if (myResponse.Status== "200"){
recyclerView.adapter = MainAdapter(myResponse)
}
}
}
})
}
fun ButtonPressed(itemId: String, itemName: String, position: Int){
val url = "some_another_url"
val payload = "some_another_data_to_send"
val requestBody = payload.toRequestBody()
val request = Request.Builder()
.method("POST",requestBody)
.url(url)
.build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("error")
}
override fun onResponse(call: Call, response: Response) {
val body = response?.body?.string()
val gson = GsonBuilder().create()
val buttomPressedResponse = gson.fromJson(body, ButtonPressedResponse::class.java)
if (buttonPressedResponse.Status== "200") {
myResponse.response[position].Status = buttomPressedResponse.Status //will update existing object myResponse with a new item's name
//this is where I have a problem
recyclerView.adapter.notifyDataSetChanged()
}
}
}
}
I tried the following changes but I still get an error.
//I get this error: Fatal Exception: OkHttp Dispatcher Process. recyclerView must not be null. Then the app crashes and the view reloads. If I clcik the Button again I get an error saying: RecyclerView: No adapter atatched. Skipping layout.
activity?.runOnUiThread {
myResponse.response[position].Status = checkInOutResponse.Status //will update existing object myResponse with updated data
recyclerView.adapter?.notifyDataSetChanged()
}
I also tried to run on it runOnUiTHread but nothing happens with this code
activity?.runOnUiThread {
myResponse.response[position].Status = checkInOutResponse.Status //will update existing object myResponse with updated data
recyclerView.adapter?.notifyDataSetChanged()
}
Create var myResponse: MyResponse variable in Adapter
Adapter.kt
var recyclerView: RecyclerView? = null
class MainAdapter(val myResponseInit: MyResponse): RecyclerView.Adapter<CustomViewHolder>(){
var myResponse: MyResponse
myResponse = myResponseInit
fun submitMyResponse(data: MyResponse) {
myResponse = data
}
//Rest of the code onCreateViewHolder etc.
}
Call submitMyResponse() function and notifyDataSetChanged() on adapter everytime you receive response.
So I'm attempting to write an app for playing podcasts from an RSS feed, mostly to challenge myself to see if I can pull it off, but I've ran into some trouble with populating the RecyclerViewer. I've been able to successfully parse the RSS feed and store it in a MutableList, using Log statements I can verify its working(in the background thread at least), but when I try to update the adapter nothing seems to happen.
I've been using The BigNerdRanch android book as my introduction to android, and I've looked at several examples for working with RecyclerViewers, but I cannot figure out what I'm doing wrong.
I can't help but wonder if I need to use a handler to pass the data from the background thread to the main thread. I can't remember where I read to use Executors.newSingleThreadExecutor() for executing the web call. I also don't know if I should be doing the actual parsing on the background thread along with the web call, or when and where I should be calling input.close() and connect.disconnect(). Or maybe I'm just inflating the wrong thing somewhere...
All the handler/adapter examples I look at are the same as what I have, the only real difference seems to be messing around threading.
At this point I only want to see it display the list.
Main Activity(I have a mockup splashScreenActivity class that starts MainActivity using runnable() and Handler().postDelayed())
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (currentFragment == null){
val fragment = PodcastListFragment.newInstance()
supportFragmentManager.beginTransaction().add(R.id.fragment_container, fragment).commit()
}
}
I basically followed an example right out of the BigNerdRanch book, except they used a database and singleton repository to initially populate their RecyclerViewer. At this point I just want to display the list its generating before moving on to build a Database/Repository/ViewModel/etc.
class PodcastListFragment : Fragment() {
private var podcastList : MutableList<Podcast> = mutableListOf() //this is just for short term to see it work
private lateinit var podcastRecyclerView: RecyclerView
private var podcastAdapter: PodcastAdapter? = PodcastAdapter(podcastList)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.podcast_list, container, false)
podcastRecyclerView = view.findViewById(R.id.podcast_recycler_view) as RecyclerView
podcastRecyclerView.layoutManager = LinearLayoutManager(context)
podcastRecyclerView.adapter = podcastAdapter
updateUI(podcastList)
return view
}
override fun onAttach(context: Context) {
super.onAttach(context)
doInBackground() //not actually sure where this should be called
}
private fun doInBackground() {
val executor = Executors.newSingleThreadExecutor()
executor.execute {
try {
var podcast = Podcast()
val url = URL(RSS)
val connect: HttpURLConnection =
url.openConnection() as HttpURLConnection
connect.readTimeout = 10000
connect.connectTimeout = 15000
connect.requestMethod = "GET"
connect.connect()
val input: InputStream = connect.inputStream
val factory: XmlPullParserFactory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
val parser: XmlPullParser = factory.newPullParser()
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
parser.setInput(input, null)
var tagname: String?
var text = ""
var event = parser.eventType
while (event != XmlPullParser.END_DOCUMENT) {
tagname = parser.name
when (event) {
XmlPullParser.START_TAG -> if (tagname == "item") podcast = Podcast()
XmlPullParser.TEXT -> text = parser.text
XmlPullParser.END_TAG -> when(tagname){
"title" -> podcast.title = text
"itunes:author" -> podcast.author = text
"pubDate" -> podcast.date = text
"guid" -> podcast.id = parseGuid(text)
"itunes:summary" -> podcast.reference = text
"item" -> podcastList.add(podcast)
}
}
event = parser.next()
}
input.close()
connect.disconnect()
for (obj in podcastList) {Log.d(TAG, "guid: ${obj.id} :: Title: ${obj.title}")}
}
catch (e: Exception) { e.printStackTrace() }
catch (e: XmlPullParserException) { e.printStackTrace() }
catch (e: NullPointerException) { e.printStackTrace() }
}
}
// Log statements show the list is getting updated
private fun updateUI(podcasts: MutableList<Podcast>){
podcastAdapter = PodcastAdapter(podcasts)
podcastRecyclerView.adapter = podcastAdapter
}
private fun parseGuid(url: String) :String {
val equalsign = url.indexOf("=", 0, false)
return if ( equalsign != -1)
url.slice(IntRange(equalsign+1, url.length-1))
else ""
}
companion object{
fun newInstance(): PodcastListFragment{
return PodcastListFragment()
}
}
/**********************************************************************************************
*
* PodcastHolder
*
* *******************************************************************************************/
private inner class PodcastHolder(view: View) : RecyclerView.ViewHolder(view) {
private val podcastTitle: TextView = itemView.findViewById(R.id.podcast_title)
private val podcastDate: TextView = itemView.findViewById(R.id.podcast_date)
private val podcastScripture: TextView = itemView.findViewById(R.id.scripture_ref)
private val dateFormat = SimpleDateFormat("MMM d", Locale.getDefault()) //just use a string?
fun bind(podcast: Podcast) {
podcastTitle.text = podcast.title
podcastDate.text = podcast.date
podcastScripture.text = podcast.reference
}
}
/**********************************************************************************************
*
* PodcastAdapter
*
* *******************************************************************************************/
private inner class PodcastAdapter(var podcasts: MutableList<Podcast>) : RecyclerView.Adapter<PodcastHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PodcastHolder {
val view = layoutInflater.inflate(R.layout.podcast_list_item, parent, false)
return PodcastHolder(view)
}
override fun onBindViewHolder(holder: PodcastHolder, position: Int) {
val podcast = podcasts[position]
holder.bind(podcast)
}
override fun getItemCount(): Int = podcasts.size
}
I don't get any errors, just an empty RecyclerView, and a headache trying to figure out what I did wrong.
Any guidance would be greatly appreciated.
Thanks!
EDIT
After playing around with Thread.currentThread().name I was able to figure out it was a threading problem even though I wasn't seeing an exception thrown.
Call doInBackground() inside onCreateView()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.podcast_list, container, false)
podcastRecyclerView = view.findViewById(R.id.podcast_recycler_view) as RecyclerView
podcastRecyclerView.layoutManager = LinearLayoutManager(context)
podcastRecyclerView.adapter = podcastAdapter
doInBackground(); /// <<<<<<< change here
updateUI(podcastList)
return view
}
And add notifyDataSetChanged() after the background work is over in doInBackground()
private fun doInBackground() {
val executor = Executors.newSingleThreadExecutor()
executor.execute {
try {
/// .... omitted code
input.close()
connect.disconnect()
for (obj in podcastList) {Log.d(TAG, "guid: ${obj.id} :: Title: ${obj.title}")}
podcastAdapter.notifyDataSetChanged() // <<<< change here
/// ............ .... omitted code
}
I'm trying to make my Android App (I'm only experienced in iOS).
I created a RecyclerView that gets the data from a web. I tried everything to implement endless scrolling to load more items, but when I call the function to get the items, the entire RecyclerView loads again and no attach the new results on the bottom.
This is my code:
ConversationUser.kt
data class ConversationUser(
val message_nickname: String,
val message_image_thumb: String,
val message_large_thumb: String,
val message_modified: String,
val message_status: String,
val message_unread: Int,
val conv_id: String,
val message_dest: String) {
}
ConversacionesActivity.kt
class ConversacionesActivity : AppCompatActivity() {
// MARK: Variables
var user_token = ""
var user_id = ""
override fun onCreate(savedInstanceState: Bundle?) {
// User Defaults
val sharedPreferences = getSharedPreferences("Preferences", Context.MODE_PRIVATE)
user_token = sharedPreferences.getString("user_token", "")!!
user_id = sharedPreferences.getString("user_id", "")!!
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_conversaciones)
recyclerConv.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
getConversationsData()
recyclerConv.setLoadingListener(object : LoadingListener {
override fun onRefresh() {
//refresh data here
}
override fun onLoadMore() {
// load more data here
getConversationsData()
}
})
}
fun getConversationsData() {
val httpAsync = "https://mywebsite.com/conversations/${user_token}"
.httpPost()
.responseString { request, response, result ->
when (result) {
is Result.Failure -> {
val ex = result.getException()
println(ex)
}
is Result.Success -> {
val data = result.get()
runOnUiThread {
val conversaciones = processJson(data)
show(conversaciones)
return#runOnUiThread
}
}
}
}
httpAsync.join()
}
fun processJson(json: String): List<ConversationUser> {
val gson: Gson = GsonBuilder().create()
val conversaciones: List<ConversationUser> = gson.fromJson(
json,
Array<ConversationUser>::class.java
).toList()
return conversaciones
}
fun show(conversaciones: List<ConversationUser>) {
recyclerConv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerConv.adapter = AdaptadorConv(conversaciones, this, user_token, user_id)
}
AdaptadorConv.kt
class AdaptadorConv(
val conversaciones: List<ConversationUser> = ArrayList(),
val context: Context,
val user_token: String,
val user_id: String) : RecyclerView.Adapter<AdaptadorConv.ConvViewHolder>() {
override fun onBindViewHolder(holder: ConvViewHolder, position: Int) {
holder.convName.text = conversaciones[position].message_nickname
holder.convTime.text = conversaciones[position].message_modified
}
override fun getItemCount(): Int {
return conversaciones.size - 1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConvViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(
R.layout.conversaciones,
parent,
false
)
return ConvViewHolder(view)
}
class ConvViewHolder(vista: View): RecyclerView.ViewHolder(vista) {
val convImg: ImageView = itemView.findViewById(R.id.convImg)
val convStatus: ImageView = itemView.findViewById(R.id.convStatus)
val convName: TextView = itemView.findViewById(R.id.convName)
val convUnread: TextView = itemView.findViewById(R.id.convUnread)
val convTime: TextView = itemView.findViewById(R.id.convTime)
}
Thanks for any help or hint.
Please check your show () method, you are creating new Adapter every time with the new dataset. You have to append the new items to the adapter's list and adapter should be set to list once. Helpful tutorial can be found at here.