my recyclerview not showing data it is only loading progressbar? - android

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()
}

Related

Kotlin Paging 3 using Recycler View but List did not showing

I used Paging 3 for my Recycler View that had to show some stories in the application. But while I used Paging 3, the lists did not show the item. Before I used Paging 3, the items had been showing including the details.
StoriesResponseItem.kt
#Entity(tableName = "story")
data class StoriesResponseItem(
#PrimaryKey
#field:SerializedName("id")
val id: String,
#field:SerializedName("photoUrl")
val photoUrl: String,
#field:SerializedName("createdAt")
val createdAt: String,
#field:SerializedName("name")
val name: String,
#field:SerializedName("description")
val description: String,
#field:SerializedName("lon")
val lon: Double? = null,
#field:SerializedName("lat")
val lat: Double? = null
)
ApiService.kt
interface ApiService {
#GET("stories")
suspend fun getPagingStories(
#Header("Authorization") Authorization: String,
#Query("page") page: Int,
#Query("size") size: Int
): List<StoriesResponseItem>
}
StoriesPagingSource.kt
class StoriesPagingSource(private val token: String, private val apiService: ApiService) : PagingSource<Int, StoriesResponseItem>() {
private companion object {
const val INITIAL_PAGE_INDEX = 1
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, StoriesResponseItem> {
return try {
val position = params.key ?: INITIAL_PAGE_INDEX
val responseData = apiService.getPagingStories(token, position, params.loadSize)
LoadResult.Page(
data = responseData,
prevKey = if (position == INITIAL_PAGE_INDEX) null else position - 1,
nextKey = if (responseData.isEmpty()) null else position + 1
)
} catch (retryableError: Exception) {
LoadResult.Error(retryableError)
}
}
override fun getRefreshKey(state: PagingState<Int, StoriesResponseItem>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
StoriesRepository.kt
class StoriesRepository(private val apiService: ApiService) {
fun getStories(token: String): LiveData<PagingData<StoriesResponseItem>> {
return Pager(
config = PagingConfig(
pageSize = 5
),
pagingSourceFactory = {
StoriesPagingSource(token, apiService)
}
).liveData
}
}
MainViewModel.kt
class MainViewModel(private val storiesRepository: StoriesRepository) : ViewModel() {
private val _stories = MutableLiveData<PagingData<StoriesResponseItem>>()
fun stories(token: String): LiveData<PagingData<StoriesResponseItem>> {
val response = storiesRepository.getStories(token).cachedIn(viewModelScope)
_stories.value = response.value
return response
}
}
class ViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return MainViewModel(Injection.provideRepository(context)) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val mainViewModel: MainViewModel by viewModels {
ViewModelFactory(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (applicationContext.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
val layoutManager = GridLayoutManager(this, 2)
binding.rvStories.layoutManager = layoutManager
} else {
val layoutManager = LinearLayoutManager(this)
binding.rvStories.layoutManager = layoutManager
}
val pref = AppDataStore.getInstance(dataStore)
val authViewModel = ViewModelProvider(this, ViewModelFactory(pref))[AuthViewModel::class.java]
authViewModel.loginToken().observe(this) { token: String? ->
val loginToken = "Bearer $token"
getData(loginToken)
}
}
private fun getData(loginToken: String) {
val adapter = ListStoriesAdapter()
binding.rvStories.adapter = adapter.withLoadStateFooter(
footer = LoadingStateAdapter {
adapter.retry()
}
)
lifecycleScope.launch {
mainViewModel.stories(loginToken).observe(this#MainActivity) {
adapter.submitData(lifecycle, it)
}
}
}
}
This is my repository for this project :
https://github.com/daffakurnia11/StoryApp

One-to-many in Room with Kotlin

The task is to open an activity with notes attached to this diary when you select a single diary.
(one-to-many)
This is how entities in the database look like:
#Entity(tableName = "word_table")
data class Word(#ColumnInfo(name = "word") val word: String,
#ColumnInfo(name = "description") val description : String
)
{
#ColumnInfo(name = "id")
#PrimaryKey(autoGenerate = true)
var id : Long = 0
}
#Entity(tableName = "note_table")
data class Note(#ColumnInfo(name = "note_name") val note : String,
#ColumnInfo(name = "text") val text : String,
#ColumnInfo(name = "diaryId") val diaryId : Long
){
#PrimaryKey(autoGenerate = true)
var idNote : Long = 0
}
Using a data class in NoteRepository.kt
data class NotesAndWords (#Embedded val word : Word,
#Relation(parentColumn = "id", entityColumn = "diaryId")
val notes : List<Note>)
And a Query in WordDao.kt
#Transaction
#Query("SELECT * from word_table ")
fun getSomeNotes() : LiveData<List<NotesAndWords>>
I get the data and save it in the NoteRepository class:
class NoteRepository (private val wordDao : WordDao) {
var allNotes : LiveData<List<NotesAndWords>> = wordDao.getSomeNotes()
suspend fun insertNote(note : Note)
{
wordDao.insertNote(note)
}
}
Then via NoteViewModel.kt passing data to NoteActivity.kt:
class NoteViewModel(application: Application) : AndroidViewModel(application) {
private val repository: NoteRepository
val allNotes: LiveData<List<NotesAndWords>>
init {
val wordsDao = WordRoomDatabase.getDatabase(application, viewModelScope).wordDao()
repository = NoteRepository(wordsDao)
allNotes = repository.allNotes
}
fun insertNote(note: Note) = viewModelScope.launch {
repository.insertNote(note)
}
}
(NoteActivity.kt)
class NoteActivity : AppCompatActivity() {
private val newWordActivityRequestCode = 1
private lateinit var noteViewModel: NoteViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_note)
val adapter = NoteListAdapter(this, intent.getLongExtra("tag", -1) ){
val intent = Intent(this, ClickedActivity::class.java)
intent.putExtra("tag", it.note)
startActivity(intent)
}
recyclerview1.adapter = adapter
recyclerview1.layoutManager = LinearLayoutManager(this)
noteViewModel = ViewModelProvider(this).get(NoteViewModel::class.java)
noteViewModel.allNotes.observe(this, Observer {
adapter.setNotes(it)
})
val fab = findViewById<FloatingActionButton>(R.id.fab)
fab.setOnClickListener {
val intent = Intent(this, NewWordActivity::class.java)
startActivityForResult(intent, newWordActivityRequestCode)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == newWordActivityRequestCode && resultCode == Activity.RESULT_OK)
{
data?.getStringArrayListExtra(NewWordActivity.EXTRA_REPLY)?.let {
val note = Note(it[0], it[1], intent.getLongExtra("tag", -1))
noteViewModel.insertNote(note)
}
}
else
{
Toast.makeText(applicationContext, R.string.empty_not_saved,
Toast.LENGTH_LONG).show()
}
}
Then, in the adapter, I use MutableMap to transform the list so that the key is the name id and the value is the notes selected on request (attached to a specific diary)
NoteListAdapter.kt:
class NoteListAdapter internal constructor(
context: Context,
val wordId: Long,
private val listener : (Note) -> Unit
) : RecyclerView.Adapter<NoteListAdapter.NoteViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
//private val mContext = context
private var notes = emptyList<NotesAndWords>() // Cached copy of words
private var notesMapped = mutableMapOf<Long, List<Note>>()
inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val noteItemView: TextView = itemView.findViewById(R.id.textView1)
private val noteDescriptionView: TextView = itemView.findViewById(R.id.textView)
fun bindView(note: Note, listener : (Note) -> Unit) {
noteItemView.text = note.diaryId.toString()
noteDescriptionView.text = note.text
itemView.setOnClickListener {
listener(note)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
val itemView = inflater.inflate(R.layout.recyclerview_layout, parent,
false)
return NoteViewHolder(itemView)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.bindView(notesMapped[wordId]!![position], listener)
}
internal fun setNotes(notes: List<NotesAndWords>) {
this.notes = notes
for (i in this.notes) {
notesMapped[i.word.id] = i.notes
}
notifyDataSetChanged()
}
override fun getItemCount() = notesMapped[wordId]!!.size
}
Database:
#Database(entities = [Word::class, Note::class], version = 2, exportSchema = false)
abstract class WordRoomDatabase : RoomDatabase() {
abstract fun wordDao(): WordDao
private class WordDatabaseCallback(private val scope: CoroutineScope) : RoomDatabase.Callback()
{
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
INSTANCE?.let { database ->
scope.launch {
populateDatabase(database.wordDao())
}
}
}
suspend fun populateDatabase(wordDao: WordDao) {
//wordDao.deleteAll()
//wordDao.deleteAllNotes()
}
}
companion object {
#Volatile
private var INSTANCE: WordRoomDatabase? = null
fun getDatabase(context: Context, scope:CoroutineScope): WordRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
val instance = Room.databaseBuilder(context.applicationContext,
WordRoomDatabase::class.java, "word_database")
.addCallback(WordDatabaseCallback(scope))
//.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
return instance
}
}
}
I've created several diaries and one note in each of them, using the buttons to create new diaries and notes. Now, if you select several diaries in turn, then on some attempt to select a diary, a NullPointerException is issued in the adapter, in this line:
override fun getItemCount() = notesMapped[wordId]!!.size
Why is this exception thrown if notesMapped always has the wordId key?
NoteActivity is called from another activity and the diary id is passed to it
This repository on GitHub: https://github.com/Lomank123/RoomDatabase
Edit:
noteViewModel.allNotes.observe(this, Observer {
var getList = emptyList<Note>()
for(i in it)
{
if(i.word.id == wordId)
{
getList = i.notes
break
}
}
adapter.setNotes(getList)
})
I've changed the Observer in NoteActivity and changed setNotes() method in adapter, but now it returns nothing. With for() I get the right notes and give them to adapter.setNotes(). If it doesn't work, how can I get the correct list of notes?
Hi initially the map is empty and the map is returning a null value and you are checking size on a null object.
Also as a good practice do not use a map instead use a list of notes only and pass the list directly.

how to filter list data in descending order in kotlin?

From the Android app, I wrote for I want to add, add a button in the toolbar that acts as a toggle. When the toggle is disabled (the default state) all posts should be shown, when it is enabled (after a tap) the list should only show the posts having user_id set to 1 and sorted by descending published_at. Tapping on the button again will return it to its default state.
Note that publishedAt returning date and publishedAt and user_id coming from postList from the server I want to know how can I implement above requirement what kind of steps should I have to follow
below my logic implementation in MainActivity.kt
class MainActivity : AppCompatActivity() {
#Inject
lateinit var restInterface: RestInterface
private fun initializeDagger() = App.appComponent.inject(this)
var context: Context? = null
private var filteredList: List<Post>? = null
private var recyclerView: RecyclerView? = null
private var switch1: Switch? = null
private var restAdapter: RestAdapter? = null
private var postList: List<Post>? = null
private var restList: RestList? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeDagger()
recyclerView = findViewById(R.id.recycler_view)
switch1 = findViewById(R.id.switch1)
fetchPosts()
switch1.setOnclickListener {
postList.forEach { postItem: Post ->
if (postItem.userId == 1)
filteredList.add(postItem)
}
recyclerView.post = filteredList
recyclerView.notifyDatasetChanged()
}
// Collections.sort( filteredList.get(4).publishedAt, Collections.reverseOrder());
}
private fun fetchPosts() {
val progress = ProgressDialog(this)
progress.setMessage("Loading... ")
progress.isIndeterminate = true
progress.show()
restInterface?.getPosts?.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : DisposableSingleObserver<Response<RestList>>() {
override fun onSuccess(response: Response<RestList>) {
restList = response.body()
val layoutManager = LinearLayoutManager(applicationContext)
recyclerView?.layoutManager = layoutManager
// initialize postList with posts
postList = restList?.posts
restAdapter = postList?.let { RestAdapter(it, restList) }
recyclerView?.adapter = restAdapter
}
override fun onError(e: Throwable) {
progress.dismiss()
Toast.makeText(context, "" + e.message, Toast.LENGTH_SHORT).show()
}
})
}
}
below my RestAdapter.kt
class RestAdapter(val post: List<Post>,val restList: RestList?) : RecyclerView.Adapter<RestAdapter.PostHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.post_list, null)
return PostHolder(itemView)
}
override fun getItemCount(): Int {
return post.size
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
val posts = post[position]
Picasso
.get() // give it the context
.load(posts.image) // load the image
.into(holder.postImage)
holder.userId.text = posts.userId.toString()
holder.postTitle.text = posts.title
holder.postTime.text = posts.publishedAt
holder.postDescription.text = posts.description
}
class PostHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postImage: ImageView = itemView.findViewById(R.id.postImage)
val userId: TextView = itemView.findViewById(R.id.userId)
val postTitle: TextView = itemView.findViewById(R.id.postTitle)
val postTime: TextView = itemView.findViewById(R.id.postTime)
val postDescription: TextView = itemView.findViewById(R.id.postDescription)
}
}
You can use method .sortedByDescending or .sortedBy on list :)
[Edit]
I give you a simple solution for that, maybe not the best but should work
1. Change RestAdapter constructor to (var post: List,val restList: RestList?)
Add method to updateData in apadapter:
fun filterData(isChecked: Boolean) {
if (isChecked) {
val filteredList = arrayListOf<Post>()
filteredList.addAll(restList?posts.filter { it.user_id == 1 }.sortedByDescending { it.published_at })
post = filteredList
} else {
post = restList?.posts
}
notifyDatasetChanged()
}
And in your class use this:
switch1.setOnclickListener {
restAdapter?.filterData(switch1.isChecked()//state of your switch i.e isChecked() which return true or false)
}

Why data is not loaded to the RecyclerView MVVM?

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)
.....
}

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