Question about the interaction between Fragments and the Adapter (Kotlin) (Android) - android

Is it possible to share variable values between Fragments and RecyclerView Adapters? I am currently using SharedPreferences to store data persistently.
I am currently working on a Save button that upon clicking, it will save the article in the Room database. Each itemview in the recyclerview has a save button, I am having trouble updating the UI of the button upon clicking it.
For example, the button is supposed to have a default text save, upon clicking it once, it saves the data and changes the Button text to saved.
In the Activity below I initialized the preferences variable
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var preferences:SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
preferences = getSharedPreferences("file",Context.MODE_PRIVATE)
}
}
This is the fragment where I use the RecyclerView. In the code below I am resolving the logic part of the button, like for instance when the button is clicked it is saved in the Database/displaying Toast.
That is when the main problem comes as I am having difficulty syncing the UI and the Logic aspect of the Button. The if-statement helps with the states of the button
BreakingNewsFragment
class BreakingNews: Fragment(R.layout.breakingnews) {
lateinit var Main:SharedPreferences
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Main = (activity as MainActivity).preferences //Initializing the Main lateinit var
val editor = Main.edit()
newsAdapter.savedOnClickListener {
if(Main.getInt("numbers",0) == 0)
{
editor.putInt("Adapter",0).apply()
Toast.makeText(activity, "Article Successfully saved", Toast.LENGTH_SHORT).show()
editor.putInt("numbers",1).apply()
viewModel.saveArticle(it)
}else if(Main.getInt("numbers",0) == 1) {
editor.putInt("Adapter",1).apply()
android.widget.Toast.makeText(activity, "Article is already saved", android.widget.Toast.LENGTH_SHORT).show()
editor.putInt("numbers",2).apply()
}else if(Main.getInt("numbers",0) == 2){
editor.clear().apply()
android.widget.Toast.makeText(activity, "Restart", android.widget.Toast.LENGTH_SHORT).show()
}
}
}
}
This is where I will handle the UI portion of the Button, I can't find a way to share variable values between the Adapter and the Fragment so they can be in sync.
NewsAdapter.kt
class NewsAdapter: RecyclerView.Adapter<NewsAdapter.ArticleViewHolder>() {
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val button = itemView.findViewById<Button>(R.id.button)}
fun savedOnClickListener(listen:(Article) -> Unit) {
SavedonItemClickListener = listen
}
private var SavedonItemClickListener: ((Article) -> Unit)? = null
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
val article = differ.currentList[position]
holder.button.setOnClickListener {
Log.e("Adapter","Before savedOnITEM")
SavedonItemClickListener?.let {
Log.e("Adapter","After if")
it(article)
}
}
}
}

Related

Is there any way to get onClickListener from View in Kotlin?

For example, I've got a complex fragment layout with some visibility changes that appear very frequently, so the state of my layout that I've set in onViewCreated will change many times during the user works with app. I want to store its state in my view model in case of activity recreation (so my layout will stay in the state before recreation). I managed to fully restore my layout with exception of my listeners. Although I can just refactor my code and reassign them manually, I just want to know if there any way to store listeners as well. Here is some abstract code to show what I'm doing and trying to archive.
class MyFragment: Fragment {
private val viewModel: MyViewModel by viewModels()
override fun onCreateView {
//binding
}
override fun onViewCreated {
if (viewModel.isStateSaved.value == true) {
restoreState()
} else {
myView1.visibility = Visibility.GONE
//i can move listeren assignment from if else but maby there are better way to do it
myView.setOnClickListener(listener) // some listener. Realization isnt really important
myView2.visibility = Visibility.VISIBLE
}
}
//abstract function to show what how my fragment layout will change during user interaction
fun onUserInteractsWithApp() {
myView1.visibility = Visibility.VISIBLE
myView1.isEnabled = false
}
override fun onStop() {
//for example fun store state just copies state of id, isEnabled, visibility and so on
//in that method i want to also get OnClickListener from a view if that possible
viewModel.storeState(myView1)
viewModel.storeState(myView2)
}
fun restoreState() {
//finding view by stored id and assigning stored values
}
}
Hello Mr George Actually i did not understand your question completely but what i get from your title of question to get clickListner from View this is answer for that
make an object of onClickLisner and assign to your view
val tempListner =object : View.OnClickListener {
override fun onClick(p0: View?) {
TODO("Not yet implemented")
}
}
..................
..................
..........
myView.setOnClickListener(tempListner)
Here is an real example from my app
private fun setOnPageChangedAction(
tab_viewpager: ViewPager?,
bottomNavigationView: BottomNavigationView
) {
tab_viewpager?.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
pageId = position
if (position == 0) {
bottomNavigationView.setSelectedItemId(R.id.homeFragment);
} else if (position == 1) {
bottomNavigationView.setSelectedItemId(R.id.dashboardFragment);
} else if (position == 2) {
bottomNavigationView.setSelectedItemId(R.id.notificationsFragment);
} else if (position == 3) {
bottomNavigationView.setSelectedItemId(R.id.profileFragment);
}
}
})
}
you can replace PageListener texts with ClickListener. It should show you that as an action.

RecyclerView doesn't update its view but List is updated

I am using recyclerView to show list of available apps in device..moreover I am using bottomSheet to show more details about selected app ...in this section, I place uninstall button ...here I use uninstall code and from onActivityResult method in BottomSheetDialog.kt file ... on OK pressed ....I want to delete that app/item from list and update View....here list is correct in coding but recyclerView doesn't update its list
Note: I debug the code and found that list got updated in BottomSheet File...I comment out that ....but
recyclerView doesn't
I searched on internet, but didn't find solution which fits in my case
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.adapter = Adapter(applicationList) // here I send mutable list of all apps in device to adapter
recyclerView.layoutManager = LinearLayoutManager(this)
private fun getApps(List: MutableList<ResolveInfo>): MutableList<AppData> {
// here I return list to adapter with details of installed apps like icon, name, packageName etc
}
DataClass
data class AppData(
val icon: Drawable,
val name: String,
val packageName: String
.....
.....)
Adapter.kt
class Adapter(private val listOfApps: MutableList<AppData>) :
RecyclerView.Adapter<Adapter.ViewHolder>() {
// here I receive mutableList in constructor of Adapter
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
init { // initiate both click listeners
appView.setOnClickListener(this)
appView.setOnLongClickListener(this)
}
// call elements from activity.xml
val icon: ImageView = appView.App_icon
val name: TextView = appView.App_name
val size: TextView = appView.App_size
override fun onClick(v: View?) {
Toast.makeText(v?.context, "OnClick", Toast.LENGTH_SHORT).show()
}
override fun onLongClick(v: View?): Boolean {
val bottomSheetDialog = BottomSheetDialog(currentItem, appList)
// send currentItem and all List to BottomSheetDialog to show details with the help of function
// Show bottomSheet on LongPress
return true
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder // done
override fun getItemCount() = listOfApps.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = listOfApps[position]
holder.icon.setImageDrawable(currentItem.icon)
holder.name.text = currentItem.name
holder.size.text = currentItem.size
}
BottomSheetDialog.kt ...... here in onActivtyResult I delete item and call notify method ... problem is here
class BottomSheetDialog(private val appData: AppData, private val appList: MutableList<AppData>) :
BottomSheetDialogFragment() {
// here I receive appData and AppList in constructor from Adapter OnLongPress
override fun onCreateView() // done
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// here when bottomSheet popup and on click of uninstall.....I check whether user click on OK or CANCEL in onActivity Method (overidden below)
Uninstall_App.setOnClickListener {
// permission in manifest added
val intent = Intent(Intent.ACTION_DELETE)
intent.data = Uri.parse("package:${appData.packageName}")
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
startActivityForResult(intent, 1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// get result from uninstall dialog
if (resultCode == -1) { // ok pressed
Toast.makeText(context, "ok clicked", Toast.LENGTH_SHORT).show()
dismiss()
// here when user pressed OK....delete that item from List
val index = appList.indexOf(appData)
appList.removeAt(index)
Adapter(appList).notifyItemRemoved(index)
Adapter(appList).notifyDataSetChanged()
// I check above three line by debugging it
// 1. val index .. return index of current item
// 2. this line remove that item
// 3. Adapter(appList) .... notify Item removed
// 4. here that indexed item is removed but view is not updated
// Note: if it is wrong which is the best method to do this
} else if (resultCode == 0) { // cancel pressed
Toast.makeText(context, "Cancel Click", Toast.LENGTH_SHORT).show()
}
}
these lines
Adapter(appList).notifyItemRemoved(index)
Adapter(appList).notifyDataSetChanged()
are both creating new adapters, notify them and.. thats all. they aren't attached to any RecyclerView (as you do in onCreate), so won't be drawn anywhere
you should notify adapter already set for RecyclerView - keep reference in Activity and refer to it instead of creating new one
What you did here is created two new adapters (that have no relation to the adapter used by recycler view except the type of adapter is the same):
Adapter(appList).notifyItemRemoved(index)
Adapter(appList).notifyDataSetChanged()
You can create an interface to listen for changes from BottomSheetDialog:
interface OnAppDeletedListener {
fun appDeletedAtIndex(index: Int)
}
Update your BottomSheetDialog to accept an additional argument of type OnAppDeletedListener:
class BottomSheetDialog(private val appData: AppData, private val appList: MutableList<AppData>, private val listener: OnAppDeletedListener) :
BottomSheetDialogFragment() {
...
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
val index = appList.indexOf(appData)
listener.appDeletedAtIndex(index)
dismiss()
}
}
}
Update your adapter. It must not be responsible for showing any dialogues. Activity or Fragment is responsible for that.
class Adapter(private val listOfApps: MutableList<AppData>, private val longClickListener: View.OnLongClickListener) :
RecyclerView.Adapter<Adapter.ViewHolder>() {
// here I receive mutableList in constructor of Adapter
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener {
init { // initiate both click listeners
appView.setOnClickListener(this)
appView.setOnLongClickListener(longClickListener)
}
// call elements from activity.xml
val icon: ImageView = appView.App_icon
val name: TextView = appView.App_name
val size: TextView = appView.App_size
override fun onClick(v: View?) {
Toast.makeText(v?.context, "OnClick", Toast.LENGTH_SHORT).show()
}
}
}
And update your activity code:
class MainActivity : AppCompatActivity() {
private lateinit var adapter: Adapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val longClickListener = object: View.OnLongClickListener {
override fun onLongClick(v: View?): Boolean {
displayAppInfoDialog()
return true
}
}
adapter = Adapter(applicationList)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
}
private fun displayAppInfoDialog() {
val listener = object: OnAppDeletedListener() {
fun appDeletedAtIndex(index: Int) {
adapter.notifyItemRemoved(index)
}
}
val bottomSheetDialog = BottomSheetDialog(currentItem, appList, listener)
bottomSheetDialog.show()
}
...
}
As already stated, you are not updating the existing Adapter, you are instead creating two new instances.
Replace this line:
recyclerView.adapter = Adapter(applicationList) // here I send mutable list of all apps in device to adapter
With
this.adapter = Adapter(applicationList)
recyclerView.adapter = this.adapter
Also add val adapter: Adapter? to your class.
Now you have a reference to the adapter the RecyclerView has.
Finally, when you want to "update" it:
// here when user pressed OK....delete that item from List
val index = appList.indexOf(appData)
appList.removeAt(index)
Adapter(appList).notifyItemRemoved(index)
Adapter(appList).notifyDataSetChanged()
Should become...
// here when user pressed OK....delete that item from List
val index = appList.indexOf(appData)
appList.removeAt(index)
this.adapter.notifyItemRemoved(index)
this.adapter.notifyDataSetChanged()
IMPORTANT CAVEAT: There are other issues here with the separation of concerns in your code, but among them, the fact that the appList you use here, is a local list; does it contain the items from applicationList (the one you used when you created the adapter)? If it doesn't then you need to expose said list so you can either modify it/replace it, etc.
The Adapter does NOT manage the list for you, it merely uses it to adapt each item to a ViewHolder. If you modify the list the adapter has, and you tell it that you inserted an item at certain position, etc. All the adapter does is (a lot behind the scenes) and "re-binds" the view at that position (if it's visible) with the new data.

Android - Store ArrayList persistent

My app is displaying a list of various categories (herbs, side dishes, ..) in a RecyclerView. Depending on the category you clicked on, a new Activity with a new RecylcerView opens containing all the ingredients.
Right now I have an ArrayList which gets filled with the ingredients via ".add" depending on the choosen category.
The problem im facing right now is, that I want to implement an option for the user to add own Ingredients. I tried storing the ArrayList containing the ingredients in SharedPreferences by using Gson, but I couldn't manage to add elements, since it always overwrote the current list.
What would be the best way to store the ingredients? A room, sqlite, ..?
Without further explanation, the ingredient list will only contain about 70 items max.
Thanks in advance.
Edit:
CatList.kt
class CatList : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cat_list)
//Create List for categories
val cats = ArrayList<IngCat>()
//Fill categories
cats.add(IngCat(R.drawable.herbs, "Herbs"))
cats.add(IngCat(R.drawable.fluessiges, "Liquids"))
cats.add(IngCat(R.drawable.festes, "Solids"))
cats.add(IngCat(R.drawable.beilagen, "Sides"))
//Recyclerview
id_rv_CatList.layoutManager = LinearLayoutManager(this)
id_rv_CatList.adapter =
CatListAdapter(cats) {listItem, position -> //go to Ingredient List Activity
goToIngList(position, listItem.name)
}
//id_rv_CatList.addItemDecoration(DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL))
//actionbar
val actionbar = supportActionBar
//set actionbar title
actionbar!!.title = "Ingredient - Categories"
}
private fun goToIngList(cat: Int, name: String){
val intent = Intent(this, IngList::class.java)
intent.putExtra("Category", cat)
intent.putExtra("Name", name)
startActivity(intent)
}
}
data class IngCat(var mImageResource:Int, var name:String)
IngList.kt
class IngList : AppCompatActivity() {
companion object {
var categoryChoosen : Int = 0
var catName : String = "Err"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ing_list)
//Initilize Ingredient List
val ings :ArrayList<IngIng> = ArrayList()
//Get category and category name
categoryChoosen = intent.getIntExtra("Kategorie",0)
catName = intent.getStringExtra("Name")!!
when (categoryChoosen) {
0 -> {
ings.add(IngIng("https://doeel.com/images/thumbnails/1100/900/detailed /92/Turmeric_Powder___Holud_Gura__.png", "Turmeric Powder"))
}
1 -> ings.add(IngIng("https://www.miraherba.de/4923-large_default/bio-ghee-300-g.jpg", "Ghee"))
2 -> ings.add(IngIng("https://www.organicfacts.net/wp-content/uploads/coriander-1.jpg", "Coriander leaves"))
3 -> ings.add(IngIng("https://gbc-cdn-public-media.azureedge.net/img75602.1426x713.jpg", "Potatoes"))
}
//Actionbar Settings
setSupportActionBar(toolbar)
val actionbar = supportActionBar
actionbar!!.title = "Ingredients- $catName"
actionbar.setDisplayHomeAsUpEnabled(true)
//Recyclerview
id_rv_IngList.layoutManager = GridLayoutManager(this,2)
id_rv_IngList.adapter =
IngListAdapter(ings) {//ClickListener RecyclerView
Toast.makeText(this, "Item clicked: ${it.name}", Toast.LENGTH_SHORT).show()
}
//Actionbar
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.actionbar_ing_list, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.id_menu_action_add -> {
val intent = Intent(this, AddIngredient::class.java)
startActivity(intent)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
}
IngListAdapter.kt
class IngListAdapter (private val ings: ArrayList<IngIng>, val clickListener: (IngIng)->Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun getItemCount(): Int = ings.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val v: View = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_ing_list_item, parent, false)
return IngViewHolder(v)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var currentItem = ings.get(position)
when (holder) {
is IngViewHolder -> {
holder.tvIngList.text = currentItem.name
// holder.ivIngImage.setImageResource(currentItem.mImageResource)
Picasso.get().load(currentItem.mImageResource).placeholder(R.drawable.ic_broken_image_black_200dp).error(R.drawable.ic_broken_image_red_24dp).into(holder.ivIngImage)
holder.cvIngCard.setOnClickListener{
clickListener(currentItem)
}
}
}
}
}
class IngViewHolder (view: View) : RecyclerView.ViewHolder(view) {
val tvIngList: TextView = view.id_text_ing
val ivIngImage: ImageView = view.id_img_ing
val cvIngCard: MaterialCardView = view.id_cv_ing_list
}
I personally think Json/Gson in a SharedPreference is the easiest way to go if there are so few items. The way I would handle it is to store the list in memory at application startup, and persist the list back to the SharedPreference when the app is shut down. Also when the app gets stopped for good measure because you can't 100% be sure onDestroy will be called.
So first I'd make a class to store the data. If you were using Fragments that all are in the same Activity, you'd put this in a ViewModel. But since they are separate Activities, you need a singleton for them. (Google doesn't recommend using multiple Activities because it's hard to share data between them. But it's not impossible. It's what we did before Fragments.)
To do it as a singleton, you could have a class like this:
class IngredientsRepo private constructor (application: Application) {
companion object {
private val INSTANCE: IngredientsRepo? = null
fun getInstance(application: Application) =
INSTANCE ?: IngredientsRepo(application).also { INSTANCE = it }
private const KEY_JSON_PREF = "ingredientsJson"
}
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
val herbsList: MutableList<IngCat>
val liquidsList: MutableList<IngCat>
val solidsList: MutableList<IngCat>
val sidesList: MutableList<IngCat>
init {
val json = sharedPreferences.getString(KEY_JSON_PREF, null)
if (json == null) {
// initialize your list contents for the first time
} else {
// convert your json and fill the data into your lists
}
}
fun save {
val jsonString = // Convert your lists to Json
sharedPreferences.edit().putString(KEY_JSON_PREF, jsonString).apply()
}
}
This class becomes responsible for setting up your lists. You can retrieve it from any Activity with IngredientsRepo.getInstance(this) and you can add and remove items from the lists whenever you like. You can also call save on it whenever you like to persist the latest data. It's probably sufficient to do this in onStop() of any Activity that modifies the list.
More properly, the data in this class would only be exposed with immutable lists, and you'd add functions for adding and removing items, so only this class directly modifies the lists. I didn't want to overcomplicate the example, but it would be better for encapsulation to not have Activities (which are supposed to be pure UI components) directly modifying data structures.

How to save data when i press back button

I have made an app in kotlin through the android studio, Now I have used ViewModels to save UI data while phone rotation(configuration change), i also used onSaveInstanceState to save data while pressing back button but it's not working.
The code is below
fragOne.kt
class fragOne : Fragment() {
private lateinit var viewModel: fragViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if(savedInstanceState!=null){
with(savedInstanceState) {
viewModel.num=getInt("number")
}
}
// Inflate the layout for this fragment
var binding = DataBindingUtil.inflate<FragmentFragoneBinding>(
inflater,
R.layout.fragment_fragone,
container,
false
)
viewModel = ViewModelProviders.of(this).get(fragViewModel::class.java)
// function to update number
fun updateNumber()
{
binding.number.text="${viewModel.num}"
}
updateNumber()
// setting on Click listener for add button
binding.add.setOnClickListener()
{
viewModel.addFive()
updateNumber()
}
// setting on on Click Listener for minus button
binding.minus.setOnClickListener()
{
viewModel.minusOne()
updateNumber()
}
return binding.root
}
override fun onSaveInstanceState(outState: Bundle) {
// Save the user's current game state
outState?.run {
putInt("number",viewModel.num)
}
// Always call the superclass so it can save the view hierarchy state
if (outState != null) {
super.onSaveInstanceState(outState)
}
}
}
ViewModelclass
class fragViewModel:ViewModel()
{
// Initializing num=0
var num=0
// Functions to add five or subtract one
fun addFive()
{
num=num+5
}
fun minusOne()
{
num=num-1
}
}
please tell me because data is not saved when I press back
You can override onBackPressed to do your state saving:
How to implement onBackPressed() in Fragments?
Remember to call super, so that is does also do the back command!
You could also do like the below:
// This callback will only be called when MyFragment is at least Started.
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// Handle the back button event
}
Really good read: https://developer.android.com/guide/navigation/navigation-custom-back
Back navigation is how users move backward through the history of screens they previously visited. All Android devices provide a Back button for this type of navigation, so you should not add a Back button to your app’s UI. Depending on the user’s Android device, this button might be a physical button or a software button.
Ref:
How to show warning message when back button is pressed in fragments
Example:
Ensure your Activity extends AppCompatActivity
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(fragViewModel::class.java)
val prefs = activity.getSharedPreferences("Key")
int num = prefs.get("number", -999)
if(num != -999) {
viewModel.num = num
}
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
prefs.edit().putInt("number", viewModel.num).apply()
}
}
...
}

RecyclerView always empty after coming back from other fragment?

I have set certain condition about when to fetch data from the internet, if the last time fetching data is more than 10 minutes ago, then fetch data from the internet, So i don't need to fetch data over and over again when coming back from other fragment. I wrote this code inonResume ,
I assume the product data will still be on my RecyclerView after coming back from other fragment.
If the last time fetching data is more than 10 minutes ago, then I can populate the RecyclerView view with the product data like this :-
But problem is, when i move from Home Fragment to other fragment, For example if tap other tab in the bottom navigation menu, RecyclerView seems empty, it just text view that appear on the screen like this. (If I back again to the home fragment, it means the last time I fetch product data from server is not more than 10 minutes ago)
the toolbar and the bottom navigation are part of my Main Activity, so I change fragment in the center part
is my problem because of the onDestroy and onDetach of my HomeFragment is activated when I change to other fragment ?
what went wrong in here ?
here is the simplified code of my Home Fragment
class HomeFragment : androidx.fragment.app.Fragment() {
lateinit var mContext : Context
lateinit var mActivity : FragmentActivity
lateinit var recyclerView1 : RecyclerView
lateinit var fragmentView : View
private var firstProducts = listOf<Product>()
lateinit var firstProductAdapter : ProductListAdapter
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
activity?.let { mActivity = it }
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// set up view
fragmentView = inflater.inflate(R.layout.fragment_home, container, false)
recyclerView1 = fragmentView.findViewById(R.id.recyclerView_1)
return fragmentView
}
override fun onResume() {
super.onResume()
if (lastTimeFetchDataIsMoreThan10MinutesAgo) {
getProducts() // when I open the app for the very first time, I fetch the product data
} else {
// when it is more than 10 minutes, do nothing
}
}
override fun onStop() {
super.onStop()
progressBar.visibility = View.INVISIBLE // to ensure the progress bar will always dissapear if move to another destination
}
private fun getProducts(type: String) {
showProgressBar(true)
Product.getProductsFromServer(customerID = userData.id.toString(), type = type) { errorMessage, products ->
errorMessage?.let {
activity?.toast(it)
} ?: run {
val productList = products ?: ArrayList()
setUpRecyclerView(type = type,products = productList)
}
}
showProgressBar(false)
}
private fun setUpRecyclerView(type: String, products: List<Product>) {
val productAdapter = ProductListAdapter(context = mContext,products = products)
val layoutManager = LinearLayoutManager(mContext,LinearLayoutManager.HORIZONTAL,false)
if (type == "special") {
firstProductAdapter = productAdapter
firstProducts = products
recyclerView1.adapter = productAdapter
recyclerView1.layoutManager = layoutManager
recyclerView1.setHasFixedSize(true)
}
}
private fun showProgressBar(enable: Boolean) {
if (enable) {
progressBar.visibility = View.VISIBLE
recyclerView1.visibility = View.GONE
selectedProductTextView.visibility = View.GONE
bestSellingProductTextView.visibility = View.GONE
} else {
progressBar.visibility = View.GONE
recyclerView1.visibility = View.VISIBLE
selectedProductTextView.visibility = View.VISIBLE
bestSellingProductTextView.visibility = View.VISIBLE
}
}
}

Categories

Resources