I am getting data from API http and using dataBinding and viewModel, all works but when I try to convert my list to mutableList and add All don't getting nothing, also try pass the data of simple way and working , the data is be there, the problem is that notifyDataSetChanged() don't do changes in my Adapter and I dont see nathing in the recyclerView.
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MostPopularTVShowsViewModel
private lateinit var activityMainBinding : ActivityMainBinding
private var tvShows: List<TVShow> = ArrayList()
private lateinit var tvShowAdapter:TVShowsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
doInitialization()
}
private fun doInitialization(){
activityMainBinding.tvShowRecycleView.setHasFixedSize(true)
viewModel= ViewModelProvider(this).get(MostPopularTVShowsViewModel::class.java)
tvShowAdapter= TVShowsAdapter(tvShows)
activityMainBinding.tvShowRecycleView.adapter=tvShowAdapter
getMostPopularTVShows()
}
private fun getMostPopularTVShows(){
activityMainBinding.isLoading=true
viewModel.getMostPopularTVShows(0).observe(this, { mostPopularTVShowsResponse ->
activityMainBinding.isLoading=false
if (mostPopularTVShowsResponse != null){
tvShows.toMutableList().addAll(mostPopularTVShowsResponse.tvShows)
//tvShows=mostPopularTVShowsResponse.tvShows
println("size is: "+tvShows.size)
tvShowAdapter.notifyDataSetChanged()
}else{
Toast.makeText(this," NULL", Toast.LENGTH_SHORT).show()
}
})
}
}
when i change tvShows=mostPopularTVShowsResponse.tvShows insted tvShows.toMutableList().addAll(mostPopularTVShowsResponse.tvShows) the list gets the data but notifyDataSetChanged dont working
this is my adapter
class TVShowsAdapter(private val items: List<TVShow>): RecyclerView.Adapter<TVShowsAdapter.ViewHolder>(){
private lateinit var layoutInflater: LayoutInflater
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
layoutInflater= LayoutInflater.from(parent.context)
val binding: ItemContainerTvShowBinding = DataBindingUtil.inflate(
layoutInflater, R.layout.item_container_tv_show,parent,false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int)= holder.bind(items[position])
override fun getItemCount()= items.size
class ViewHolder(private val itemContainerTvShowBinding: ItemContainerTvShowBinding):
RecyclerView.ViewHolder(itemContainerTvShowBinding.root){
fun bind (tvShow: TVShow){
itemContainerTvShowBinding.tvShow=tvShow
itemContainerTvShowBinding.executePendingBindings()
}
}
}
You're not actually changing the data in the adapter. You're passing in a reference to the tvShows ArrayList when you create the adapter, so if you changed the contents of that list the adapter would be able to see it. But when you do
tvShows.toMutableList().addAll(mostPopularTVShowsResponse.tvShows)
you're creating a new, separate list by calling toMutableList, and you're adding stuff to that. tvShows is still the original, immutable ArrayList with nothing in it.
If you're going to do it this way, you need to make the list mutable from the beginning
val tvShows = mutableListOf<TVShow>()
then you can clear and add to it when you get new data.
Generally though, a better idea is to give the adapter some kind of setData function where you pass in a list, so it can update its own internal data set and notify itself about the change. That way the adapter manages it state itself, all your activity or fragment needs to do is pass it some new data
Related
I'm starting using Kotlin (i'm a web dev) to maintain the mobile app of my current job. To practice my learning, I'm creating a basic app which is displaying a list of France departments (using a REST Api), and I need to allow the user to click on a list item to get more info on the selected item.
I'm trying to build this with databinding, Koin as dependency injection, and Room as db layer.
My issue is that I created a RecyclerView custom Adapter, and used the databinding to give it the datas. But now I want to implement the onClick behaviour, which should launch another activity to display item details. My problem is: I don't know how to do this in a clean way.
I was thinking about creating a viewModel to link to my Adapter, but can't really find how to do it well. And even if I did, how to start another activity in a viewModel ? (don't have access to the context and startActivity function). So I finally dropped that solution that doesn't seems to fit.
So I'm currently thinking of passing directly from my adapter the onClick function, but can't find a way to bind this function in my xml file. Here is my files:
MainActivity:
class MainActivity : AppCompatActivity() {
private val mViewModel: DepartmentsViewModel by viewModel()
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.toolbar.title = "Liste des départements"
val adapter = DepartmentListAdaptater()
binding.recyclerview.adapter = adapter
binding.recyclerview.layoutManager = LinearLayoutManager(this)
mViewModel.allDepartments.observe(this, Observer { data -> adapter.submitList(data) })
}
}
RecyclerView.Adapter:
class DepartmentListAdaptater : RecyclerView.Adapter<DepartmentListAdaptater.ViewHolder>() {
private var dataSet: List<Department>? = null
inner class ViewHolder(private val binding: DepartmentListRowBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(department: Department?) {
binding.department = department
}
}
fun submitList(list: List<Department>) {
dataSet = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = DepartmentListRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int = dataSet?.size ?: 0
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(dataSet?.get(position))
}
}
The XML View:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="department" type="com.navalex.francemap.data.entity.Department" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="72dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="#drawable/list_item_bg"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:clickable="true"
tools:ignore="UselessParent">
<TextView
android:id="#+id/textView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingRight="16dp"
android:text="#{department.nom}"
android:paddingLeft="16dp"/>
</LinearLayout>
</RelativeLayout>
</layout>
First I want to say that it's really impressive that you are a web developer and you already have a lot of knowledge about things like dependency injection and keep the state of the view on ViewModel, congrats. Now, let's talk about your problem... I'll start with some suggestions that will improve the code clarity and performance.
For the Adapter implementation, always prefer to use ListAdapter, because this implementation have a more efficient way to compare the current list with the new list and update it. You can follow this example:
class MyAdapter: ListAdapter<ItemModel, MyAdapter.MyViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = FragmentFirstBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(getItem(position))
}
class MyViewHolder(
private val binding: FragmentFirstBinding
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: ItemModel) {
// Here you can get the item values to put this values on your view
}
}
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ItemModel>() {
override fun areItemsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean {
// need a unique identifier to have sure they are the same item. could be a comparison of ids. In this case, that is just a list of strings just compare like this below
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean {
// compare the objects
return oldItem == newItem
}
}
}
}
In your fragment, you have a observer, that observe the value you want to sent to the adapter, right? When a update happen, you call the submitList sending the updated list and when the adapter receive this new list, the adapter will be responsible to update just the items that changed, because of your DIFF_CALLBACK implementation.
About the onClick item, you can wait for a callback on your adapter. Doing this:
class MyAdapter(
private val onItemClicked: (item: ItemModel) -> Unit
): ListAdapter<ItemModel, MyAdapter.MyViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = FragmentFirstBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding, onItemClicked)
}
// ...
class MyViewHolder(
private val binding: FragmentFirstBinding,
private val onItemClicked: (item: ItemModel) -> Unit
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: ItemModel) {
// ...
// Here you set the callback to a listener
binding.root.setOnClickListener {
onItemClicked.invoke(item)
}
}
}
// ...
}
As you can see, we will receive the callback on the Adapter constructor, then we send to the ViewHolder by constructor too. And on the ViewHolder bind we set the callback to a click listener.
On you fragment, you will have something like this:
class MyFragment: Fragment() {
private lateinit var adapter: MyAdapter
private val onItemClicked: (itemModel: ItemModel) -> Unit = { itemModel ->
// do something here when the item is clicked, like redirect to another activity
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = MyAdapter(onItemClicked)
}
}
I hope it helps you. Please, let me know if you need something more. I really appreciate helping.
I don't know about data binding specifically, but a typical way to do it is to let the Activity handle details like app navigation, and let the Adapter trigger that logic. A listener function is an easy way to do this:
// in your Adapter
var clickListener: ((YourData) -> ())? = null
// in your ViewHolder (make it an inner class so it can access the Adapter's
// fields, like the listener object and the stored data)
init {
clickableView.setOnClickListener {
// pass back whatever data here, if the listener needs to know
// what's been clicked. I'm just doing a lookup and passing
// the data item currently being displayed
clickListener?.invoke(
adapterData[bindingAdapterPosition]
)
}
}
// in your Activity, when setting up the adapter
adapter.clickListener = { whateverData ->
// do what you need to do in response to the click
}
So the Activity itself is defining that logic about actions that should be taken when a click happens - it's basically wiring up different parts of the app, so the Adapter doesn't need to be concerned with anything except taking data, displaying it, and informing a listener when specific interactions take place. That listener code (defined by the Activity) could navigate somewhere else, or update a database, or pass it to a networking component... the adapter doesn't need to know about that.
(The non-Kotlin way to do this would be to create an interface and have the Activity implement that, and pass itself as the listener/callback object, that kind of thing)
I have a problem with recycler view. In my previous app, when i get the list for recycler view adapter from database and observe it in my fragment, i used the notifyDataSetChanged() and when i tried to delete a item , view updated successfully. But in this app this does not work and i don't understand why. When i click the delete button the item deleted in database successfully but i can't see it immediatly. When i go to any other fragment and back to this Favourites fragment i see the items deleted.
I tried all the options in stackoverflow but still i can't fix it.
My Adapter:
class FavouritesAdapter(owner: ViewModelStoreOwner, val favouritesList : ArrayList<Vocabulary>) : RecyclerView.Adapter<FavouritesAdapter.FavouritesViewHolder>() {
val viewModel = ViewModelProvider(owner).get(FavouritesViewModel::class.java)
class FavouritesViewHolder(val binding: FavouritesItemRowBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavouritesViewHolder {
return FavouritesViewHolder(FavouritesItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: FavouritesViewHolder, position: Int) {
holder.binding.englishWordTV.text = favouritesList[position].word
holder.binding.turkishWordTV.text = favouritesList[position].translation
holder.binding.deleteButtonRV.setOnClickListener {
viewModel.deleteVocabulary(favouritesList[position])
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return favouritesList.size
}
fun updateList(myList : List<Vocabulary>) {
favouritesList.clear()
favouritesList.addAll(myList)
notifyDataSetChanged()
}
}
My problem is in delete button in my recycler row;
holder.binding.deleteButtonRV.setOnClickListener {
viewModel.deleteVocabulary(favouritesList[position])
notifyDataSetChanged()
}
And here is my fragment ;
class FavouritesFragment : Fragment() {
private var _binding: FragmentFavouritesBinding? = null
private val binding get() = _binding!!
private lateinit var favouritesAdapter : FavouritesAdapter
private lateinit var viewModel : FavouritesViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFavouritesBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(FavouritesViewModel::class.java)
favouritesAdapter = FavouritesAdapter(this, arrayListOf())
viewModel.getAllVocabulariesFromDB()
prepareRecyclerView()
observeFavouritesLiveData()
}
fun prepareRecyclerView(){
binding.favouritesRecyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = favouritesAdapter
}
}
fun observeFavouritesLiveData(){
viewModel.favouritesListLiveData.observe(viewLifecycleOwner, Observer {
it?.let {
favouritesAdapter.updateList(it)
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Try with notifyItemRemoved(position) instead of notifyDataSetChanged().
It all looks fine to me - you observe the favourites LiveData, that passes the new data to an update function in your Adapter, and that modifies the internal data set and calls notifyDataSetChanged() (which works for any kind of update).
So, are you sure your ViewModel is updating favouritesListLiveData properly when you call deleteVocabulary? Check if your observer is actually firing with a new value when you hit delete, and check if its contents are what you expect (the previous list minus the thing you want removed)
You could check it with some logging, but setting some breakpoints and debugging the app might be more helpful if you're not sure where it's going wrong
(also your button doesn't need to call notifyDataSetChanged() - that only needs to happen when the data is updated, which happens through the update function, in there is the right place for it!)
i tried to use the path "button click -> UI sends delete event to VM -> VM updates data -> observer sees new data -> observer calls update with new data" as #cactuctictacs mentioned. I added this lines to my adapter,
lateinit var onDeleteItemClick : ((Vocabulary) -> Unit)
holder.binding.deleteButtonRV.setOnClickListener {
onDeleteItemClick.invoke(favouritesList[position])
notifyItemRemoved(position)
}
and added to my fragment,
fun deleteButtonClicked(){
favouritesAdapter.onDeleteItemClick = {
viewModel.deleteVocabulary(it)
viewModel.getAllVocabulariesFromDB()
observeFavouritesLiveData()
favouritesAdapter.notifyDataSetChanged()
}
}
I hope this is the proper way to do this.
When I rotate the screen the spinner reset though I am using MVVM architecture.
While setting value I set value in view model, but still spinner reset to its orignal state.
In Main Activity I have done this,
GetBusinessPartners.setOnItemSelectedListener(object:OnItemSelectedListener{
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
dealMealPreApproval.initsetSpinnerIndex(position)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
})
dealMealPreApproval.getSpinnerValue().observe(this#DealMealPreApproval, Observer {
GetBusinessPartners.setSelection(it)
})
in view model i have done this
class MealPolicyViewModel : ViewModel() {
var businessPartners=MutableLiveData<ArrayList<BusinessPartnersModel>>()
var spinnerString=MutableLiveData<Int>()
fun initsetSpinnerIndex(valueOfSpinner:Int){
spinnerString.value=valueOfSpinner
Log.d("valueOfValueOFSPinner",valueOfSpinner.toString())
}
fun getSpinnerValue() : LiveData<Int>{
return spinnerString
}
}
For A small data like double, boolean, string, int you should use onSavedInstance like this, for large amount of data view model will be used.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("MySPinner", GetBusinessPartners.getSelectedItemPosition());
}
Then getValue Like this in OnCreate Method
var counter=0
if (savedInstanceState != null) {
counter = savedInstanceState.getInt("MySPinner", 0)
}
After Spinner Adapter call SetSelection and pass counter in it like,
ArrayAdapter<BusinessPartnersModel>(context, android.R.layout.simple_list_item_1, list)
GetBusinessPartners.setSelection(counter)
I still recommend you to use viewmodel with livedata in this case. Please check my solution.
In the viewmodel, you create the livedata that you want to store the data to display on the view. I still recommend using MutableLiveData to set data for live data, and LiveData for view to get data.
class MealPolicyViewModel : ViewModel() {
private val _businessPartners = MutableLiveData<ArrayList<BusinessPartnersModel>>()
val businessPartners: LiveData<ArrayList<BusinessPartnersModel>> = _businessPartners
private val _spinnerString = MutableLiveData<Int>()
val spinnerString: LiveData<Int> = _spinnerString
fun initsetSpinnerIndex(valueOfSpinner: Int){
_spinnerString.value = valueOfSpinner
Log.d("valueOfValueOFSPinner", valueOfSpinner.toString())
}
}
In the view, specifically MainActivity, you initialize the viewModel through the lazy variable associated with the built-in extension of the activity-ktx library by viewModels(). Then you observe your livedata in onCreate().
class MainActivity : AppCompatActivity() {
private val viewModel: MealPolicyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.spinnerString.observe(this) {
// TODO do something.
}
}
}
As you know, livedata will always observe the lifecycle of the view. In case you rotate the screen, the livedata will observe again when you finish rotating the screen.
Try my implementation and let me know if you still get the error.
save the position in viewmodel,did u try that?but do not call it in spinner adapter after item selected, save it in onPasue and the save button(or ending to network button).
this is how u get position:
binding.spinnerLessonModelName.selectedItemPosition
READ FIRST:
Apologies, it seems I have played myself. I was using RecyclerView in my xml earlier, but switched it over for CardStackView (it still uses the exact same RecyclerView adapter). If I switch back to RecyclerView, the original code below works - the scroll position is saved and restored automatically on configuration change.
I'm using a MVVM viewmodel class which successfully retains list data for a RecyclerView after a configuration change. However, the previous RecyclerView position is not restored. Is this expected behaviour? What would be a good way to solve this?
I saw a blog post on medium briefly mentioning you can preserve scroll position by setting the adapter data before setting said adapter on the RecyclerView.
From what I understand, after a configuration change the livedata that was being observed earlier gets a callback. That callback is where I set my adapter data. But it seems this callback happens after the onCreate() function finishes by which point my RecyclerView adapter is already set.
class MainActivity : AppCompatActivity() {
private val adapter = MovieAdapter()
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Create or retrieve viewmodel and observe data needed for recyclerview
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
viewModel.movies.observe(this, {
adapter.items = it
})
binding.recyclerview.adapter = adapter
// If viewmodel has no data for recyclerview, retrieve it
if (viewModel.movies.value == null) viewModel.retrieveMovies()
}
}
class MovieAdapter :
RecyclerView.Adapter<MovieAdapter.MovieViewHolder>() {
var items: List<Movie> by Delegates.observable(emptyList()) { _, _, _ ->
notifyDataSetChanged()
}
class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val binding = ItemMovieCardBinding.bind(itemView)
fun bind(item: Movie) {
with(binding) {
imagePoster.load(item.posterUrl)
textRating.text = item.rating.toString()
textDate.text = item.date
textOverview.text = item.overview
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_movie_card, parent, false)
return MovieViewHolder(view)
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.size
}
class MainViewModel : ViewModel() {
private val _movies = MutableLiveData<List<Movie>>()
val movies: LiveData<List<Movie>> get() = _movies
fun retrieveMovies() {
viewModelScope.launch {
val client = ApiClient.create()
val result: Movies = withContext(Dispatchers.IO) { client.getPopularMovies() }
_movies.value = result.movies
}
}
}
Set adapter only after its items are available.
viewModel.movies.observe(this, {
adapter.items = it
binding.recyclerview.adapter = adapter
})
I have a RecyclerView which was build using an Arraylist. That Arraylist consists of User defined objects named ListItem.
Each recyclerview has a card view. Each CardView holds each ListItem.
I have removed one CardView from that RecyclerView.
When I rotate the screen , A new Activity is created which results in showing the old data. But I want the recyclerview to hold only updated list and should retain the scrolled position.
ListItem class :
class ListItem(var title: String, var info: String, val imageResource: Int) {
}
MainActivity class :
class MainActivity : AppCompatActivity() {
private lateinit var mSportsData: ArrayList<ListItem>
private lateinit var mAdapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val gridColumnCount = resources.getInteger(R.integer.grid_column_count)
recycler_view.layoutManager = GridLayoutManager(this,gridColumnCount)
mSportsData = ArrayList()
recycler_view.setHasFixedSize(true)
initializeData()
recycler_view.adapter = mAdapter
var swipeDirs = 0
if (gridColumnCount <= 1) {
swipeDirs = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
}
val helper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT or ItemTouchHelper.UP or ItemTouchHelper.DOWN,swipeDirs) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val from = viewHolder.adapterPosition
val to = target.adapterPosition
Collections.swap(mSportsData,from,to)
mAdapter.notifyItemMoved(from,to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
mSportsData.removeAt(viewHolder.adapterPosition)
mAdapter.notifyItemRemoved(viewHolder.adapterPosition)
}
})
helper.attachToRecyclerView(recycler_view)
}
private fun initializeData() {
val sportsList : Array<String> = resources.getStringArray(R.array.sports_titles)
Log.d("Printing","$sportsList")
val sportsInfo : Array<String> = resources.getStringArray(R.array.sports_info)
val sportsImageResources : TypedArray = resources.obtainTypedArray(R.array.sports_images)
mSportsData.clear()
for (i in sportsList.indices-1) {
Log.d("Printing","${sportsList[i]},${sportsInfo[i]},${sportsImageResources.getResourceId(i,0)}")
mSportsData.add(ListItem(sportsList[i], sportsInfo[i], sportsImageResources.getResourceId(i, 0)))
}
sportsImageResources.recycle()
mAdapter = MyAdapter(mSportsData,this)
mAdapter.notifyDataSetChanged()
}
fun resetSports(view: View) {
initializeData()
}
}
MyAdapter class :
class MyAdapter(var mSportsData: ArrayList<ListItem>, var context: Context) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.wordlist_item,parent,false))
}
override fun getItemCount() = mSportsData.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val listItem = mSportsData.get(position)
holder.bindTo(listItem)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
override fun onClick(view: View) {
val currentSport = mSportsData.get(adapterPosition)
val detailIntent = Intent(context, DetailActivity::class.java)
detailIntent.putExtra("title", currentSport.title)
detailIntent.putExtra("image_resource", currentSport.imageResource)
context.startActivity(detailIntent)
}
fun bindTo(currentSport : ListItem){
itemView.heading_textview.setText(currentSport.title)
itemView.description_textview.setText(currentSport.info)
Glide.with(context).load(currentSport.imageResource).into(itemView.image_view)
}
}
}
You can restrict activity restarting in your Manifest if you have same layout for Portrait and Landscape mode.
Add this to your activity in the manifest.
<activity android:name=".activity.YourActivity"
android:label="#string/app_name"
android:configChanges="orientation|screenSize"/>
If you don't want to restrict screen orientation changes, then you can use OnSaveInstanceState method to save your older data when orientation changed. Whatever data you save via this method you will receive it in your OnCreate Method in bundle. Here is the helping link. So here as you have ArrayList of your own class type you also need to use Serializable or Parcelable to put your ArrayList in your Bundle.
Except these making ArrayList as public static is always a solution, But its not a good solution in Object Oriented paratime. It can also give you NullPointerException or loss of data, in case of low memory conditions.
It looks like initializeData is called twice since onCreate is called again on orientation change, you could use some boolean to check if data has been already initialized then skip initializing
What you are doing is you are deleting the values that are passed down to the recyclerview but when the orientation changes the recyclerview reloads from activity and the original data from activity is passed down again and nothing changes, so if you want to save the changes in recyclerview you have to change the original data in the activity so that if the view reloads the data is the same.
I think u initialize adapter in oncreate method in which the whole adapter will be recreated and all datas is also newly created when configuration changes. Because u init data in oncreate method. Try something globally maintain the list and also delete the item in the list in activity when u delete in adapter also. Or try something like view model architecture
Use MVVM pattern in the project. It will manage the orientation state.
MVVM RecyclerView example:
https://medium.com/#Varnit/android-data-binding-with-recycler-views-and-mvvm-a-clean-coding-approach-c5eaf3cf3d72