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
}
Related
I was trying to build a RecyclerView using a response from Retrofit. But, I ran into an issue that my Recycler turns up empty white while my log shows that I have data in my ArrayList from the network response. (I do not want to set up an MVVM yet until I get comfortable with Kotlin.)
PlaylistRecyclerAdapter
class PlaylistRecyclerAdapter (private val playListNames: Array<String>) :
RecyclerView.Adapter<PlaylistRecyclerAdapter.PlayListViewHolder>() {
// Describes an item view and its place within the RecyclerView
class PlayListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val playlistTextView: TextView = itemView.findViewById(R.id.playlist_name_text)
fun bind(word: String) {
playlistTextView.text = word
}
}
// Returns a new ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayListViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.playlist_name_item, parent, false)
return PlayListViewHolder(view)
}
// Returns size of data list
override fun getItemCount(): Int {
return playListNames.size
}
// Displays data at a certain position
override fun onBindViewHolder(holder: PlayListViewHolder, position: Int) {
holder.bind(playListNames[position])
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
val templist = getPlaylistItems()
//Log.d("RESPONSE", "onCreate: "+templist.get(0).toString())
recyclerView.adapter = PlaylistRecyclerAdapter(templist.toTypedArray())
recyclerView.adapter?.notifyDataSetChanged()
}
private fun getPlaylistItems(): ArrayList<String> {
var playlisttitles = ArrayList<String>()
var BASE_URL = "https://flicastdemo.s3.amazonaws.com/jwplayer/"
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(HomeWebService::class.java)
val call = service.getHomeContent()
var home = HomeRoot()
call.enqueue(object : Callback<HomeRoot> {
override fun onResponse(call: Call<HomeRoot>, response: Response<HomeRoot>) {
if (response.code() == 200) {
home = response.body()
if(!home.equals(null))
{
//Log.e("HOME", "val: " + home.toString())
for (i in 0 until home.content.size){
val BASE_URL = "https://cdn.jwplayer.com/v2/"
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(PlaylistWebService::class.java)
val call = service.getPlayListItem(home.content.get(i).playlistId) //"1QhdrFVq"
call.enqueue(object : Callback<PlaylistRoot> {
override fun onResponse(call: Call<PlaylistRoot>, response: Response<PlaylistRoot>) {
if (response.code() == 200) {
var playlistinfo : PlaylistRoot = response.body();
playlisttitles.add(playlistinfo.title)
Log.e("PlaylistTitle!", "onResponseTitle: "+playlistinfo.title)
}
}
override fun onFailure(call: Call<PlaylistRoot>, t: Throwable) {
Log.d("NO!NO!NO!", "onResponse: "+"NO!")
playlisttitles.add("No Playlist")
}
})
}
}
}
}
override fun onFailure(call: Call<HomeRoot>, t: Throwable) {
Log.d("NO!NO!NO!", "onResponse: "+"NO!")
}
})
return playlisttitles
}
}
Retrofit returns data in a background thread, so the callback to onResponse() is asynchronous to the UI, i.e. it takes some time until the data comes in; and therefore the getPlaylistItems() method will be returned before the retrofit data is up. And therefore it returns an empty list in val templist = getPlaylistItems().
To fix, this you can create a listener interface, or just build-up the RecyclerView within the onResponse callback:
override fun onResponse(call: Call<PlaylistRoot>, response: Response<PlaylistRoot>) {
if (response.code() == 200) {
var playlistinfo : PlaylistRoot = response.body();
playlisttitles.add(playlistinfo.title)
Log.e("PlaylistTitle!", "onResponseTitle: "+playlistinfo.title)
recyclerView.adapter = PlaylistRecyclerAdapter(playlisttitles.toTypedArray())
recyclerView.adapter?.notifyDataSetChanged()
}
}
I have two activity and each activity has recyclerview, I have nested json.I want when user tap first activity recyclerview then they goes to next activity and show the recyclerview.Better understanding please follow the json link and code.
Json Link
: https://run.mocky.io/v3/8c3f6be2-a53f-47da-838c-72e603af844d
CropData.kt
data class CropData(
#SerializedName("cropImage")
val cropImage: String,
#SerializedName("cropName")
val cropName: String,
#SerializedName("cropTime")
val cropTime: String,
#SerializedName("cropDetails")
val cropDetails: String,
#SerializedName("cropProcess")
val cropProcess: String,
#SerializedName("cropType")
val cropType: String,
#SerializedName("cropFertilizer")
val cropFertilizer: List<CropFertilizer>
)
CropFertilizer.kt
data class CropFertilizer(
#SerializedName("fertilizerName")
val fertilizerName: String,
#SerializedName("fertilizerFirst")
val fertilizerFirst: String,
#SerializedName("fertilizerSecond")
val fertilizerSecond: String,
#SerializedName("fertilizerThird")
val fertilizerThird: String
)
CropActivity.kt
class CropActivity : AppCompatActivity(), CropOnItemClickListener {
private lateinit var viewModel: CropActivityViewModel
private val cropAdapter by lazy { CropAdapter(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_crop)
val repository = Repository()
val viewModelFactory = CropActivityViewModelFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory).get(CropActivityViewModel::class.java)
viewModel.getCropData()
viewModel.cropResponse.observe(this, Observer { response ->
cropAdapter.setData(response)
Log.d("dCrop", response.toString())
})
setUpCrops()
}
private fun setUpCrops() {
crop_recyclerview.layoutManager = LinearLayoutManager(
this,
LinearLayoutManager.VERTICAL, false
)
crop_recyclerview.setHasFixedSize(true)
crop_recyclerview.adapter = cropAdapter
}
override fun onClick(item: CropData, position: Int) {
val intent = Intent(this, CropDetailsActivity::class.java)
intent.putExtra("name", item.cropName)
intent.putExtra("image", item.cropImage)
intent.putExtra("intro", item.cropDetails)
intent.putExtra("process", item.cropProcess)
intent.putExtra("type", item.cropType)
intent.putExtra("time", item.cropTime)
startActivity(intent)
}
}
CropAdapter.kt
class CropAdapter(private val cropOnItemClickListener: CropOnItemClickListener) :
RecyclerView.Adapter<CropAdapter.ViewHolder>() {
private var cropList = emptyList<CropData>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.crop_row,parent,false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.crop_name.text = cropList[position].cropName
holder.itemView.crop_details.text = cropList[position].cropDetails
holder.itemView.crop_time.text = cropList[position].cropTime
holder.itemView.setOnClickListener{
cropOnItemClickListener.onClick(cropList[position],position)
}
}
override fun getItemCount(): Int {
return cropList.size
}
fun setData(newList: List<CropData>){
notifyDataSetChanged()
cropList = newList
notifyDataSetChanged()
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
}
CropDetailsActivity.kt
class CropDetailsActivity : AppCompatActivity() {
private lateinit var viewModel: CropDetailsActivityViewModel
private val fertlizerAdapter by lazy { FertilizerAdapter() }
private val cropFertilizerAdapter by lazy { FertilizerAdapter() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_crop_details)
val bundle:Bundle? = intent.extras
crop_details_name.text = bundle!!.getString("name")
val i: String = bundle!!.getString("intro")
crop_details_intro.text = i
Glide.with(this).load(bundle.getString("image")).into(crop_details_image);
val j: String = bundle!!.getString("process")
crop_details_process.text = j
crop_details_time.text = bundle!!.getString("time")
crop_details_type.text = bundle!!.getString("type")
val repository = Repository()
val viewModelFactory = CropDetailsActivityViewModelFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory).get(CropDetailsActivityViewModel::class.java)
viewModel.getFertilizer()
viewModel.fResponse.observe(this, Observer {
cropFertilizerAdapter.setData(it)
})
crop_details_recyclerview.layoutManager = LinearLayoutManager(
this,
LinearLayoutManager.VERTICAL, false
)
crop_details_recyclerview.setHasFixedSize(true)
crop_details_recyclerview.adapter = cropFertilizerAdapter
}
}
Better understanding see the image
[1]: https://i.stack.imgur.com/BtPqe.jpg
I want to show CropFertilizer list in CropDetailsActivity.How can i do this?
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)
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 build a simple android application showing data from TMDb API using Retrofit, but how do I programmatically get data access speed when requesting data from the server and show it on Android studio Logcat?
class MainActivity : AppCompatActivity() {
lateinit var apiKey : String
var movies : MutableList<Movie> = mutableListOf()
var adapter = MovieAdapter(movies)
val movieService : MovieService = ApiClient.getClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
rvMovie.layoutManager = LinearLayoutManager(applicationContext)
rvMovie.adapter = adapter
apiKey = getString(R.string.api_key)
getPopularMovies(apiKey)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_menu, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return super.onOptionsItemSelected(item)
}
fun getPopularMovies(apiKey: String) {
val call : Call<MovieResult> = movieService.getPopularMovies(apiKey)
getMovieData(call)
}
fun getMovieData(call : Call<MovieResult>) {
call.enqueue(object : Callback<MovieResult> {
override fun onFailure(call: Call<MovieResult>?, t: Throwable?) {
Toast.makeText(applicationContext, "${t.toString()}", Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: Call<MovieResult>?, response: Response<MovieResult>?) {
if (response?.body() != null) {
movies = response.body()!!.movies.toMutableList()
adapter = MovieAdapter(movies)
rvMovie.adapter = adapter
// HOW DO I MEASURE THE DATA SPEED TRANSFER
Log.i("speedtest", "Data transfer speed is = 99Kb/s");
}
}
})
}
Example
Try this
#Streaming // make you add this, since you are downloading movie
#Get("bla/bla/bla") // any Http method you are using
fun getPopularMovies(apiKey : Int)
...
// where you consume the apiservice
val call : Call<MovieResult> = movieService.getPopularMovies(apiKey)
getMovieData(call)
fun getMovieData(call : Call<MovieResult>){
call.enqueue(object : Callback<MovieResult> {
override fun onFailure(call: Call<MovieResult>?, t: Throwable?) {
Toast.makeText(applicationContext, "${t.toString()}", Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: Call<MovieResult>?, response: Response<MovieResult>?) {
if (response?.body() != null) {
val moviebytes = ByteArray(1024 * 1024 * 1024) // also try this val bytes = byteArrayOf() if does not work
val inputStream = body.byteStream();
val numberOfbytes = inputStream.read(moviebytes)
log(Log.i("speedtest", "Data transfer speed is = ${numberOfbytes}bytes/s");
}
}
})
}