I have a RecyclerView with a ListAdapter. The list that is shown in the RecyclerView comes from a Flow that is observed in the Fragment that the recyclerView is instantiated.
When the Fragment is created, the data are calculated too (in onViewCreated Method) .
In the first data-calculation the RecyclerView is empty, a progressBar is shown, then the data is calculated, the progressbar hides, and the RecyclerView is populated.
If I go again in this Fragment to re-calculate the data, the previous list is shown simultaneously with the progressbar, and then is updated.
I want every time that new data is calculated to NOT show the previous list (just like the first data-calculation), but can't find a way to do it.
I tried to clear() the currentList and notifyDataSetChanged() but it still happens.. Any ideas?
Here is the code:
Fagment:
#AndroidEntryPoint
class YourPlanFragment : Fragment(R.layout.fragment_your_plan) {
lateinit var navController: NavController
private lateinit var binding: FragmentYourPlanBinding
private val sharedViewModel: WorkoutPlansViewModel by activityViewModels()
private lateinit var appBarConfiguration: AppBarConfiguration
#Inject
lateinit var dataStore: UserPreferencesRepo
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
binding = FragmentYourPlanBinding.bind(view)
appBarConfiguration = AppBarConfiguration(navController.graph)
val toolbar = binding.yourPlanToolbar
toolbar.setupWithNavController(navController, appBarConfiguration)
val exerciseAdapter = DayListAdapter(DayListAdapter.OnClickListener {
navigateTo(sharedViewModel.weekIndex, it.dayNumber)
})
exerciseAdapter.currentList.clear()
exerciseAdapter.notifyDataSetChanged()
binding.recyclerViewYourPlan.apply {
adapter = exerciseAdapter
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
recycledViewPool.clear()
removeAllViews()
adapter?.notifyDataSetChanged()
}
if (!sharedViewModel.planGenerated) {
// Here the data is generated
sharedViewModel.onTriggerEvent(WorkoutPlansEvent.GetWorkoutPlanEvent)
} else if (sharedViewModel.planGenerated) {
sharedViewModel.onTriggerEvent(WorkoutPlansEvent.GetWeekEvent)
}
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
sharedViewModel.yourPlanState.collect { yourPlanState ->
when (yourPlanState.progressBarState) {
is ProgressBarState.Loading -> {
binding.progressBar.isVisible = true
}
is ProgressBarState.Idle -> {
binding.progressBar.isInvisible = true
}
}
exerciseAdapter.submitList(yourPlanState.planDays)
}
}
}
}
private fun navigateTo(
currentWeek: Int,
dayNumber: Int,
) {
val action =
YourPlanFragmentDirections.actionYourPlanFragmentToYourDayFragment(
currentWeek,
dayNumber
)
navController.navigate(action)
}
}
ListAdapter:
class DayListAdapter(private val onClickListener: OnClickListener) :
ListAdapter<Day, DayListAdapter.DayViewHolder>(ExerciseComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DayViewHolder {
val binding = ListItemDayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DayViewHolder(binding)
}
override fun onBindViewHolder(holder: DayViewHolder, position: Int) {
val currentItem = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(currentItem)
}
if (currentItem != null) {
holder.bind(currentItem)
}
}
class DayViewHolder(private val binding: ListItemDayBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(day: Day) {
binding.apply {
dayNumber = day.dayNumber.toString()
executePendingBindings()
}
}
}
class ExerciseComparator : DiffUtil.ItemCallback<Day>() {
override fun areItemsTheSame(oldItem: Day, newItem: Day) =
oldItem.dayNumber == newItem.dayNumber
override fun areContentsTheSame(oldItem: Day, newItem: Day) =
oldItem == newItem
}
class OnClickListener(val clickListener: (day: Day) -> Unit) {
fun onClick(day: Day) = clickListener(day)
}
}
This is happing because of the activityViewModels() that will preserve the viewmodel based on activity lifecycle. Either create WorkoutPlansViewModel using viewmodels() or Try to clear the list from the WorkoutPlansViewModel when you are creating the fragment.
Related
First of all, I am Spanish so my english is not good.
I have an app with Kotlin and room, and it has a Recyclerview.
I have 3 tables: coaster, user and favorite.
The user can add coasters to favorite, and this is done succesfully.
The problem that I have is that when the user clicks on the button to add or delete from favorites, the recyclerview resets, it displays again. So it scrolls to the top of the Screen, and also some odd spaces appears after the element.
I also have a function to search, and it happens the same: spaces appears after each element when I am searching.
I have tried everything: notifyItemChanged,
notifyDataSetChanged... it doesnt work! I also tried removing the observer once from the recyclerview...
My main activity:
class CoasterFragment : Fragment() {
lateinit var coasterListener: CoasterListener
lateinit var usuarioCoaster: List\<UsuarioCoaster\>
private lateinit var searchView: SearchView
private lateinit var cAdapter: CoasterRecyclerViewAdapter
private var \_binding: FragmentCoasterBinding? = null
private val binding get() = \_binding!!
private val viewModel: CoastersViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentCoasterBinding.inflate(inflater, container, false)
val root: View = binding.root
/* val livedata = viewModel.coasters()
livedata.observe(viewLifecycleOwner,object: Observer <List<CoasterFavorito>> {
override fun onChanged(it: List<CoasterFavorito>) {
createRecyclerView(it)
livedata.removeObserver(this)
}
})*/
viewModel.coasters().observe(viewLifecycleOwner){createRecyclerView(it)}
coasterListener = CoasterListenerImpl(requireContext(), viewModel)
searchView = binding.search
searchView.clearFocus()
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
if(query != null){
searchDatabase(query)
}
return true
}
override fun onQueryTextChange(query: String?): Boolean {
if(query != null){
searchDatabase(query)
}
return true
}
})
return root
}
fun createRecyclerView(coasters: List<CoasterFavorito>) {
cAdapter =
CoasterRecyclerViewAdapter(
coasters as MutableList<CoasterFavorito>,
coasterListener,
requireContext()
)
val recyclerView = binding.recyclerCoaster
recyclerView.apply {
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
adapter = cAdapter
addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL))
cAdapter.notifyDataSetChanged()
}
}
fun searchDatabase(query: String) {
val searchQuery = "%$query%"
viewModel.searchDatabase(searchQuery).observe(viewLifecycleOwner) { createRecyclerView(it)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
my adapter:
class CoasterRecyclerViewAdapter(val coasters: List<CoasterFavorito>, val listener: CoasterListener,
val context: Context, ) : RecyclerView.Adapter<CoasterRecyclerViewAdapter.ViewHolder>(){
class ViewHolder private constructor(val binding: CoasterItemBinding, private val listener: CoasterListener,
private val context: Context): RecyclerView.ViewHolder(binding.root){
fun relleno(data: CoasterFavorito){
binding.nombre.text = data.coaster.nombre
binding.parque.text = data.coaster.parque
binding.ciudad.text = data.coaster.ciudad
binding.provincia.text = data.coaster.provincia
binding.comunidad.text = data.coaster.comunidadAutonoma
Glide
.with(context)
.load(data.coaster.imagen)
.centerCrop()
.into(binding.imagen)
binding.check.isChecked = data.favorito
binding.check.setOnClickListener{
if (data.favorito) {
listener.delFavorito(data.coaster.id)
binding.check.isChecked = false
} else {
listener.addFavorito(data.coaster.id)
binding.check.isChecked = true
}
}
}
companion object{
fun crearViewHolder(parent: ViewGroup, listener: CoasterListener, adapter: CoasterRecyclerViewAdapter, context: Context):ViewHolder{
val layoutInflater = LayoutInflater.from(parent.context)
val binding = CoasterItemBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding, listener, context )
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder.crearViewHolder(parent, listener, this, context)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.relleno(coasters[position])
override fun getItemCount() = coasters.size
}
interface CoasterListener {
fun addFavorito(id: Long)
fun delFavorito(id: Long)
}
I have tried everything: notifyItemChanged,
notifyDataSetChanged... it doesnt work! I also tried removing the observer once from the recyclerview...
Your createRecyclerView function should be invoked only once in a whole lifecycle of the Fragment. You should not create any new RecyclerView.Adapter, or set a LayoutManager to the RecyclerView every time your data set changes.
Therefore the Observer used in viewModel.coasters.observe() should only submit a new List to the existing Adapter and call .notifyDataSetChanged(), or other notifying functions.
when i try to delete first row i get first row back and with duplicate second row
enter image description here
after delete any row from the list gives me duplicate data like this
enter image description here
This is my Adapter class. On my deleteItem function position is passed from fragment class and
it is supposed to delete an item from the given position and update the list but its rendering the duplicate data.
class MyListAdapter :ListAdapter<Article,MyListAdapter.MyViewHolder>(MyDiffUtil()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recycler_item,parent,false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = getItem(position)
holder.onBind(item)
// to click each item of list
holder.itemView.setOnClickListener {
onItemClickListener?.let {it(item) }
}
}
class MyViewHolder(itemView :View) : RecyclerView.ViewHolder(itemView)
{
fun onBind(article: Article) = with(itemView)
{
Glide.with(this)
.load(article.urlToImage)
.error(R.drawable.notfound)
.into(imageView)
webSource.text = article.source?.name
newsDate.text = article.publishedAt
newsTitle.text = article.title
newsDescription.text = article.description
}
}
// lamda function for handling web view
private var onItemClickListener : ((Article) -> Unit)? = null
fun setOnItemClickListener(listener : (Article) ->Unit)
{
onItemClickListener = listener
}
// implementing diffutil class
class MyDiffUtil : DiffUtil.ItemCallback<Article>()
{
override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem == newItem
}
}
fun deleteItem(pos : Int)
{
var myl = ArrayList<Article>()
val currentList= currentList.toMutableList()
currentList.removeAt(pos)
myl.addAll(currentList)
submitList(myl)
}
}
`
I tried to delete the saved news from the ListAdapter and update the recycler view with animation of DiffUtill class but its not updating the recycler view and
giving duplicate data . How can i delete data with diffutil animation as its normal way. Thanks in advance.
this is my fragment
package com.example.news.Fragments
class SavedNews : Fragment() {
lateinit var mymainViewModel: MainViewModel
lateinit var databaseObject: MyDataBase
lateinit var myAdapter: MyListAdapter
lateinit var convertedArticleList : ArrayList<Article>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
myAdapter = MyListAdapter()
convertedArticleList = ArrayList()
return inflater.inflate(R.layout.fragment_saved_news, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
savedNewsRecyclerView.layoutManager = LinearLayoutManager(activity,LinearLayoutManager.VERTICAL,
false)
savedNewsRecyclerView.adapter = myAdapter
ItemTouchHelper(object :ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT){
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
// this method is called
// when the item is moved.
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// this method is called when we swipe our item to right direction.
// on below line we are getting the item at a particular position.
val deletedCourse: Article =
convertedArticleList.get(viewHolder.adapterPosition)
myAdapter.deleteItem(viewHolder.adapterPosition)
mymainViewModel.deleteSavedNews(deletedCourse.id!!)
// myAdapter.notifyItemRemoved(viewHolder.adapterPosition)
// myAdapter.submitList(myCurrentList)
// Snackbar.make(savedNewsRecyclerView,"Deleted" + deletedCourse.title,
// Snackbar.LENGTH_LONG).setAction(
// "Undo",
// View.OnClickListener {
//
// convertedArticleList.add(position,deletedCourse)
// myAdapter.notifyItemInserted(position)
// }
// ).show()
}
}).attachToRecyclerView(savedNewsRecyclerView)
}
override fun onResume() {
super.onResume()
Log.d("LIFE","ON Resume")
val myInterfaceObject = ApiInterface.MyObject.getInstance()
databaseObject = MyDataBase.MyObjectDB.getDBInstance(activity as MainActivity)
val myRepository = Repository(myInterfaceObject, databaseObject)
mymainViewModel = ViewModelProvider(
this,
MainViewModelFactory(myRepository)
).get(MainViewModel::class.java)
//listAdapter things
// show saved news in savednews fragment
lifecycleScope.launch(Dispatchers.Main) {
//abstract data from saved room database and converting likedartcile datacass object to
// Artticle data class article
mymainViewModel.abstractSavedNews().observe(viewLifecycleOwner, Observer {
it.forEach { eachLikedArticle ->
val obj = toArticle(eachLikedArticle)
convertedArticleList.add(obj)
}
myAdapter.submitList(convertedArticleList)
})
//clicking the item of save news
myAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("article", it)
}
convertedArticleList.clear()
findNavController().navigate(R.id.action_savedNews_to_article, bundle)
}
}
}
private fun toArticle(rawObject: LikedArticle) = Article(
rawObject.author, rawObject.content,
rawObject.description, rawObject.publishedAt, rawObject.source, rawObject.title,
rawObject.url, rawObject.urlToImage, rawObject.id
)
}
There is not enough information to actually replicate how you're trying to use this in your Fragment/Activity but without changing your code too much, this works (note I used view binding):
class MyListAdapter :
ListAdapter<Article, MyViewHolder>(MyDiffUtil()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(
RecyclerItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = getItem(position)
holder.onBind(item)
}
fun deleteItem(pos: Int) {
var myl = ArrayList<Article>()
val currentList = currentList.toMutableList()
currentList.removeAt(pos)
myl.addAll(currentList)
submitList(myl)
}
}
class MyViewHolder(private val binding: RecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun onBind(article: Article) = with(binding) {
textView.text = article.url
}
}
class MyDiffUtil : DiffUtil.ItemCallback<Article>() {
override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem == newItem
}
}
Overall this should work.
You can also refactor deleteItem to something more meaningful:
fun deleteItem(pos: Int) {
val oldList = currentList.toMutableList()
oldList.removeAt(pos)
val updatedList = oldList
submitList(updatedList)
}
I implemented a ListAdapter with DiffUtil and faced an issue when appending a new list. It overwrites instead of appending to old one. To solve issue i created a new project and populate it with some test data.
Here is my code:
MainActivity
private lateinit var binding: ActivityMainBinding
private val viewModel: ItemViewModel by lazy {
ItemViewModel()
}
private val adapter: ItemAdapter by lazy {
ItemAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel.getItems()
viewModel.items.observe(this, Observer { items ->
adapter.submitList(items)
})
binding.recyclerView.adapter = adapter
binding.fab.setOnClickListener {
viewModel.getItems(9)
}
}
ItemViewModel
class ItemViewModel: ViewModel() {
private val repository = FakeRepository()
private val _items: MutableLiveData<List<Item>> = MutableLiveData()
val items: LiveData<List<Item>> = _items
fun getItems(start: Int = 1) {
viewModelScope.launch {
val items = repository.getItems(start)
_items.value = items
/*val newItems = items.map { it.copy() }
_items.postValue(newItems)*/
}
}
}
ItemAdapter
class ItemAdapter: ListAdapter<Item, ItemAdapter.ViewHolder>(DiffUtilCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ItemRowBinding.inflate(LayoutInflater.from(parent.context),parent,false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
class ViewHolder(private val binding: ItemRowBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
binding.apply {
title.text = item.title
}
}
}
private class DiffUtilCallback: DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Item, newItem: Item) = oldItem == newItem
}
}
Item
data class Item(
val id: Int,
val title: String,
val timestamp: String
)
As per documentation:
Submits a new list to be diffed, and displayed.
If a list is already being displayed, a diff will be computed on a
background thread, which will dispatch Adapter.notifyItem events on
the main thread.
So, when you submit a new list via the LiveData observer, it's a brand new list to the adapter, and therefore it overwrites the current items not appending them.
If you want to append the current items, you can create a method in the adapter to consolidate the current list with the new one, and eventually submit it:
class ItemAdapter : ListAdapter<Item, ItemAdapter.ViewHolder>(DiffUtilCallback()) {
//......
fun appendList(list: List<Item>) {
val currentList = currentList.toMutableList() // get the current adapter list as a mutated list
currentList.addAll(list)
submitList(currentList)
}
}
And apply that to the observer callback in the activity:
viewModel.items.observe(this, Observer { items ->
// myAdapter.submitList(items) // send a brand new list
myAdapter.appendList(items) // Update the current list
})
I have a RecyclerView displaying data from a Dao.
In the data that is shown in the recyclerView is a button with which the item should get deleted.
I know the function for that is :
fun deleteReceipt(receipts: Receipts) = viewModelScope.launch {
receiptDao.delete(receipts)
}
But I am not quite sure where to put it and call it. Because if I want to call it in the Adapter where i set the other displayed Item values I have no access to the Dao
Here is the fragment:
#AndroidEntryPoint
class HistoryFragment : Fragment(R.layout.fragment_history) {
private val viewModel: PurchaseViewmodel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentHistoryBinding.bind(view)
val exampleAdapter = ExampleAdapter()
binding.apply{
recyclerView.apply{
layoutManager = LinearLayoutManager(requireContext())
adapter = exampleAdapter
setHasFixedSize(true)
}
}
setFragmentResultListener("add_receipt_request"){_,bundle ->
val result = bundle.getInt("add_receipt_request")
viewModel.onAddResult(result)
}
viewModel.receipts.observe(viewLifecycleOwner){ /// New Items get passed to the List
exampleAdapter.submitList(it)
}
}
}
The ViewModel:
#HiltViewModel
class PurchaseViewmodel #Inject constructor(
private val receiptDao: ReceiptDao
): ViewModel() {
private val tasksEventChannel = Channel<TasksEvent>()
val addTaskEvent = tasksEventChannel.receiveAsFlow()
val receipts = receiptDao.getAllReceipts().asLiveData()
fun onAddResult(result: Int){
when (result){
ADD_RECEIPT_RESULT_OK ->showReceiptSavedConfirmation("Receipt is saved")
}
}
private fun showReceiptSavedConfirmation (text: String) = viewModelScope.launch {
tasksEventChannel.send(TasksEvent.ShowReceiptSavedConfirmation(text))
}
fun deleteReceipt(receipts: Receipts) = viewModelScope.launch {
receiptDao.delete(receipts)
}
sealed class TasksEvent {
data class ShowReceiptSavedConfirmation(val msg: String) : TasksEvent()
}
}
And the Adapter:
class ExampleAdapter : ListAdapter<Receipts,ExampleAdapter.ExampleViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExampleViewHolder { // Basically how to get a new Item from the List and display it
val binding = ReceiptsBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return ExampleViewHolder(binding)
}
override fun onBindViewHolder(holder: ExampleViewHolder, position: Int) {
val currentItem = getItem(position)
holder.bind(currentItem)
}
override fun getItemCount(): Int {
return super.getItemCount()
}
class ExampleViewHolder(private val binding: ReceiptsBinding) : RecyclerView.ViewHolder(binding.root){ //Examples One Row in our list
fun bind (receipts: Receipts) {
binding.apply {
storeHistory.text = receipts.store
amountHistory.text = receipts.total
dateHistory.text = receipts.date
}
}
}
class DiffCallback : DiffUtil.ItemCallback<Receipts>() {
override fun areItemsTheSame(oldItem: Receipts, newItem: Receipts) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Receipts, newItem: Receipts) =
oldItem == newItem
}
}
Create a listener using Interface
Implement the listener in Fragment
Create listener variable in Adapter
pass the listener from fragment to adapter
pass the listener to viewHolder
on Delete button click of that item call the listener method, which will trigger the implementation in Fragment where you have access to your ViewModel
delete the receipt using viewModel.deleteReceipt
I have created an Android app, which connects to GitHub API and shows a list of repositories of certain user. List is shown by recyclerView. I'm trying to save and restore a list, when I'm switching fragments. I have tried to save a list to a variable and add this to recyclerView. But it doesn't work.
My Fragment:
class MainFragment : Fragment() {
lateinit var recyclerView: RecyclerView
var responseSave:List<GitHubPOJO> = ArrayList()
var posts: MutableList<GitHubPOJO> = ArrayList()
lateinit var btn:Button
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_main, container, false)
btn = view.findViewById(R.id.button)
btn.setOnClickListener {
posts = ArrayList()
val name:String = view!!.findViewById<EditText>(R.id.editText).text.toString()
recyclerView = view!!.findViewById(R.id.posts_recycle_view)
val layoutManager = LinearLayoutManager(this.activity!!)
recyclerView.layoutManager = layoutManager
val adapter = PostsAdapter(posts)
recyclerView.adapter = adapter
//HIDE KEYBOARD
val inputMethodManager = this.activity!!.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
if(activity!!.currentFocus !=null) {
inputMethodManager.hideSoftInputFromWindow(this.activity!!.currentFocus!!.windowToken, 0)
}
val service = Retrofit.Builder()
.baseUrl("https://api.github.com/") // CHANGE API
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(GitHubService::class.java)
service.retrieveRepositories(name)
.enqueue(object : Callback<List<GitHubPOJO>> {
override fun onResponse(call: Call<List<GitHubPOJO>>, response: Response<List<GitHubPOJO>>) {
responseSave = response.body()!!
posts.addAll(responseSave)
response.body()?.forEach { println ("TAG_: $it")}
recyclerView.adapter?.notifyDataSetChanged()
}
override fun onFailure(call: Call<List<GitHubPOJO>>, t: Throwable) {
//Toast.makeText(this#MainFragment, "Error occurred while networking", Toast.LENGTH_SHORT).show()
}
})
recyclerView.addOnItemTouchListener(
ClickListener(this.activity!!, recyclerView, object : ClickListener.OnItemClickListener {
override fun onLongItemClick(view: View?, position: Int) {
}
override fun onItemClick(view: View, position: Int) {
val url = posts!![position].htmlUrl
println("URL = $url")
view.findNavController().navigate(MainFragmentDirections.actionMainFragmentToWebFragment(url))
}
})
)
}
return view
}
And there is my code onResume:
override fun onResume() {
super.onResume()
val adapter = PostsAdapter()
adapter.updateAdapterList(responseSave.toMutableList())
println("RESUME")
println(responseSave)
}
When I print responseSave I see that my list is there. But it doesn't appears in RecyclerView.
Fragments are swithced by standart navigation library.
Activity code:
class MainActivity : AppCompatActivity(){
private lateinit var navController: NavController
private lateinit var mNavView:NavigationView
private lateinit var mDrawerLayout:DrawerLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mDrawerLayout = findViewById(R.id.drawer_layout)
mNavView = findViewById(R.id.nav_view)
navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController,mDrawerLayout)
NavigationUI.setupWithNavController(mNavView, navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return NavigationUI.navigateUp(mDrawerLayout,navController)
}}
My RecyclerView Adapter code:
class PostsAdapter(private var posts: MutableList<GitHubPOJO>? = ArrayList()) : RecyclerView.Adapter<PostsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.post_item, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = posts!![position]
holder.post.text = post.name
holder.site.text = post.fullName // Change what you wanna see
}
fun updateAdapterList(newList: MutableList<GitHubPOJO>) {
posts!!.clear()
posts!!.addAll(newList)
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return posts?.size ?: 0
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var post: TextView = itemView.findViewById<View>(R.id.postitem_post) as TextView
var site: TextView = itemView.findViewById<View>(R.id.postitem_site) as TextView
}}
Add a method in your adapter thats something like;
fun updateAdapterList(newList: MutableList<GitHubPOJO>) {
oldList.clear()
oldList.addAll(newList)
notifyDataSetChanged()
}
Then call this in your onResume() and pass in your updated list
The problem is on your onResume method. You are not adding an Adapter and a LayoutManager to the RecylerView so it can't display the data. Try moving the RecylerView initalization code from the onClickListener to your onResume method:
recyclerView = view!!.findViewById(R.id.posts_recycle_view)
val layoutManager = LinearLayoutManager(this.activity!!)
recyclerView.layoutManager = layoutManager
val adapter = PostsAdapter(posts)
recyclerView.adapter = adapter
Although having this initialization code in your onCreateView method would be better since it's the first lifecycle method called when a fragment returns to the front view from the backstack.