I want to fetch some json data, see in the image the green arrow:
The problem is that Android Studio doesn't let me get the data I want. It stops until a step before (I think). In my adapter class check:
holder?.view?.textWeather?.text = weatherFor.weather.toString()
Also it shows me in the emulator the red arrow, what is this?
Below is my main Activity's json method with the classes i want to fetch data for, and the associated Adapter class.
Main Activity
fun fetchJson() {
val url="https://api.openweathermap.org/data/2.5/forecast?q=Prague,CZ&appid=4cf7f6610d941a1ca7583f50e7e41ba3"
val request=Request.Builder().url(url).build()
val client= OkHttpClient()
client.newCall(request).enqueue(object :Callback {
override fun onResponse(call: Call?, response: Response?) {
val body=response?.body()?.string()
println(body)
val gson=GsonBuilder().create()
val forecastfeed=gson.fromJson(body,ForecastFeed::class.java)
runOnUiThread{
recyclerView_main.adapter=MainAdapter(forecastfeed)
}
}
override fun onFailure(call: Call?, e: IOException?) {
println("Failed to execute request")
}
})
}
class ForecastFeed(val list:List<ForecastWeatherList>) { }
class ForecastWeatherList(val weather:List<WeatherData>) { }
class WeatherData(val main:String,val icon:String) { }
Adapter
class MainAdapter(val forecastfeed: ForecastFeed): RecyclerView.Adapter<CustomViewHolder>() {
val forecastWeather = listOf<String>("First","Second")
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val weatherFor = forecastfeed.list.get(position)
holder?.view?.textWeather?.text = weatherFor.weather.toString()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder{
//how do we even create a view
val layoutInflater =LayoutInflater.from(parent?.context)
val cellForRow=layoutInflater.inflate(R.layout.weather_row,parent,false)
return CustomViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return forecastfeed.list.count()
}
}
class CustomViewHolder(val view: View):RecyclerView.ViewHolder(view) { }
You can format the data manually
holder?.view?.textWeather?.text = "weather ${weatherFor.weather.map{it.main}.joinToString(", ")}"
or use data classes
You need to overwrite WeatherData.toString() to have a hand on what's displayed.
class WeatherData(val main:String,val icon:String) {
override fun toString(): String {
return "$main $icon"
}
}
Further more you should use a RecyclerView with a ViewHolder to handle properties one-by-one and enable more complex layouts. If needed.
Related
I'm new to kotlin and I could not find related solutions.
What I want to do is get the data from api(amount and currency) and show it onclick.
I was able to loop data but I don't know how to unloop.
The response from api is this:
{
"data": {
"amount": 825,
"currency": "hkd"
}
}
My Model:
data class MainData(
var data: AmountData
)
data class AmountData(
val amount: Int,
val currency: String,
)
My ApiService:
interface ApiService {
#GET("posts")
fun getPosts(): Call<MutableList<PostModel>>
#GET("checkout/vend/CLIENT_ID/payment/request")
fun paymentRequest(): Call<MainData>
}
My Adapter:
class PaymentAdapter(private val mainData: MainData): RecyclerView.Adapter<PaymentViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PaymentViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.card_post, parent, false)
return PaymentViewHolder(view)
}
override fun onBindViewHolder(holder: PaymentViewHolder, position: Int) {
return holder.bindView(mainData) // I don't even know how to bind the data
}
override fun getItemCount(): Int {
return mainData.data.amount // This is also incorrect but I don't know what to do
}
}
class PaymentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
private val tvAmount: TextView = itemView.findViewById(R.id.tvAmount)
private val tvCurrency: TextView = itemView.findViewById(R.id.tvCurrency)
fun bindView(mainData: MainData){
tvAmount.text = mainData.data.amount.toString()
tvCurrency.text = mainData.data.currency
}
}
This is the result so far.
because you only have 1 item always you could just do
override fun getItemCount(): Int {
return 1
}
And it might give already exactly what you want
Though, it really is unnecessary to use a RecyclerView for this then. I would remove the RecyclerView and just add two TextViews or something.
I am doing a school project.
I have a list with Doses, so I need to fetch data en set text one by one.
Right now I'm getting:
kotlin.UninitializedPropertyAccessException: lateinit property medicine has not been initialized.
So I need to wait till the first item is fetched and set before continuing to next item.
can you help me?
class ClientDoseListAdapter(private val doses: List<Dose>) : RecyclerView.Adapter<ClientDoseListAdapter.ViewHolder>() {
private lateinit var medicine : Medicine
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.client_dose_listitem, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = doses[position]
runBlocking {
displayMedicine(item.medicine)
}
holder.med_name.text = medicine.name
holder.dose_amount.text = item.amount.toString()
}
private suspend fun displayMedicine(id: Int) {
fetchMedicine(id)
}
override fun getItemCount(): Int = doses.size
inner class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView),
LayoutContainer
private fun fetchMedicine(id: Int) {
service.getMedicine(id, "Bearer ${ClienOverzichtFragment.auth}")
.enqueue(object : Callback<List<Medicine>> {
override fun onResponse(call: Call<List<Medicine>>, response: Response<List<Medicine>>) {
if (response.code() == 200) {
val temp = response.body()!!
medicine = temp[0]
Log.v("SHIT", medicine.name)
} else {
Log.v("SHIT", response.code().toString())
//TODO
}
}
override fun onFailure(call: Call<List<Medicine>>, t: Throwable) {
Log.v("SHIT", "FAILED : "+t.message)
}
})
}
}
Move your service call out of the Recycler (best into a ViewModel, but can call from Activity or using any other pattern - the main thing, shouldn't be part of the Recycler) and pass the data, when it's received, into the Recycler.
Your ClientDoseListAdapter to accept medicine:
class ClientDoseListAdapter(private val doses: List<Dose>, private val medicine: Medicine)
In your activity, initiate and a call for medicine and observe it - when the data arrives, pass it to the adapter. Assuming you use a view model your code in Activity would look something like this:
viewModel.getMedicine().observe(
this,
Observer<Medicine> { medicine ->
//assuming doses come from somewhere else
adapter = ClientDoseListAdapter(doses, medicine, this)
clientDoseRecyclerView.adapter = adapter
}
)
I am trying to display navigation bar title in my TestProjectList class activity but the value is empty so, I am not able to see the Navigation bar tile. I am not sure why its showing empty Value. Your help is appreciated.
Model Class:
class TestProject(val name: String,val location: String)
Main Class:
private class ItemDetailAdapter(val TestProjectList:Array<TestProject>): RecyclerView.Adapter<ItemDetailViewHolder>()
{
override fun onBindViewHolder(p0: ItemDetailViewHolder, p1: Int) {
val TestProject=TestProjectList.get(p1)
p0?.customView?.TestProjectName?.text=TestProject.name
val TestProjectPicture=p0?.customView?.itemPicture
Picasso.get().load(TestProject.location).into(TestProjectPicture)
}
override fun getItemCount(): Int {
return TestProjectList.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ItemDetailViewHolder {
val layoutInflater=LayoutInflater.from(p0?.context)
val customView=layoutInflater.inflate(R.layout.items_details,p0,false)
return ItemDetailViewHolder(customView)
}
}
class ItemDetailViewHolder(val customView:View,var Title: TestProject?=null):RecyclerView.ViewHolder(customView)
{
companion object {
val ITEM_TITLE_KEY="TestProject"
}
init {
customView.setOnClickListener {
val intent= Intent(customView.context,TestProjectMenuList::class.java)
intent.putExtra(ITEM_TITLE_KEY,Title?.name)
print("Printting Title :$Title?.name")
println("Hello Test $ITEM_TITLE_KEY")
customView.context.startActivity(intent)
println("Test")
}
}
TestProjectList Class:
val navBarTitle=intent.getStringExtra(MainClass.ItemDetailViewHolder.ITEM_TITLE_KEY)
supportActionBar?.title=navBarTitle
When you are creating your viewholder in the adapter return ItemDetailViewHolder(customView) you aren't passing any value for the parameter Title. You aren't setting it up latter either, but you are populating the intent with intent.putExtra(ITEM_TITLE_KEY,Title?.name). In this case the value you will always retrieve from the intent will be null.
Model Class:
class TestProject(val name: String,val location: String)
Main Class:
private class ItemDetailAdapter(val TestProjectList:Array<TestProject>): RecyclerView.Adapter<ItemDetailViewHolder>()
{
override fun onBindViewHolder(p0: ItemDetailViewHolder, p1: Int) {
val TestProject=TestProjectList.get(p1)
p0?.customView?.TestProjectName?.text=TestProject.name
val TestProjectPicture=p0?.customView?.itemPicture
Picasso.get().load(TestProject.location).into(TestProjectPicture)
//Below code solved the Title Problem
p0?.Title=TestProject
}
override fun getItemCount(): Int {
return TestProjectList.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ItemDetailViewHolder {
val layoutInflater=LayoutInflater.from(p0?.context)
val customView=layoutInflater.inflate(R.layout.items_details,p0,false)
return ItemDetailViewHolder(customView)
}
}
class ItemDetailViewHolder(val customView:View,var Title: TestProject?=null):RecyclerView.ViewHolder(customView)
{
companion object {
val ITEM_TITLE_KEY="TestProject"
}
init {
customView.setOnClickListener {
val intent= Intent(customView.context,TestProjectMenuList::class.java)
intent.putExtra(ITEM_TITLE_KEY,Title?.name)
print("Printting Title :$Title?.name")
println("Hello Test $ITEM_TITLE_KEY")
customView.context.startActivity(intent)
println("Test")
}
}
TestProjectList Class:
val navBarTitle=intent.getStringExtra(MainClass.ItemDetailViewHolder.ITEM_TITLE_KEY)
supportActionBar?.title=navBarTitle
I'm using Paging Library with PagedListAdapter, I'm using BoundaryCallback in PagedList to know when the user reaches the end of the list. The problem is that the method in my callback never gets called.
This is my data source code:
class PropertyDataSource : ItemKeyedDataSource<Int, Property>() {
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Property>) {
callback.onResult(getProperties(1, params.requestedLoadSize))
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Property>) {
Thread.sleep(1000)
callback.onResult(getProperties(params.key + 1, params.requestedLoadSize + params.key))
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Property>) {
}
override fun getKey(item: Property): Int {
return item.id
}
}
my code for boundary callback is
class MyBoundryCallBack: PagedList.BoundaryCallback<Property>() {
override fun onItemAtEndLoaded(itemAtEnd: Property) {
Log.e("alz", "at end $itemAtEnd")
}
override fun onItemAtFrontLoaded(itemAtFront: Property) {
Log.e("alz", "at front $itemAtFront")
}
override fun onZeroItemsLoaded() {
Log.e("alz", "zero item loaded")
}
}
here is my activity code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = MyListAdapter(PropertyDiffCallback())
mainRecyclerView.adapter = adapter
mainRecyclerView.layoutManager = LinearLayoutManager(this)
val propertyDataSourceFactory = PropertyDataSourceFactory()
val config = PagedList.Config.Builder()
.setPageSize(2)
.setInitialLoadSizeHint(3)
.setEnablePlaceholders(false)
.build()
val pagedList = LivePagedListBuilder(propertyDataSourceFactory, config)
.setBoundaryCallback(MyBoundryCallBack())
.build()
pagedList.observe(this, Observer {
adapter.submitList(it)
}
}
And my code for Adapter is look like this
class MyListAdapter(diffCallback: DiffUtil.ItemCallback<Property>) :
PagedListAdapter<Property, RecyclerView.ViewHolder>(diffCallback) {
class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val titleTV: TextView = itemView.findViewById(R.id.title)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ItemHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_property, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if(getItem(position) == null) return
val itemHolder = holder as ItemHolder
itemHolder.titleTV.text = getItem(position)!!.title
}
}
And here is my implementation of getProperties() that will return some mock data. Later on, I'm going to change it to load data from the server.
fun getProperties(from: Int, to: Int): List<Property> {
val list = ArrayList<Property>()
for (i in from..to){
list.add(Property(i, "item $i"))
}
return list
}
Note that, my code works fine and my adapter requesting more data as it goes to the end, I just don't get the callback when PagedList gets to the end.
I'm a novice Android developer. I'm trying to build a movie database app using tmdb. For this, I am using the Kotlin language and chose to use Retrofit and GSON for my JSON parsing and HTTP calls. However, I haven't done this before. I went through multiple tutorials but one is different than the other and my A.D.D. doesn't really help when it comes to deriving the concepts.
For now, this is my code. All it does is take a placeholder image and display it in a 3 column recyclerview grid layout 300x (random number because I don't have a list size yet):
MainActivity.kt
class MainActivity() : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view);
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.layoutManager = GridLayoutManager(this, 3);
recyclerView.adapter = PosterAdapter()
}
}
PosterAdapter.kt
class PosterAdapter() : RecyclerView.Adapter<PosterHolder>(){
override fun getItemCount(): Int { return 300}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PosterHolder{
val layoutInflater = LayoutInflater.from(parent.context)
val listItem = layoutInflater.inflate(R.layout.list_item, parent, false)
return PosterHolder(listItem)
}
override fun onBindViewHolder(holder: PosterHolder, position: Int) {
holder.view.movie_poster?.setImageResource(R.mipmap.beauty_and_the_beast_ver3)
holder.view.movie_poster?.scaleType = ImageView.ScaleType.FIT_XY
}
}
class PosterHolder(val view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
var imageView: ImageView? = null
fun PosterHolder(view: View){ this.imageView = view.findViewById<View>(R.id.movie_poster) as ImageView }
override fun onClick(p0: View?) {}
}
I don't want someone to cook the code for me. I can't learn that way. I would appreciate a simple step-by-step explanation on how to implement both libraries in my app.
Here is a simple code snippits for how you can setup Retrofit with Kotlin.
First, the api interface where you will define your network calls. For example:
interface ServerAPI {
#FormUrlEncoded
#POST("/api/v1/login")
fun login(#Header("Authorization") authorization: String): Call<LoginResponse>
}
Then, create Retrofit instance. Personally, I prefer to make a kotlin object and intialize it with by lazy. Also, notice the GsonConverterFactory
object RetrofitServer {
val client: ServerAPI by lazy {
val client = OkHttpClient.Builder()
.build()
val retrofit = Retrofit.Builder()
.baseUrl("http://127.0.0.1/")
// For Gson
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
retrofit.create(ServerAPI::class.java)
}
}
Then that's it. To make a call, simply
RetrofitServer.client.login("Auth header").enqueue(object: Callback<BalanceResponse> {
override fun onFailure(call: Call<BalanceResponse>?, t: Throwable?) {
}
override fun onResponse(call: Call<BalanceResponse>?, t: Response<BalanceResponse>?) {
}
}
)