I have API data which ID's are uuid (strings) and not (integer) and when I want to get those ids in my adapter it says
Type mismatch.
Required:
Int
Found:
String?
Sample of API items
{
"id":"0ade1bfb-6d02-4a1f-9cd4-dc88fa8aadbd",
"name":"ABC",
"photo":null // if not null, will be full URL of image (https://example.com/img/abc.jpg)
}
Code
Adapter (commented)
class ServicesAdapter(private val serviceList: List<Service>) : RecyclerView.Adapter<ServicesAdapter.ServiceViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ServiceViewHolder {
val imageView =
LayoutInflater.from(parent.context).inflate(R.layout.service_item, parent, false)
return ServiceViewHolder(imageView)
}
override fun onBindViewHolder(holder: ServiceViewHolder, position: Int) {
val currentItem = serviceList[position]
holder.imageView.setImageResource(currentItem.photo) <-- error line
holder.textView.text = currentItem.name
}
override fun getItemCount() = serviceList.size
class ServiceViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.imageView
val textView: TextView = itemView.textView2
}
}
Activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
run("api_url")
}
fun run(url: String) {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) {
val list: ArrayList<Service> = ArrayList()
getServices(response.body()!!.string(), list)
recycler.layoutManager = LinearLayoutManager(this#MainActivity)
recycler.adapter = ServicesAdapter(list)
}
})
}
fun getServices(response: String, list: ArrayList<Service>) {
var jsonObject = JSONObject(response)
val jsonArray = jsonObject.getJSONArray("data")
for (i in 0 until jsonArray.length()) {
val jsonObject1 = jsonArray.getJSONObject(i)
var listingObject = Service(
jsonObject1.getString("id"),
jsonObject1.getString("name"),
jsonObject1.getString("photo")
)
list.add(listingObject)
}
}
Class
class Service (val id: String?, val name: String?, val photo: String?) {
}
Any idea?
Add the following lines of code in your OnBindViewHolder to load images from the URL
currentItem.photo?.apply{
Glide.with(holder.imageView.context)
.load(this)
.into(holder.imageView)
}
holder.imageView.setImageResource(currentItem.photo)
this method requires int value, which you are providing null(treated as null string)
try replacing null as blank while parsing json data
or you can use this
public static boolean isBlankOrNull(String value)
{
return (value == null || value.equals("") || value.equals("null") || value.trim().equals(""));
}
like this
holder.imageView.setImageResource(isBlankOrNull(currentItem.photo) ? 0 : currentItem.photo)
Just set image when you are getting it in response i.e. its value is not empty or null.Also you need to use any library to set image to imageview for example You can use Square's Picasso and can update your code as below
if(!TextUtils.isEmpty(currentItem.photo))
Picasso
.get()
.load(currentItem.photo)
.into(holder.imageView)
Related
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.
I try to get data from json: https://github.com/rolling-scopes-school/rs.android.task.6/blob/master/data/data.json
Service to prepare retrofit to make the call:
object ServiceBuilder {
private val client = OkHttpClient.Builder().build()
var gson = GsonBuilder()
.setLenient()
.create()
private val retrofit = Retrofit.Builder()
.baseUrl("https://github.com/rolling-scopes-school/rs.android.task.6/blob/master/data/")
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}
}
The Interface:
interface ApiInterface {
#GET("/data.json")
fun getItems(): Call <List<Item>>
}
MainActivity code:
val request = ServiceBuilder.buildService(ApiInterface::class.java)
val call = request.getItems()
call.enqueue(object : Callback<List<Item>> {
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
Toast.makeText(this#MainActivity, "${t.message}", Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
if (response.isSuccessful){
recyclerview.apply {
progress_bar.visibility = View.GONE
setHasFixedSize(true)
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = MyAdapter(response.body()!!)
}
}
}
})
}
MyAdapter:
class MyAdapter(val items: List): RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recycler_layout, parent, false)
return ItemsViewHolder(view)
}
override fun getItemCount(): Int {
return items.size
System.out.println("items.size - " + items.size)
}
override fun onBindViewHolder(holder: ItemsViewHolder, position: Int) {
val title = items[position].title ?: ""
val description = items[position].description ?: ""
val imageurl = items[position].image.url ?: ""
holder.bind(title, description, imageurl)
}
}
class ItemsViewHolder(itemView : View): RecyclerView.ViewHolder(itemView){
private val photo: ImageView = itemView.findViewById(R.id.image_photo)
private val title_text: TextView = itemView.findViewById(R.id.title_)
private val description_text:TextView = itemView.findViewById(R.id.description)
fun bind(title: String,description:String,imageurl:String) {
Glide.with(itemView.context).load(imageurl).into(photo)
title_text.text = title
description_text.text = description
}
Dataclass Item:
data class Item(
val description: String,
val duration: Duration,
val enclosure: Enclosure,
val group: Group,
val guid: Guid,
val image: ImageX,
val link: String,
val pubDate: String,
val title: String
)
I run app and no such data in screen :-). What's wrong? Can you help me?
The problem seems to be lying here. you are getting the response and you are creating a new adapter each time, but not assigning the new adapter to the recyclerview i guess.
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
if (response.isSuccessful){
recyclerview.apply {
progress_bar.visibility = View.GONE
setHasFixedSize(true)
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = MyAdapter(response.body()!!)
//add this code to assign the adapter and notify the adapter
recyclerview.adapter = adapter
adapter.notifyDataSetChanged()
}
}
}
note: it would be better if you create your adapter in onCreate() and assign the adapter to RecyclerView there only, and now when you call the github api to get the details just update the data in the adapter and call notifyDataSetChanged on adapter to notify about the new data changed.
Edit: It seems there is issue with the api endpoint also you won't get the json data by hitting that url directly. you need to convert this to raw. something like this.
https://raw.githubusercontent.com/rolling-scopes-school/rs.android.task.6/master/data/data.json
Also the model which you have mentioned won't be mapped by json adapter because the response is in a different format so here change the model to something like below
data class GitResponse(val channel: Channel)
data class Channel(val item: List<Item>)
so change the retrofit with the following url param.
interface ApiInterface {
#GET
fun getItems(#Url url: String): Call<GitResponse>
}
//and where ever you are call this now pass the full url which is different from the one you have.
val url = "https://raw.githubusercontent.com/rolling-scopes-school/rs.android.task.6/master/data/data.json"
val request = ServiceBuilder.buildService(ApiInterface::class.java)
val call = request.getItems(url)
call.enqueue(object : Callback<GitResponse> {
override fun onFailure(call: Call<GitResponse>, t: Throwable) {
Toast.makeText(this#MainActivity, "${t.message}", Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<GitResponse>, response: Response<GitResponse>) {
if (response.isSuccessful){
recyclerview.apply {
progress_bar.visibility = View.GONE
setHasFixedSize(true)
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = MyAdapter(response.body()?.channel?.item ?: listOf())
//add this code to assign the adapter and notify
recyclerview.adapter = adapter
adapter.notifyDataSetChanged()
}
}
}
})
now you will get the api response, check by applying debug point.
So I have a RecyclerView which data is populated with data from some Data Class. I want to Apply a search item function for this recyclerview. Back when I don't use Data Classes, I followed this tutorial (The tutorial is in Java).
Now that I'm using a data class since I fetch the data that populate the recyclerview from an API endpoint, I'm quite confused on how to apply the search function in the current recyclerview.
(I fetch the data using library Retrofit if you wondering.)
This is the code snippet from the fragment:
RefundListFragment.kt
private fun fetchRefundListData() {
NetworkConfig().getRefundListDetailService().getRefundList().enqueue(object :
Callback<RefundListPOJODataClass> {
override fun onFailure(call: Call<RefundListPOJODataClass>, t: Throwable) {
...
...
...
}
override fun onResponse(
call: Call<RefundListPOJODataClass>,
response: Response<RefundListPOJODataClass>
) {
binding.refundProgressBar.visibility = View.GONE
binding.rvRefundList.adapter =
response.body()?.let { RefundListAdapter(it, this#RefundListFragment) }
fun filterString(text: String) {
val filteredList: List<RefundListPOJODataClass> = ArrayList()
for (item in response.body()?.data!!) {
if (item != null) {
if (item.buyerName?.toLowerCase()?.contains(text.toLowerCase())!!) {
filteredList.add(item)
}
}
}
adapter.filterList(filteredList)
}
binding.refundListSearchBar.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
filterString(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
})
}
However it returned an error:
Unresolved reference: add
This is the recyclerview Adapter:
RefundListAdapter.kt
class RefundListItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val refundedOrderId: TextView = itemView.refundedOrderId
private val orderDateTime: TextView = itemView.orderDateTime
private val refundedCustomerName: TextView = itemView.refundedCustomerName
fun bind(
refundHistory: RefundListPOJODataClassDataItem,
clickListener: RefundListOnItemClickListener
) {
refundedOrderId.text = refundHistory.orderId
orderDateTime.text = refundHistory.orderDate
refundedCustomerName.text = refundHistory.buyerName
itemView.setOnClickListener {
clickListener.onItemClicked(refundHistory)
}
}
}
class RefundListAdapter(
private var refundList: RefundListPOJODataClass,
private val itemClickListener: RefundListOnItemClickListener
) : RecyclerView.Adapter<RefundListItemHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RefundListItemHolder {
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.refund_list_item_layout, parent, false)
return RefundListItemHolder(v)
}
override fun getItemCount(): Int {
if (refundList.data != null) {
return refundList.data!!.size
} else {
return 0
}
}
override fun onBindViewHolder(holder: RefundListItemHolder, position: Int) {
val refundHistory = refundList.data?.get(position)
if (refundHistory != null) {
holder.bind(refundHistory, itemClickListener)
}
}
fun filterList(filteredList: List<RefundListPOJODataClass>) { //This is the function I called in the fragment
refundList = filteredList.get(0)
notifyDataSetChanged()
}
}
interface RefundListOnItemClickListener {
fun onItemClicked(refundHistory: RefundListPOJODataClassDataItem)
}
And this is how the data class looks like
RefundListPOJODataClass.kt
data class RefundListPOJODataClass(
#field:SerializedName("data")
val data: List<RefundListPOJODataClassDataItem?>? = null,
#field:SerializedName("error")
val error: Error? = null
)
data class RefundListPOJODataClassError(
#field:SerializedName("msg")
val msg: String? = null,
#field:SerializedName("code")
val code: Int? = null,
#field:SerializedName("status")
val status: Boolean? = null
)
data class RefundListPOJODataClassDataItem(
#field:SerializedName("order_date")
val orderDate: String? = null,
#field:SerializedName("buyer_name")
val buyerName: String? = null,
#field:SerializedName("phone_number")
val phoneNumber: String? = null,
#field:SerializedName("status_refund")
val statusRefund: String? = null,
#field:SerializedName("order_id")
val orderId: String? = null,
#field:SerializedName("refund")
val refund: Int? = null,
#field:SerializedName("refund_date")
val refundDate: String? = null
)
I want to search the recyclerview item by the attribute buyerName. How can I achieve it ? What should i change in my code? If there's any detail I missed to tell, Just let me know.
Your filtered list is of type List, which is immutable.
Change it to array list or MutableList:
val filteredList: ArrayList<RefundListPOJODataClass> = ArrayList()
I want to take data from API using AsyncHttpClient(), after that, I show the data to recycle view. but when trying to debug my code is run show the data to recycle view which is no data because not yet finish setData using AsyncHttpClient(). my question is how to run setData recycle view adapter after my setLeague is finish getting data? Sorry, I am a newbie and I put my code below. Please help, I already try using async-await but doesn't work.
This is my ViewModel Class
val footballLeagueList = MutableLiveData<ArrayList<league>>()
internal fun setLeague(){
val client = AsyncHttpClient()
val client2 = AsyncHttpClient()
val listItems = ArrayList<league>()
val leagueListUrl = "https://www.thesportsdb.com/api/v1/json/1/all_leagues.php"
val leagueDetailUrl = "https://www.thesportsdb.com/api/v1/json/1/lookupleague.php?id="
client.get(leagueListUrl, object : AsyncHttpResponseHandler(){
override fun onSuccess(
statusCode: Int,
headers: Array<out Header>?,
responseBody: ByteArray?
) {
try{
val result = String(responseBody!!)
val responseObject = JSONObject(result)
for(i in 0 until 10){
val list = responseObject.getJSONArray("leagues")
val leagues = list.getJSONObject(i)
val leaguesItems = league()
val detailUrl = leagueDetailUrl + leagues.getString("idLeague")
leaguesItems.id = leagues.getString("id")
leaguesItems.name = leagues.getString("strLeague")
leaguesItems.badgeUrl = leagues.getString( "https://www.thesportsdb.com/images/media/league/badge/dqo6r91549878326.png")
listItems.add(leaguesItems)
}
footballLeagueList.postValue(listItems)
}catch (e: Exception){
Log.d("Exception", e.message.toString())
}
}
override fun onFailure(
statusCode: Int,
headers: Array<out Header>?,
responseBody: ByteArray?,
error: Throwable?
) {
Log.d("Failed", "On Failure")
}
})
}
internal fun getLeague(): LiveData<ArrayList<league>> {
return footballLeagueList
}
And this is my MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var mainViewModel: MainViewModel
private lateinit var adapterLeague: leagueAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapterLeague = leagueAdapter()
adapterLeague.notifyDataSetChanged()
verticalLayout {
lparams(matchParent, matchParent)
recyclerView {
layoutManager = GridLayoutManager(context, 2)
adapter = adapterLeague
}
}
mainViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
.get(MainViewModel::class.java)
mainViewModel.setLeague()
mainViewModel.getLeague().observe(this, Observer { leaguesItems ->
if(leaguesItems != null){
adapterLeague.setData(leaguesItems)
}
})
}
}
So I am using the OpenWeatherMap Api to create a sun spotter app, so the issue I have right now is that, I have all the icons in my drawable folder, the issue is I want to display all the icons right now I have ti set up like this:
I am using that hard coded icon as a placeholder but I want all the other icons to show, my issue is how do I do that, the image from the api is given in string format and i don't know how I would get them from by drawable folder
Code
// Array Adapter
class ForecastAdapter(val forecast: Forecast) : RecyclerView.Adapter<ForecastData>(){
override fun getItemCount(): Int {
return forecast.list.count()
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ForecastData {
val layoutInflator = LayoutInflater.from(p0?.context)
val cellForRow = layoutInflator.inflate(R.layout.weather_row, p0, false)
return ForecastData(cellForRow)
}
override fun onBindViewHolder(p0: ForecastData, p1: Int) {
val getWeather = forecast.list[p1]
val clouds = getWeather.weather[0]
val getDateTime = forecast.list[p1]
val getIcon = forecast.list[p1]
// val icon = getIcon.weather[0]
p0.view.textView_text_clouds.text = clouds.main
p0.view.textView_date_time.text = getDateTime.dt_txt
// p0.view.imageView_icon = icon.icon
}
}
class ForecastData(val view: View): RecyclerView.ViewHolder(view){
}
// MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
searchButton.setOnClickListener {
getRequest()
}
view.layoutManager = LinearLayoutManager(this)
// val drawableId: Int = getResources().getIdentifier("drawable", "drawable", getPackageName())
}
private fun getRequest() {
val input = searchBar.getText().toString()
val url = "http://api.openweathermap.org/data/2.5/forecast?zip=" + input + "&units=imperial&APPID=" + getString(R.string.OPEN_WEATHER_MAP_API_KEY)
val request = okhttp3.Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call, response: okhttp3.Response) {
val body = response?.body()?.string()
println(body)
val gson = GsonBuilder().create()
val weather = gson.fromJson(body, Forecast::class.java)
runOnUiThread {
view.adapter = ForecastAdapter(weather)
}
}
override fun onFailure(call: Call, e: IOException) {
println("Failed to execute")
}
})
}
}
Use When
when("forecast.list[p1]") {
"icon01d.png" -> p0.view.imageView_icon.setDrawable(R.drawable.icon01d.png)
"icon02n.png" -> p0.view.imageView_icon.setDrawable(R.drawable.icon02n.png)
else -> // Your place holder icon for imageview
}