Why data is not loaded to the RecyclerView MVVM? - android

I have an android application that requests a list from the network and should display it in a recycler view but this does not happen. There are no errors in the logcat. I think the problem is in my function in ViewModel. Help me to understand
My CategoryClient class:
class CategoryClient {
companion object {
const val KEY = "5de979d34658275ac9dc2375"
}
var category: List<Category>? = null
fun loadCategory(): List<Category>? {
// x-apikey interceptor for restdb API
fun createOkHttpClient(): OkHttpClient? {
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(object : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val original = chain.request()
val originalHttpUrl = original.url
val url = originalHttpUrl.newBuilder()
.addQueryParameter("apikey", KEY)
.build()
val requestBuilder = original.newBuilder()
.url(url)
val request = requestBuilder.build()
return chain.proceed(request)
}
})
// logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
httpClient.addInterceptor(logging)
return httpClient.build()
}
val retrofit = Retrofit.Builder()
.baseUrl("https://testcategory-d6d7.restdb.io/rest/")
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
.build()
val api = retrofit.create(CategoryApi::class.java)
api.fetchAllCategory().enqueue(object : Callback<List<Category>> {
override fun onFailure(call: Call<List<Category>>, t: Throwable) {
}
override fun onResponse(call: Call<List<Category>>, response: Response<List<Category>>) {
//Log.d(TAG, "onResponse: ${response.body()!![0].name}")
category = response.body()!!
//presenter.setupCategoryList(categoryList = category as ArrayList<Category>)
}
})
return category
}
}
My Category Activity class
val binding: ActivityCategoryBinding =
DataBindingUtil.setContentView(this#CategoryActivity, R.layout.activity_category)
val categoryViewModel =
ViewModelProviders.of(this#CategoryActivity).get(CategoryViewModel::class.java)
binding.categoryViewModel = categoryViewModel
categoryViewModel.getArrayList().observe(this#CategoryActivity, Observer { category ->
mAdapter = CategoryAdapter(this#CategoryActivity, categoryList = category )
recycler_category.layoutManager = LinearLayoutManager(applicationContext, OrientationHelper.VERTICAL, false)
recycler_category.adapter = mAdapter
recycler_category.setHasFixedSize(true)
})
My RecyclerView Adapter class
class CategoryAdapter(private val context: Context, private val categoryList: ArrayList<Category>?)
: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var mCategoryList: ArrayList<Category> = ArrayList()
private var mSourceList: ArrayList<Category> = ArrayList()
fun setupCategory(categoryList: ArrayList<Category>) {
mSourceList.clear()
mSourceList.addAll(categoryList)
search(query = "")
}
fun search(query: String) {
mCategoryList.clear()
mSourceList.forEach {
if (it.name.contains(query, ignoreCase = true)) {
mCategoryList.add(it)
}
}
notifyDataSetChanged()
}
fun sortByName() {
mCategoryList.sortBy { it.name }
notifyDataSetChanged()
}
fun sortByPrice() {
mCategoryList.sortBy { it.price }
notifyDataSetChanged()
}
fun filter() {
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is CategoryViewHolder) {
holder.bind(categoryModel = mCategoryList[position])
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val itemView = layoutInflater.inflate(R.layout.cell_category, parent, false)
return CategoryViewHolder(itemView = itemView)
}
override fun getItemCount(): Int {
return mCategoryList.count()
}
class CategoryViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
var mCategoryIcon: CircleImageView = itemView.findViewById(R.id.category_icon)
var mCategoryName: TextView = itemView.findViewById(R.id.category_name)
var mCategoryPrice: TextView = itemView.findViewById(R.id.category_price)
private var mCategoryType: TextView = itemView.findViewById(R.id.category_type)
fun bind(categoryModel: Category) {
categoryModel.icon.let { url ->
Picasso.with(itemView.context).load(url)
.into(mCategoryIcon)
}
mCategoryName.text = categoryModel.name
mCategoryPrice.text = categoryModel.price.toString()
mCategoryType.text = categoryModel.category
}
}
}
And my ViewModel class
class CategoryViewModel : ViewModel() {
var mutableLiveData = MutableLiveData<ArrayList<Category>>()
fun getArrayList(): MutableLiveData<ArrayList<Category>> {
mutableLiveData.value = CategoryClient().loadCategory() as ArrayList<Category>?
return mutableLiveData
}
}

Your data is not updated from asynchronous API call. Change you implementation like below to get updated data:
class CategoryViewModel : ViewModel() {
fun getArrayList(): MutableLiveData<ArrayList<Category>> {
return CategoryClient().loadCategory()
}
}
And your loadCategory
var mutableLiveData = MutableLiveData<ArrayList<Category>>()
fun loadCategory(): MutableLiveData<ArrayList<Category>> {
api.fetchAllCategory().enqueue(object : Callback<List<Category>> {
override fun onFailure(call: Call<List<Category>>, t: Throwable) {
}
override fun onResponse(call: Call<List<Category>>, response: Response<List<Category>>) {
mutableLiveData.postValue(response.body())
}
})
return mutableLiveData
}
And your adapter should be corrected to use single source
class CategoryAdapter(private val context: Context, private val mCategoryList: ArrayList<Category>?)
: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var mSourceList: ArrayList<Category> = ArrayList(mCategoryList)
.....
}

Related

Data From API Has Not Appeared Using Paging 3

I'm learning paging 3, but the data from the API doesn't appear. My code is like below:
interface PokeAPI {
#GET("pokemon")
fun getPokemonList() : Call<PokemonList>
#GET("pokemon")
fun getAllPokemon(
#Query("limit") limit: Int,
#Query("offset") offset: Int) : PokemonList
#GET("pokemon/{name}")
fun getPokemonInfo(
#Path("name") name: String
) : Call<Pokemon>
}
class PokePagingSource(private val apiService: PokeAPI): PagingSource<Int, Result>() {
private companion object {
const val INITIAL_PAGE_INDEX = 1
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Result> {
return try {
val position = params.key ?: INITIAL_PAGE_INDEX
val responseData = apiService.getAllPokemon(position, params.loadSize)
if (responseData.results.isEmpty()) {
Log.e("Response Succeed!", responseData.results.toString())
} else {
Log.e("Response Failed!", responseData.results.toString())
}
LoadResult.Page(
data = responseData.results,
prevKey = if (position == INITIAL_PAGE_INDEX) null else position - 1,
nextKey = if (responseData.results.isNullOrEmpty()) null else position + 1
)
} catch (exception: Exception) {
return LoadResult.Error(exception)
}
}
override fun getRefreshKey(state: PagingState<Int, Result>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
class PokemonRepository(private val apiService: PokeAPI) {
fun getAllPokemon(): LiveData<PagingData<Result>>{
return Pager(
config = PagingConfig(
pageSize = 10
),
pagingSourceFactory = {
PokePagingSource(apiService)
}
).liveData
}
}
object Injection {
private val api by lazy { RetrofitClient().endpoint }
fun provideRepository(): PokemonRepository {
return PokemonRepository(api)
}
}
class PokemonViewModel(pokemonRepository: PokemonRepository) : ViewModel() {
val allPokemonList: LiveData<PagingData<Result>> =
pokemonRepository.getAllPokemon().cachedIn(viewModelScope)
}
class ViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(PokemonViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return PokemonViewModel(Injection.provideRepository()) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
`class PokemonPagingAdapter(private val context: Context) :
PagingDataAdapter<Result, PokemonPagingAdapter.ViewHolder>(DIFF_CALLBACK) {
private var onItemClick: OnAdapterListener? = null
fun setOnItemClick(onItemClick: OnAdapterListener) {
this.onItemClick = onItemClick
}
class ViewHolder(val binding: AdapterPokemonBinding) : RecyclerView.ViewHolder(binding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
AdapterPokemonBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pokemonData = getItem(position)
if (pokemonData != null) {
holder.binding.apply {
val number = if (pokemonData.url.endsWith("/")) {
pokemonData.url.dropLast(1).takeLastWhile { it.isDigit() }
} else {
pokemonData.url.takeLastWhile { it.isDigit() }
}
val url = "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${number}.png"
Glide.with(context)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade())
.centerCrop()
.circleCrop()
.into(ivPokemon)
tvNamePokemon.text = pokemonData.name
btnDetail.setOnClickListener {
onItemClick?.onClick(pokemonData, pokemonData.name, url)
}
}
}
}
companion object {
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Result>() {
override fun areItemsTheSame(
oldItem: Result,
newItem: Result
): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(
oldItem: Result,
newItem: Result
): Boolean {
return oldItem.name == newItem.name
}
}
}
interface OnAdapterListener {
fun onClick(data: Result, name: String, url: String)
}
}`
class FragmentPokemon: Fragment(R.layout.fragment_pokemon) {
private var _binding : FragmentPokemonBinding? = null
private val binding get() = _binding!!
private lateinit var dataPagingAdapter: PokemonPagingAdapter
private val viewModel: PokemonViewModel by viewModels {
ViewModelFactory()
}
private lateinit var comm: Communicator
override fun onStart() {
super.onStart()
getData()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentPokemonBinding.bind(view)
val toolBar = requireActivity().findViewById<View>(R.id.tool_bar)
toolBar.visibility = View.VISIBLE
val navBar = requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
navBar.visibility = View.VISIBLE
comm = requireActivity() as Communicator
setupListPokemon()
}
private fun setupListPokemon(){
dataPagingAdapter = PokemonPagingAdapter(requireContext())
dataPagingAdapter.setOnItemClick(object: PokemonPagingAdapter.OnAdapterListener{
override fun onClick(data: Result, name: String, url: String) {
comm.passDataCom(name, url)
}
})
binding.apply {
rvPokemon.layoutManager = LinearLayoutManager(context)
rvPokemon.setHasFixedSize(true)
rvPokemon.adapter = dataPagingAdapter
}
}
private fun getData(){
viewModel.allPokemonList.observe(viewLifecycleOwner){
dataPagingAdapter.submitData(lifecycle, it)
binding.btnCoba.setOnClickListener { btn ->
if (it == null){
Log.e("ResponseFailed", it.toString())
} else Log.e("ResponseSucceed", it.toString())
}
}
}
}
What's the reason? I have followed the step by step implementation of paging 3 but the data still doesn't appear either.
I don't know the API you are using, but it seems that you are using it incorrectly. The getAllPokemon method has limit and offset parameters and you are calling it like apiService.getAllPokemon(position, params.loadSize), so you are using position as a limit and params.loadSize as an offset.
You should pass params.loadSize as a limit, rename INITIAL_PAGE_INDEX to INITIAL_OFFSET and set it to 0, since your API uses offsets instead of pages (at least it seems so from what you provided). The load function should then look something like this:
// get current offset
val offset = params.key ?: INITIAL_OFFSET
val responseData = apiService.getAllPokemon(limit = params.loadSize, offset = offset)
val prevKey = offset - params.loadSize
val nextKey = offset + params.loadSize

How to make recyclerview populate after response finishes

I'm having a problem with "UninitializedPropertyAccessException", I believe it's because the recyclerview is trying to be populated before the response finishes the process, does anyone know how I can solve it? P.S: This is causing my published app to crash, but in the emulator it works normally
TibiaApiService:
class TibiaApiService {
companion object {
private const val BASE_URL = "https://api.tibiadata.com"
private var retrofit: Retrofit? = null
fun getInstance(): Retrofit {
val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.build()
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
}
return retrofit!!
}
}}
NewsArchiveFragment:
class NewsArchiveFragment : Fragment() {
private lateinit var binding: FragmentNewsArchiveBinding
private lateinit var newsModel: List<NewsModel>
private lateinit var newsFromId: NewsFromIdModel
private lateinit var progressDialog: ProgressDialog
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentNewsArchiveBinding.inflate(layoutInflater, container, false)
progressDialog = ProgressDialog(activity)
getNews()
return binding.root
}
private fun getNews() = with(binding) {
try {
progressDialog.setMessage("Loading...")
progressDialog.show()
val apiService = TibiaApiService.getInstance().create(TibiaApiInterface::class.java)
apiService.getNewsArchive().enqueue(object : retrofit2.Callback<NewsResponse>,
NewsArchiveAdapter.ClickNews {
override fun onResponse(
call: Call<NewsResponse>,
response: Response<NewsResponse>
) {
if(response.isSuccessful) {
newsModel = response.body()!!.news
populateRV(newsModel, this)
}
}
override fun onFailure(call: Call<NewsResponse>, t: Throwable) {
progressDialog.dismiss()
Snackbar.make(root, t.message.toString(), Snackbar.LENGTH_LONG)
.show()
}
override fun clickNews(news: NewsModel) {
loadCompleteNews(news.id)
}
})
} catch (e: SocketTimeoutException) {
Snackbar.make(root, e.message.toString(), Snackbar.LENGTH_LONG)
.show()
}
}
private fun populateRV(
newsModel: List<NewsModel>,
clickNews: NewsArchiveAdapter. ClickNews
) = with(binding) {
rvNews.layoutManager = LinearLayoutManager(activity)
rvNews.setHasFixedSize(true)
rvNews.adapter = NewsArchiveAdapter(newsModel, clickNews)
rvNews.viewTreeObserver.addOnGlobalLayoutListener {
progressDialog.dismiss()
}
}
private fun loadCompleteNews(id: Int) = with(binding) {
val apiService = TibiaApiService.getInstance().create(TibiaApiInterface::class.java)
apiService.getNewsArchiveFromId(id)
.enqueue(object : retrofit2.Callback<NewsFromIdResponse> {
override fun onResponse(
call: Call<NewsFromIdResponse>,
response: Response<NewsFromIdResponse>
) {
newsFromId = response.body()!!.newsFromId
onShowDialog(newsFromId)
}
override fun onFailure(call: Call<NewsFromIdResponse>, t: Throwable) {
Snackbar.make(root, t.message.toString(), Snackbar.LENGTH_LONG).show()
}
})
}
private fun onShowDialog(newsFromId: NewsFromIdModel) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(newsFromId.date)
.setMessage(newsFromId.content)
.setNegativeButton(getString(R.string.close_dialog)) { dialog, _ ->
dialog.dismiss()
}
.show()
}}
NewsArchiveAdapter:
class NewsArchiveAdapter(
private val news: List<NewsModel>,
private val clickNews: ClickNews
) : RecyclerView.Adapter<NewsArchiveAdapter.ViewHolder>() {
interface ClickNews{
fun clickNews(news: NewsModel)
}
inner class ViewHolder(val binding: RvNewsListBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
RvNewsListBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val _news = news[position]
holder.binding.apply {
txtNewsNews.text = _news.news
txtNewsDate.text = _news.date
if (position % 2 == 0) {
cardViewNews.setBackgroundColor(
root.resources.getColor(R.color.very_light_brown)
)
} else {
cardViewNews.setBackgroundColor(
root.resources.getColor(R.color.light_brown)
)
}
cardViewNews.setOnClickListener { clickNews.clickNews(_news) }
}
}
override fun getItemCount(): Int = news.size}

Displaying images in recyclerview retrieved from retrofit

I'm new in android development and building a movie app in which I'm using recyclerview to display movie images and using retrofit to fetch data from themoviedb.org API.
I'm successfully getting the data from the mention URL but not able to display it on the screen.
GenreModel class:
data class Genre (
var genres: ArrayList<GenreList>
): Serializable
data class GenreList (
val id: Int,
val name: String,
val results: ArrayList<Results>
): Serializable
data class Results (
var id: Int,
var title: String,
var poster_path: String,
var release_date: String,
var original_language: String,
var overview: String
): Serializable
Function to get movie details from API:
private fun getGenreName() {
val retrofit = RetrofitClient.getInstance()
val apiInterface = retrofit.create(ApiInterface::class.java)
val genreListCall: Call<Genre> = apiInterface.getGenreDetails(Constants.APP_ID)
genreListCall.enqueue(object: Callback<Genre> {
override fun onResponse(call: Call<Genre>, response: Response<Genre>) {
if(response.isSuccessful) {
val result: Genre? = response.body() // returns genres object which contains ID and Name
val genres: ArrayList<GenreList> = result!!.genres// saves ID and Names of genres
for(x in genres) {
val id = x.id
val name = x.name // returns the genre names. Ex- action, family, drama....
val listCall: Call<GenreList> = apiInterface.getGenreMovieDetails(id, Constants.APP_ID)
listCall.enqueue(object: Callback<GenreList> {
override fun onResponse(call: Call<GenreList>, response: Response<GenreList>) {
if(response.isSuccessful) { // returns id, results[]
val result1: GenreList? = response.body()
results = result1!!.results // results[id, title, poster, language....]
}
}
override fun onFailure(call: Call<GenreList>, t: Throwable) {
Log.e("ERRORInGenreMovieList", t.message.toString())
}
})
genreList.add(GenreList(id, name, results))
genreCategories.add(name)
}
} else {
val rc = response.code()
when (rc) {
400 -> {
Log.e("Error 400", "Bad Connection")
}
404 -> {
Log.e("Error 404", "Not Found")
}
else -> {
Log.e("Error", "Generic Error")
}
}
}
}
override fun onFailure(call: Call<Genre>, t: Throwable) {
Log.e("ERROR in Genre Name", t.message.toString())
}
})
}
GenreAdapter for recyclerview:
class GenreFragmentAdapter(private val genreList: ArrayList<GenreList>, val context: Context): RecyclerView.Adapter<GenreFragmentAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var genreName:TextView = view.findViewById(R.id.genreName)
var genreListRecyclerView: RecyclerView = view.findViewById(R.id.genreListRecyclerView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View = LayoutInflater.from(context).inflate(R.layout.genre_list_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val name = genreList[position].name
val results = genreList[position].results
holder.genreName.text = name
println("Name: $name")
setItemRecyclerView(holder.genreListRecyclerView, results)
println("Results: $results")
}
override fun getItemCount(): Int {
return genreList.size
}
private fun setItemRecyclerView(recyclerView: RecyclerView, results: ArrayList<Results>) {
val genreListAdapter = GenreResultsAdapter(results, context)
recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
recyclerView.adapter = genreListAdapter
}
}
BaseUrl: https://api.themoviedb.org
ApiInterface:
interface ApiInterface {
#GET("/3/genre/movie/list") // returns genre list
fun getGenreDetails(
#Query("api_key") api_key: String
): Call<Genre>
#GET("/3/genre/{id}/movies") // returns movie details according to specified genre
fun getGenreMovieDetails(
#Path("id") id: Int,
#Query("api_key") api_key: String
): Call<GenreList>
#GET("/3/movie/upcoming") // returns upcoming movies
fun getUpcomingMovies(
#Query("api_key") api_key: String
): Call<GenreList>
#GET("/3/movie/{id}")
fun getMovie(
#Path("id") id: Int,
#Query("api_key") api_key: String
): Call<Results>
}
RetrofitClient:
object RetrofitClient {
fun getInstance(): Retrofit {
return Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getUnsafeOkHttpClient()!!.build())
.build()
}
private fun getUnsafeOkHttpClient(): OkHttpClient.Builder? {
return try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(
object : X509TrustManager {
#Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
}
#Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
}
override fun getAcceptedIssuers(): Array<X509Certificate?>? {
return arrayOf()
}
}
)
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
builder.hostnameVerifier { _, _ -> true }
builder
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}

my recyclerview not showing data it is only loading progressbar?

I am developing a new app and I have implemented recyclerview but it is not loading data only showing progress bar loading I did not understand where is the problem.
I am developing a new app and I have implemented recyclerview but it is not loading data only showing progress bar loading I did not understand where is problem
below my MemberActivity.kt class where I have implemented network call
class MemberActivity : AppCompatActivity() {
private var memberAdapter:MemberAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_member)
val compositeDisposable = CompositeDisposable()
compositeDisposable.add(
ServiceBuilder.buildService(SpectrumInterface::class.java).getMembers()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
{ members -> onResponse(members) },
{ t -> onFailure(t) })
)
}
private fun onFailure(t: Throwable) {
Toast.makeText(this, t.message, Toast.LENGTH_SHORT).show()
}
private fun onResponse(members: List<Member>) {
memberAdapter = MemberAdapter()
recyclerView.apply {
setHasFixedSize(true)
progressBar.visibility = View.GONE
layoutManager = LinearLayoutManager(this#MemberActivity)
adapter = memberAdapter
}
}
}
below my Adapter class
class MemberAdapter() : RecyclerView.Adapter<MemberAdapter.MemberViewHolder>() {
private val members = mutableListOf<Member>()
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): MemberAdapter.MemberViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.member_list, parent, false)
return MemberViewHolder(view)
}
override fun getItemCount(): Int {
return members.size
}
override fun onBindViewHolder(holder: MemberAdapter.MemberViewHolder, position: Int) {
return holder.bind(members[position])
}
class MemberViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val memberAge: TextView = itemView.findViewById(R.id.memberAge)
private val memberName: TextView = itemView.findViewById(R.id.memberName)
private val lastName: TextView = itemView.findViewById(R.id.lastName)
private val firstName:TextView = itemView.findViewById(R.id.firstName)
private val emailAddress:TextView = itemView.findViewById(R.id.emailAddress)
private val phone:TextView = itemView.findViewById(R.id.phone)
fun bind(member: Member) {
memberAge.text = member.age.toString()
memberName.text = member.name.toString()
lastName.text = member.name.last
firstName.text = member.name.first
emailAddress.text = member.email
phone.text = member.phone
}
}
}
below my ServiceBuilder.kt
object ServiceBuilder {
private val client = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build()
private val retrofit = Retrofit.Builder()
.baseUrl("https://api.npoint.io/")
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}
}
below
SpectrumInterface.kt
interface SpectrumInterface {
#GET("15c43d65bc7a989f47f1")
fun getMembers(): Observable<List<Member>>
}
below Member.kt
data class Member(
#SerializedName("age")
val age: Int,
#SerializedName("email")
val email: String,
#SerializedName("_id")
val id: String,
#SerializedName("name")
val name: Name,
#SerializedName("phone")
val phone: String
)
in your adpater since you are create a new one every time you receive data do the following
class MemberAdapter(private val members: List<Member>())
to initialize do
MemberAdapter(memebers)
then after this code
recyclerView.apply {
setHasFixedSize(true)
progressBar.visibility = View.GONE
layoutManager = LinearLayoutManager(this#MemberActivity)
adapter = memberAdapter
}
memberAdapter.notififyDataSetChanged()
Proper Solution
Make only one MemberAdpater
so transform this into Immutable
in the activity
private val memberAdapter:MemberAdapter = MemberAdapter()
then in adapter make a function that replaces the data
something like
private val members: mutableListOf<Member>()
fun setMembers(data: List<Member>) {
members.clear()
members.addAll(data)
this.notifyDataSetChanged()
}

parse JSON object using retrofit in kotlin

I am trying to show json data using retrofit library in kotlin
This is my Json:
[
{
"login": "mojombo",
"id": 1,
},
{
"login": "defunkt",
"id": 2,
}
]
My Main activity
call.enqueue(object : Callback<UserResponse> {
override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
Log.e("list","list")
val countrylist = response.body()
for (size in response.body()) {
System.out.println(size.toString())
}
// var listOfMovies: List<UserResponse> = response.body()?.results!!
// myCustomAdapter = UserListAdapter(applicationContext, listOfMovies)
// recyclerView.setAdapter(myCustomAdapter)
progressBar.visibility = View.GONE
}
override fun onFailure(call: Call<UserResponse>?, t: Throwable?) {
progressBar.visibility = View.GONE
Log.e("list", t.toString())
}
})
This an app that I build in kotlin using retrofit and rxjava in the best way possible using a test API.
Model
data class Post( val userID:Int, val title:String, val body: String)
Retrofit Package
IMyApi interface
interface IMyApi {
#get:GET("posts")
val posts: Observable<List<Post>>
}
RetrofitClient Object class
object RetrofitClient {
val instance: Retrofit by lazy {
Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
Adapter Package
PostAdapter class
class PostAdapter(private val context: Context, private val postList: List<Post>)
:RecyclerView.Adapter<PostViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
PostViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.post_item, parent, false)
return PostViewHolder(itemView)
}
override fun getItemCount(): Int {
return postList.size
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int)
{
holder.userId.text = postList[position].userID.toString()
holder.title.text = postList[position].title
holder.body.text = StringBuilder(postList[position].body.substring(0,20))
.append("...").toString()
}
}
PostViewHolder class
class PostViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {
var userId = itemView.txtID
var title = itemView.txtTitle
var body = itemView.txtBody
}
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var jsonApi: IMyApi
private var compositeDisposable: CompositeDisposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Init api
val retrofit = RetrofitClient.instance
jsonApi = retrofit.create(IMyApi::class.java)
// View
recycler_posts.layoutManager = LinearLayoutManager(this)
recycler_posts.setHasFixedSize(true)
fetchData()
}
private fun fetchData() {
compositeDisposable.add(jsonApi.posts
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { posts->displayData(posts)})
}
private fun displayData(posts: List<Post>?) {
val adapter = PostAdapter(this, posts!!)
recycler_posts.adapter = adapter
}
}
Using this as displayed above should help you solve your issue hopefully. Also when in the code you come across "recycler_posts". This is a id to the recycler added in activity_main. If you need me to include that let me know
That's what we have on our app
object GetFAQsAPI {
private val LOG_TAG = GetFAQsAPI.javaClass.simpleName
interface ThisCallback {
fun onSuccess(getFAQs: GetFAQs)
fun onFailure(failureMessage: String)
fun onError(errorMessage: String)
}
/* POST */
fun postData(jo: JsonObject, callback: GetFAQsAPI.ThisCallback) {
val call = Service.getService().get_faqs(jo)
call.enqueue(object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
//Log.e(LOG_TAG, response.body().toString());
try {
if (response.body()?.get("success")!!.asBoolean) {
val gson = GsonBuilder().setPrettyPrinting().create()
val getFAQs = gson.fromJson(response.body(), GetFAQs::class.java)
callback.onSuccess(getFAQs)
} else {
Log.e(LOG_TAG, "else")
val error = response.body()!!.get("err").asString
callback.onError(error)
}
} catch (e: Exception) {
Log.e(LOG_TAG, "exception" + e.localizedMessage)
callback.onFailure(e.message!!)
}
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
Log.e(LOG_TAG, "onFailure: " + t.message)
callback.onFailure(t.message!!)
}
})
}
}
That's how we call it from our fragment - getFAQs is the object parsed.
private fun getFAQsAPI() {
showLoading(true)
val jo = JsonObject().apply {
addProperty("faq_category", "admin")
}
GetFAQsAPI.postData(jo, object : GetFAQsAPI.ThisCallback {
override fun onSuccess(getFAQs: GetFAQs) {
Log.i(LOG_TAG, "onSuccess")
showLoading(false)
updateUI(getFAQs)
}
override fun onFailure(failureMessage: String) {
Log.e(LOG_TAG, failureMessage)
}
override fun onError(errorMessage: String) {
Log.e(LOG_TAG, errorMessage)
}
})
}
Hope that helps.

Categories

Resources