I want to get information using retrofit2
However, it is not possible due to the first error. How to solve this
this error is not infomation,,,,,
data class Beers(
val id : String,
val name : String,
val ph : Int,
val first_brewed : String
)
val retrofit = Retrofit.Builder()
.baseUrl("https://api.punkapi.com/v2/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(BeerService::class.java)
service.getBeers().enqueue(object : Callback<List<Beers>>{
override fun onResponse(call: Call<List<Beers>>, response: Response<List<Beers>>) {
if (response.isSuccessful) {
val body = response.body()
binding.tvRe.text = body.toString()
Log.d("Response :", response?.body().toString())
// body?.let {
//
// }
// Log.d("Response :", response?.body().toString())
}
}
override fun onFailure(call: Call<List<Beers>>, t: Throwable) {
Log.d("Error", call.toString())
}
})
}
I fixed the data class
val ph : Int --> val ph : Float
Related
Main Fragment:
here were initializing the recyclerview and the viewmodel and getting the data to submit to the adapter:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding: FragmentMainBinding = FragmentMainBinding.inflate(inflater, container, false)
this.binding = FragmentMainBinding.inflate(layoutInflater)
dataFromViewModel = arrayListOf()
setupViewModel()
setupList()
setupView()
return binding.root;
}
private fun setupView() {
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
viewModel.listData.collectLatest {
Log.e("Output Frg", it.toString())
if (!dataFromViewModel.contains(it)) {
dataFromViewModel.add(it)
mainAdapter.submitData(it)
mainAdapter.notifyDataSetChanged()
Log.e(TAG, "setupView: ${mainAdapter.repos.toString()}", )
}
}
}
}
private fun setupList() {
mainAdapter = MainAdapter()
val gridLayoutManager: GridLayoutManager = GridLayoutManager(context, 2)
binding.recyclerview.apply {
layoutManager = gridLayoutManager
adapter = mainAdapter
}
// mainAdapter.addLoadStateListener { loadState ->
//
// if (loadState.refresh is LoadState.Loading && !flag) {
// binding.progressbar.visibility = View.VISIBLE
// flag = true
// } else {
// binding.progressbar.visibility = View.GONE
// }
// }
}
private fun setupViewModel() {
viewModel =
ViewModelProvider(
this,
ViewModelFactory(RetrofitService.getApiService())
)[MainViewModel::class.java]
}
Main ViewModel:
here were just calling the postdatasource function and adding it to the listData value
class MainViewModel(private val apiService: RetrofitService) : ViewModel() {
val listData = Pager(PagingConfig(pageSize = 6)) {
PostDataSource(apiService)
}.flow.cachedIn(viewModelScope)
}
PostDataSource:
Here, we have extended PostDataSource with PagingSource which will implement a suspend load function which will help us to load the data.
PostDataSource also takes a primary constructor parameter APIService. PostDataSource acts here as a repository and the load function gets the data from the API.
Since the load function is a suspend function, we can call other suspend functions inside it without any issues which we created in APIService.
In the PostDataSource, we take two parameters one of integer type and other of the data type we have to load on the list item. The integer parameter represents the page number here.
Here, we get the page number from params and assign it to nextPage variable using param.key and if it returns null, we set a default value 1.
We also do the API call and get assign the response to the response variable using APIService which we passed as a constructor parameter to PostDataSource class.
After doing all the operations with the successful response, we return the LoadResult.Page object here with the required data and it something went wrong we use LoadResult.Error.
We are also passing null as the next key if there is no corresponding data in that direction.
class PostDataSource(private val apiService: RetrofitService) : PagingSource<Int, repo>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, repo> {
try {
val currentLoadingPageKey = params.key ?: 1
val response = apiService.getListData(currentLoadingPageKey)
val responseData = mutableListOf<repo>()
var repos : List<repo>
val data = response
responseData.addAll(data)
Log.e(TAG, "load: $responseData", )
val prevKey = if (currentLoadingPageKey == 1) null else currentLoadingPageKey - 1
return LoadResult.Page(
data = responseData,
prevKey = prevKey,
nextKey = currentLoadingPageKey.plus(1)
)
} catch (e: Exception) {
return LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, repo>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
// fun getAllrepos() = retrofitService.getAllrepos()
}
MainAdapter:
Now, we will update the MainListAdapter. We will extend the MainListAdapter with PagingDataAdapter and the PagingDataAdapter will take the type of data we need to display in the list and the ViewHolder.
It also takes a DiffUtil callback, as a parameter to its primary constructor which helps the PagingDataAdapter to update the items if they are changed or updated. And DiffUtil callback is used because they are more performant.
Now, the MainListAdapter looks like,
class MainAdapter : PagingDataAdapter<repo, MainAdapter.ViewHolder>(DiffCallBack) , Filterable {
private lateinit var Repos : List<repo>
class ViewHolder(view: View, mlistener: onItemClickListener) : RecyclerView.ViewHolder(view)
private lateinit var mlistener: onItemClickListener
interface onItemClickListener {
fun OnItemClick(position: Int)
}
var repos: MutableList<repo> = ArrayList()
private var reposFull: MutableList<repo> = ArrayList()
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.findViewById<TextView>(R.id.title).text = getItem(position)?.name
Log.e(TAG,"name ${holder.itemView.findViewById<TextView>(R.id.title).text}", )
Glide.with(holder.itemView.context)
.load(getItem(position)?.owner?.avatar_url)
.into(holder.itemView.findViewById(R.id.imgCircle))
// val repo = repos[position]
//
//
// val mainViewHolder: ViewHolder = holder as ViewHolder
// mainViewHolder.itemView Title?.text = repo.name
// mainViewHolder.Image?.let {
// Glide.with(holder.itemView.context).load(repo.owner.avatar_url).into(
// it
// )
// }
}
fun submitList(list: PagingData<repo>) {
Repos = list as List<repo>
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater
.from(parent.context)
.inflate(R.layout.repo_layout, parent, false), mlistener
)
}
fun setOnItemClickListener(listener: onItemClickListener) {
mlistener = listener
}
object DiffCallBack : DiffUtil.ItemCallback<repo>() {
override fun areItemsTheSame(oldItem: repo, newItem: repo): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: repo, newItem: repo): Boolean {
return oldItem == newItem
}
}
// class MainViewHolder(itemView: View, listener: onItemClickListener) : RecyclerView.ViewHolder(itemView) {
// var Title: TextView? =
// itemView.findViewById<View>(com.lau.google_rep.R.id.title) as TextView
// var Image: ImageView? =
// itemView.findViewById<View>(com.lau.google_rep.R.id.imgCircle) as ImageView
//
// init {
// itemView.setOnClickListener {
// listener.OnItemClick(adapterPosition)
// }
// }
// }
override fun getFilter(): Filter {
return exampleFilter
}
private val exampleFilter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: MutableList<repo> = ArrayList()
if (constraint == null || constraint.length == 0) {
filteredList.addAll(reposFull)
} else {
val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in reposFull) {
if (item.name.toLowerCase().contains(filterPattern)) {
filteredList.add(item)
}
}
}
Log.d(TAG, "publishResults: $filteredList")
val results = FilterResults()
results.values = filteredList
return results
}
#SuppressLint("NotifyDataSetChanged")
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
repos.clear()
(results.values as? Collection<repo>)?.let { repos.addAll(it) }
notifyDataSetChanged()
}
}
}
retrofit service:
interface RetrofitService {
// #Headers("Authorization: token ")
#GET("repos")
suspend fun getListData(#Query("page") pageNumber: Int): List<repo>
companion object{
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
fun getApiService() = Retrofit.Builder()
.baseUrl("https://api.github.com/orgs/google/")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
.create(RetrofitService::class.java)
}
}
Snapshot of my firebase realtime database
I want to extract the entire data under the "Orders" node, please tell me how should I model my data class for android in Kotlin?
I tried with this type of modeling,
After getting the reference of (Orders/uid/)
Order.kt
data class Order(
val items:ArrayList<Myitems>=ArrayList(),
val timeStamp:Long=0,
val totalCost:Int=0
)
MyItems.kt
data class MyItems(
val Item:ArrayList<Menu>=ArrayList()
)
Menu.kt
data class Menu(
val menCategory:String="",
val menName:String="",
val menImage:String="",
val menId:String="",
val menQuantity:Int=0,
val menCost:Int=0
)
After a lot of thinking and research online. I was finally able to model my classes and call add value event listener to it. Here it goes:
Order.kt
data class Order(
val items: ArrayList<HashMap<String, Any>> = ArrayList(),
val timeStamp: Long = 0,
val totalCost: Int = 0
)
OItem.kt
data class OItem(
val menCategory: String = "",
val menId: String = "",
val menImage: String = "",
val menName: String = "",
val menPrice: Int = 0,
var menQuantity: Int = 0
)
MainActivity.kt
val uid = FirebaseAuth.getInstance().uid
val ref = FirebaseDatabase.getInstance().getReference("Orders/$uid")
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {
//
}
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val order = it.getValue(Order::class.java)
ordList.add(order!!)
}
Log.d("hf", ordList.toString())
}
})
I migrated from paging 2 to paging 3. I tried to implement ItemKeyedDataSource of Paging 2 to Paging library 3. But the problem I was facing is, the same value(currentJodId) was passed as the nextkey in two sequential Pages loaded. And After that app crashes. but if I add "keyReuseSupported = true" in DataSource, app does not crash. But it started calling same item id as the nextkey.
JobSliderRestApi.kt
#GET("job/list/slides")
fun getDetailOfSelectedJob(
#Query("current_job") currentJodId: Int?,
#Query("limit") jobLimit: Int?,
#Query("search_in") fetchType: String?
): Single<Response<JobViewResponse>>
JobViewResponse.kt
data class JobViewResponse(
#SerializedName("data") val data: ArrayList<JobDetail>?
) : BaseResponse()
JobDetail.kt
data class JobDetail(
#SerializedName("job_id") val jobId: Int,
#SerializedName("tuition_type") val jobType: String?,
#SerializedName("class_image") val jobImage: String,
#SerializedName("salary") val salary: String,
#SerializedName("no_of_student") val noOfStudent: Int,
#SerializedName("student_gender") val studentGender: String,
#SerializedName("tutor_gender") val preferredTutor: String,
#SerializedName("days_per_week") val daysPerWeek: String?,
#SerializedName("other_req") val otherReq: String?,
#SerializedName("latitude") val latitude: Double?,
#SerializedName("longitude") val longitude: Double?,
#SerializedName("area") val area: String,
#SerializedName("tutoring_time") val tutoringTime: String?,
#SerializedName("posted_date") val postedDate: String?,
#SerializedName("subjects") val subjects: String,
#SerializedName("title") val title: String
)
JodSliderDataSource.kt
class JodSliderDataSource #Inject constructor(
private val jobSliderRestApi: JobSliderRestApi
): RxPagingSource<Int, JobDetail>() {
// override val keyReuseSupported = true
#ExperimentalPagingApi
override fun getRefreshKey(state: PagingState<Int, JobDetail>): Int? {
return state.anchorPosition?.let {
state.closestItemToPosition(it)?.jobId
}
}
override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, JobDetail>> {
return jobSliderRestApi.getDetailOfSelectedJob(42673, 2, "next").toSingle()
.subscribeOn(Schedulers.io())
.map { jobResponse -> toLoadResult(jobResponse.data) }
.onErrorReturn { LoadResult.Error(it) }
}
private fun toLoadResult(data: ArrayList<JobDetail>): LoadResult<Int, JobDetail> {
return LoadResult.Page(data = data, prevKey = null, nextKey = data.lastOrNull()?.jobId)
}
}
I was getting the same error and this is what worked for me. In the JodSliderDataSource class, toLoadResult method, set the nextKey parameter value by getting the page number from the response data and adding one.
private fun toLoadResult(
data: ArrayList<JobDetail>
): LoadResult<Int, JobDetail> {
return LoadResult.Page(
data = data,
prevKey = null,
nextKey = data.lastOrNull()?.jobId + 1 // Add one to the page number here.
)
}
How to search for data in RecyclerView?
I have model like this and I get data with retrofit. I have to make RecyclerView with model data like this and how to make Search from RecyclerView?
data class ListArticle(
#SerializedName("status") val status : String,
#SerializedName("totalResults") val totalResults : Int,
#SerializedName("articles") val articles : List<Articles>
)
data class Source (
#SerializedName("id") val id : String,
#SerializedName("name") val name : String
)
data class Articles (
#SerializedName("source") val source : Source,
#SerializedName("author") val author : String,
#SerializedName("title") val title : String,
#SerializedName("description") val description : String,
#SerializedName("url") val url : String,
#SerializedName("urlToImage") val urlToImage : String,
#SerializedName("publishedAt") val publishedAt : String,
#SerializedName("content") val content : String
)
Have to solve this question and I'm sorry for not completed question
just added this to your adapter
fun getFilter(): Filter? {
return articleFilter
}
private val articleFilter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: MutableList<Articles> = ArrayList()
if (constraint == null || constraint.length == 0) {
filteredList.addAll(articles)
} else {
val filterPattern =
constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in articles) {
if (item.title.toLowerCase().contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(
constraint: CharSequence?,
results: FilterResults
) {
searchArticles = results.values as List<Articles>
notifyDataSetChanged()
}
}
fun setData(article: List<Articles>){
searchArticles = article
notifyDataSetChanged()
}
I have a remote server from where I want to fetch 20 items(Job) per api call and show them in RecyclerView using paging library.
For that, I want to show a loading indicator at the beginning of the first api call when list of items is being fetched from the server. Everything is okay if data is fetched successfully. That means the loading indicator got invisible if data loaded successfully. The code is given bellow.
JobService.KT
#GET(Constants.API_JOB_LIST)
fun getJobPost(
#Query("page") pageNumber: Int
): Observable<Response<JobResponse>>
JobResponse.kt
data class JobResponse(
#SerializedName("status") val status: Int? = null,
#SerializedName("message") val message: Any? = null,
#SerializedName("data") val jobData: JobData? = null
)
JobData.kt
data class JobData(
#SerializedName("jobs") val jobs: List<Job?>? = null,
#SerializedName("total") val totalJob: Int? = null,
#SerializedName("page") val currentPage: Int? = null,
#SerializedName("showing") val currentlyShowing: Int? = null,
#SerializedName("has_more") val hasMore: Boolean? = null
)
NetworkState.kt
sealed class NetworkState {
data class Progress(val isLoading: Boolean) : NetworkState()
data class Failure(val errorMessage: String?) : NetworkState()
companion object {
fun loading(isLoading: Boolean): NetworkState = Progress(isLoading)
fun failure(errorMessage: String?): NetworkState = Failure(errorMessage)
}
}
Event.kt
open class Event<out T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled() = if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
fun peekContent() = content
}
JobDataSource.kt
class JobDataSource(
private val jobService: JobService,
private val compositeDisposable: CompositeDisposable
) : PageKeyedDataSource<Int, Job>() {
val paginationState: MutableLiveData<Event<NetworkState>> = MutableLiveData()
val initialLoadingState: MutableLiveData<Event<NetworkState>> = MutableLiveData()
val totalJob: MutableLiveData<Event<Int>> = MutableLiveData()
companion object {
private const val FIRST_PAGE = 1
}
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Job>) {
compositeDisposable += jobService.getJobPost(FIRST_PAGE)
.performOnBackgroundOutputOnMain()
.doOnSubscribe { initialLoadingState.postValue(Event(loading(true))) }
.doOnTerminate { initialLoadingState.postValue(Event(loading(false))) }
.subscribe({
if (it.isSuccessful) {
val jobData = it.body()?.jobData
totalJob.postValue(Event(jobData?.totalJob!!))
jobData.jobs?.let { jobs -> callback.onResult(jobs, null, FIRST_PAGE+1) }
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
CUSTOM_STATUS_CODE -> initialLoadingState.postValue(Event(failure(error.message!!)))
else -> initialLoadingState.postValue(Event(failure("Something went wrong")))
}
}
}, {
if (it is IOException) {
initialLoadingState.postValue(Event(failure("Check Internet Connectivity")))
} else {
initialLoadingState.postValue(Event(failure("Json Parsing error")))
}
})
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Job>) {
compositeDisposable += jobService.getJobPost(params.key)
.performOnBackgroundOutputOnMain()
.doOnSubscribe { if (params.key != 2) paginationState.postValue(Event(loading(true))) }
.doOnTerminate { paginationState.postValue(Event(loading(false))) }
.subscribe({
if (it.isSuccessful) {
val jobData = it.body()?.jobData
totalJob.postValue(Event(jobData?.totalJob!!))
jobData.jobs?.let { jobs -> callback.onResult(jobs, if (jobData.hasMore!!) params.key+1 else null) }
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
CUSTOM_STATUS_CODE -> initialLoadingState.postValue(Event(failure(error.message!!)))
else -> initialLoadingState.postValue(Event(failure("Something went wrong")))
}
}
}, {
if (it is IOException) {
paginationState.postValue(Event(failure("Check Internet Connectivity")))
} else {
paginationState.postValue(Event(failure("Json Parsing error")))
}
})
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Job>) {}
}
JobDataSourceFactory.kt
class JobDataSourceFactory(
private val jobService: JobService,
private val compositeDisposable: CompositeDisposable
): DataSource.Factory<Int, Job>() {
val jobDataSourceLiveData = MutableLiveData<JobDataSource>()
override fun create(): DataSource<Int, Job> {
val jobDataSource = JobDataSource(jobService, compositeDisposable)
jobDataSourceLiveData.postValue(jobDataSource)
return jobDataSource
}
}
JobBoardViewModel.kt
class JobBoardViewModel(
private val jobService: JobService
) : BaseViewModel() {
companion object {
private const val PAGE_SIZE = 20
private const val PREFETCH_DISTANCE = 20
}
private val jobDataSourceFactory: JobDataSourceFactory = JobDataSourceFactory(jobService, compositeDisposable)
var jobList: LiveData<PagedList<Job>>
init {
val config = PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setInitialLoadSizeHint(PAGE_SIZE)
.setPrefetchDistance(PREFETCH_DISTANCE)
.setEnablePlaceholders(false)
.build()
jobList = LivePagedListBuilder(jobDataSourceFactory, config).build()
}
fun getPaginationState(): LiveData<Event<NetworkState>> = Transformations.switchMap<JobDataSource, Event<NetworkState>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::paginationState
)
fun getInitialLoadingState(): LiveData<Event<NetworkState>> = Transformations.switchMap<JobDataSource, Event<NetworkState>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::initialLoadingState
)
fun getTotalJob(): LiveData<Event<Int>> = Transformations.switchMap<JobDataSource, Event<Int>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::totalJob
)
}
JobBoardFragment.kt
class JobBoardFragment : BaseFragment() {
private val viewModel: JobBoardViewModel by lazy {
getViewModel { JobBoardViewModel(ApiFactory.jobListApi) }
}
private val jobAdapter by lazy {
JobAdapter {
val bundle = Bundle()
bundle.putInt(CLICKED_JOB_ID, it.jobId!!)
navigateTo(R.id.jobBoard_to_jobView, R.id.home_navigation_fragment, bundle)
}
}
override fun getLayoutResId() = R.layout.fragment_job_board
override fun initWidget() {
job_list_recycler_view.adapter = jobAdapter
back_to_main_image_view.setOnClickListener { onBackPressed() }
}
override fun observeLiveData() {
with(viewModel) {
jobList.observe(this#JobBoardFragment, Observer {
jobAdapter.submitList(it)
})
getInitialLoadingState().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is Progress -> {
if (state == loading(true)) {
network_loading_indicator.visible()
} else {
network_loading_indicator.visibilityGone()
}
}
is Failure -> context?.showToast(state.errorMessage.toString())
}
}
})
getPaginationState().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is Progress -> {
if (state == loading(true)) {
pagination_loading_indicator.visible()
} else {
pagination_loading_indicator.visibilityGone()
}
}
is Failure -> context?.showToast(state.errorMessage.toString())
}
}
})
getTotalJob().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
job_board_text_view.visible()
with(profile_completed_image_view) {
visible()
text = state.toString()
}
}
})
}
}
}
But the problem is if data fetching failed due to internet connectivity or any other server related problem loading indicator does not invisible that means it still loading though I make the loadingStatus false and error message is shown. it means .doOnTerminate { initialLoadingState.postValue(Event(loading(false))) } is not called if error occured. This is the first problem. Another problem is loadInitial() and loadAfter() is being called simultaneously at the first call. But I just want the loadInitial() method is called at the beginning. after scrolling loadAfter() method will be called.
Try replacing all your LiveData's postValue() methods by setValue() or simply .value =.
The problem is that the postValue() method is for updating the value from a background thread to observers in the main thread. In this case you are always changing the values from the main thread itself, so you should use .value =.
Hope it's not too late.