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}
Related
Why doesn't RecyclerView appear in my fragment? I've added recyclerview adapter in the fragment, but it still didn't appear. Here are the codes:
FollowersFragment.kt
class FollowersFragment : Fragment() {
private lateinit var binding: FragmentFollowersBinding
companion object {
private const val TAG = "FollowersFragment"
const val ARG_NAME = "userName"
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_followers, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentFollowersBinding.inflate(layoutInflater)
val username = arguments?.getString(ARG_NAME)
val layoutManager = LinearLayoutManager(requireActivity())
binding.rvFollowers.layoutManager = layoutManager
val itemDecoration = DividerItemDecoration(requireActivity(), layoutManager.orientation)
binding.rvFollowers.addItemDecoration(itemDecoration)
val client = ApiConfig.getApiService().getFollowers(username.toString(),"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")
client.enqueue(object : Callback<List<FollowsResponseItem>> {
override fun onResponse(
call: Call<List<FollowsResponseItem>>,
response: Response<List<FollowsResponseItem>>
) {
if(response.isSuccessful){
val responseBody = response.body()
if(responseBody!=null){
Log.d(TAG,responseBody.toString())
setUserData(responseBody)
}else{
Log.e(TAG,"onFailure: ${response.message()}")
}
}
}
override fun onFailure(call: Call<List<FollowsResponseItem>>, t: Throwable) {
Log.e(TAG, "onFailure: ${t.message}")
}
})
}
fun setUserData(item: List<FollowsResponseItem>){
val listUser = ArrayList<UserResponse>()
val executor = Executors.newSingleThreadExecutor()
executor.execute {
try {
for (i in 0..item.size-1) {
if(item.size>5 && i>5){
break
}
val client = ApiConfig.getApiService()
.getUser(item.get(i).login, "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")
client.enqueue(object : Callback<UserResponse> {
override fun onResponse(
call: Call<UserResponse>,
response: Response<UserResponse>
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
listUser.add(responseBody)
if(i==4 || item.get(i).login.equals(item.get(item.size-1).login)){
Log.d(TAG,"user : $listUser")
val adapter = ListUserAdapter(listUser)
binding.rvFollowers.adapter = adapter
Log.d(TAG,adapter.toString())
adapter.setOnItemClickCallback(object: ListUserAdapter.OnItemClickCallback{
override fun onItemClicked(data: UserParcelable) {
showSelectedUser(data)
}
})
}
} else {
Log.e(TAG, "onFailure: ${response.message()}")
}
}
}
override fun onFailure(call: Call<UserResponse>, t: Throwable) {
Log.e(TAG, "onFailure: ${t.message}")
}
})
}
} catch(e: InterruptedException) {
e.printStackTrace()
}
}
}
private fun showSelectedUser(data: UserParcelable) {
}
}
DetailActivity.kt
class DetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailBinding
private var getUserName: String ="sidiqpermana"
companion object{
const val EXTRA_DATA = "extra_data"
#StringRes
private val TAB_TITLES = intArrayOf(
R.string.tab_text_1,
R.string.tab_text_2
)
}
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.hide()
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
val data = intent.getParcelableExtra<UserParcelable>(EXTRA_DATA) as UserParcelable
val sectionsPagerAdapter = SectionsPagerAdapter(this)
val viewPager: ViewPager2 = binding.viewPager
viewPager.adapter = sectionsPagerAdapter
val tabs: TabLayout = binding.tabs
sectionsPagerAdapter.userName = data.login
TabLayoutMediator(tabs,viewPager){ tab, position ->
tab.text = resources.getString(TAB_TITLES[position])
}.attach()
getUserName = data.login
showInfo(data)
}
fun getUserName() : String{
return getUserName
}
#SuppressLint("SetTextI18n")
private fun showInfo(data: UserParcelable){
Glide.with(this#DetailActivity)
.load(data.avatar_url)
.into(binding.detailPp)
if(data.name.equals("null")) binding.detailName.setText("No Name") else binding.detailName.setText(data.name)
binding.detailUsername.setText(data.login)
if(data.bio.equals("null")) binding.detailBio.setText("No Name") else binding.detailBio.setText(data.bio)
binding.detailFollowers.setText("${data.followers} Followers")
binding.detailFollowings.setText("${data.following} Following")
if(data.location.equals("null")) binding.detailLocation.setText("No Location") else binding.detailLocation.setText(data.location)
}
}
ListUserAdapter.kt (RecyclerView Adapter)
class ListUserAdapter (private val listUser: ArrayList<UserResponse>) : RecyclerView.Adapter<ListUserAdapter.ListViewHolder>() {
private lateinit var onItemClickCallback: OnItemClickCallback
fun setOnItemClickCallback(onItemClickCallback: OnItemClickCallback){
this.onItemClickCallback = onItemClickCallback
}
class ListViewHolder(var binding: ItemUsersRowBinding) : RecyclerView.ViewHolder(binding.root) {
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ListViewHolder {
val binding = ItemUsersRowBinding.inflate(LayoutInflater.from(viewGroup.context),viewGroup,false)
return ListViewHolder(binding)
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
var (followers,avatar_url,following, name,bio, location, login) = listUser[position]
name = name ?: "No Name"
bio = bio ?: "No Bio"
location = location?: "No Location"
holder.apply {
Glide.with(itemView.getContext())
.load(avatar_url)
.into(binding.photoProfile)
binding.profileName.setText(name.toString())
binding.username.setText(login)
binding.followers.setText("$following Followers")
binding.followings.setText("$following Followings")
binding.location.setText(location.toString())
val detailUser = UserParcelable(followers,avatar_url,following,
name.toString(), bio.toString(), location.toString(), login)
itemView.setOnClickListener{ onItemClickCallback.onItemClicked(detailUser)}
}
}
override fun getItemCount(): Int {
return listUser.size
}
interface OnItemClickCallback {
fun onItemClicked(data: UserParcelable)
}
}
Help me solve this problem please.
There is no need to initialize adapter every time you want to update the list. Either make your ListUserAdapter extend ListAdapter and than use adapter.submitList(listUser) or if you want to extend RecyclerView.Adapter as you do, you can do the following :
class ListUserAdapter () : RecyclerView.Adapter<ListUserAdapter.ListViewHolder>() {
private val listUser: List<UserResponse>
fun submitList(newList: List<UserResponse>) {
listUser = newList
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val listItem = listUser[position]
...
}
override fun getItemCount(): Int {
return listUser.size
}
}
I suggest you go with ListAdapter. Check if Log.d(TAG,"user : $listUser") is printed, if it is and listUser is not empty than call adapter.submitList(listUser) and RV should be populated.
You have missed to notify adapter about the changes, So after
binding.rvFollowers.adapter = adapter call adapter.notifyDataSetChanged()
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)
.....
}
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.
I'm using android architecture component in my project.So i used paging library for pagination, in my case i needed PageKeyedDataSource, but i could not able to get the loaded data back to the UI,
Codes are
DataSourceFactory.kt
class DataSourceFactory : DataSource.Factory<Int, Item>() {
private val mutableLiveData = MutableLiveData<SearchedItemsDataSource>()
private lateinit var feedDataSource: SearchedItemsDataSource
override fun create(): DataSource<Int, Item> {
feedDataSource = SearchedItemsDataSource()
mutableLiveData.postValue(feedDataSource)
return feedDataSource
}}
DataSource.kt
class SearchedItemsDataSource() : PageKeyedDataSource<Int, Item>() {
private var client: Api
init {
val logging = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(logging)
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.stackexchange.com/2.2/")
.client(httpClient.build())
.build()
client = retrofit.create(Api::class.java)
}
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Item>) {
val callClient = client.getAnswers(1, params.requestedLoadSize, "stackoverflow")
callClient.enqueue(object : Callback<StackApiResponse> {
override fun onResponse(call: Call<StackApiResponse>, response: Response<StackApiResponse>) {
if (response.isSuccessful && response.body() != null) {
println("loadInitial onResponse1111 :${response.isSuccessful} *** ${params.requestedLoadSize}")
val searchedItems = response.body() as StackApiResponse
callback.onResult(searchedItems.items, 0,searchedItems.items.size,null, 21)
println("loadInitial onResponse1111 :${response.isSuccessful} *** ${params.requestedLoadSize} *** ${searchedItems.items.size}")
} else {
println("loadInitial onResponse :${response.isSuccessful} *** ${response.message()}")
}
}
override fun onFailure(call: Call<StackApiResponse>, t: Throwable) {
t.printStackTrace()
println("loadInitial onFailure :${t.message} ${t.localizedMessage}")
}
})
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Item>) {
println("loadAfter " + params.key + " Count " + params.requestedLoadSize);
val callClient = client.getAnswers(params.key, params.requestedLoadSize, "stackoverflow")
callClient.enqueue(object : Callback<StackApiResponse> {
override fun onResponse(call: Call<StackApiResponse>, response: Response<StackApiResponse>) {
if (response.isSuccessful && response.body() != null) {
println("loadAfter onResponse :${response.isSuccessful} *** ${params.key}")
val nextKey = params.key + 1
val searchedItems = response.body() as StackApiResponse
callback.onResult(searchedItems.items, nextKey)
} else {
println("loadAfter onResponse :${response.isSuccessful} *** ${response.message()}")
}
}
override fun onFailure(call: Call<StackApiResponse>, t: Throwable) {
t.printStackTrace()
println("loadAfter onFailure :${t.message} ${t.localizedMessage}")
}
})
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Item>) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}}
ViewModel.kt
class MainViewModel : ViewModel() {
internal lateinit var itemList: LiveData<PagedList<Item>>
fun getPagedItems(): LiveData<PagedList<Item>> {
val executor = Executors.newFixedThreadPool(5)
val feedDataFactory = DataSourceFactory()
val pagedListConfig = PagedList.Config.Builder()
.setEnablePlaceholders(true)
.setInitialLoadSizeHint(20)
.setPageSize(20).build()
itemList = (LivePagedListBuilder(feedDataFactory, pagedListConfig)).build()
return itemList
}}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainViewModel
private lateinit var mAdapter: ItemAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
mAdapter = ItemAdapter(this)
binding.recyclerView.adapter = mAdapter
viewModel.getPagedItems().observe(this, Observer { items ->
if (items != null && items.isNotEmpty()) {
println("observe called 1 ${items.size}")
mAdapter.submitList(items)
} else {
println("observe called 2 ${items?.size}")
}
})
}
}
In your Adapter, you should not be managing the list manually since you are using PagedListAdapter.
In your #Override public int getItemCount() {} do:
#Override
public int getItemCount() {
return super.getItemCount()
}
I am trying to hit api locally but onResponse method is not workingonFailure mehtod always runs
class Categories : Fragment() {
lateinit var categories_recycler: RecyclerView
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val v = inflater!!.inflate(R.layout.fragment_categories, container, false)
categories_recycler = v.findViewById(R.id.categories_recycler)
categories_recycler.setHasFixedSize(true)
categories_recycler.layoutManager = LinearLayoutManager(activity)
retrofit()
return v
}
fun retrofit(){
val retrofit = RetrofitBuilder.instance
val client = retrofit.create(SkillClientRetrofit::class.java)
val call = client.CategoryNames()
call.enqueue(object : Callback< List< GetCatName>> {
override fun onResponse(call: Call< List< GetCatName>>, response: Response< List< GetCatName>>) {
if(response.isSuccessful) {
activity.toast("this is success")
val data = response.body()
categories_recycler.adapter = CategoriesSkillAdapter(data!!)
}
}
override fun onFailure(call: Call< List< GetCatName>>, t: Throwable) {
activity.toast("this is error")
}
})
}
}
Below is RetrofitBuilder:
object RetrofitBuilder {
private var r: Retrofit? = null
val instance:Retrofit
get(){
if(r == null){
r = Retrofit.Builder().baseUrl("<http://192.168.56.1:3000/>")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return r!!
}
}
Below is interface:
public interface SkillClientRetrofit
{ #GET(" category/names") fun CategoryNames(): Call< List< GetCatName>> }
Here is data class:
data class GetCatName(
#SerializedName("_id") val id: String,
#SerializedName("category_name") val categoryName: String )