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
}
}
}
Related
I have an arraylist (called Itemlist) of all recyclerview elements. In each element there are 2 textviews - a german and english word. only one of them is shown (because they overlap). when i click on the element it shows the other language (for example: the german word is set to gone and the english word is visible now).
Now I want a function which sets all english textviews (in every element) to gone and the german to visible. My problem is - i dont know how to reach all elements in this arraylist and check the visibility of the textviews. in my example it resets only the first word.
For better understanding
Here is the code:
fun reset_to_EN() {
ItemList.forEach { test_if_german() }
}
OR
fun reset_to_EN2() {
for (item in ItemList) {
test_if_german()
}
}
Check visibility
fun test_if_german(){
if (text_view_de.visibility == View.VISIBLE) {
text_view_en.visibility = View.VISIBLE
text_view_de.visibility = View.GONE
}
adapter.notifyDataSetChanged()
}
If you can please show me a code example for better understanding.
Thanks to everyone who tries to help.
Or here is the whole code for the adapter and mainActivity if it's needed:
class Adapter(
val c: Context,
private val ArrList: ArrayList<Item>):
RecyclerView.Adapter<Adapter.ViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
return ViewHolder(inflater)
}
override fun getItemCount() = ArrList.size
inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener {
var textViewDe: TextView = v.text_view_de
var textViewEn: TextView = v.text_view_en
private var menueImage: Button
init {
v.setOnClickListener(this)
textViewDe = v.findViewById(R.id.text_view_de)
textViewEn = v.findViewById(R.id.text_view_en)
menueImage = v.findViewById(R.id.menu_button)
menueImage.setOnClickListener { popupMenu(it) }
}
private fun popupMenu(v:View) {
val drop = PopupMenu(c, v)
val position = ArrList[adapterPosition]
drop.inflate(R.menu.drop_menu)
drop.setOnMenuItemClickListener {
when(it.itemId){
R.id.edit_menu->{
val v2 = LayoutInflater.from(c).inflate(R.layout.add_item_layout,null)
val DE = v2.findViewById<EditText>(R.id.editText)
val EN = v2.findViewById<EditText>(R.id.editText2)
AlertDialog.Builder(c)
.setView(v2)
.setPositiveButton("Ok"){
dialog,_->
position.Englisch = DE.text.toString()
position.Deutsch = EN.text.toString()
notifyDataSetChanged()
//Toast.makeText(c,"User Information is Edited",Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
.setNegativeButton("Cancel"){
dialog,_->
dialog.dismiss()
}
.create()
.show()
true
}
R.id.delete_menu-> {
ArrList.removeAt(adapterPosition)
notifyDataSetChanged()
//Toast.makeText(c,"entfernt",Toast.LENGTH_SHORT).show()
true
}
else -> true
}
}
drop.show()
val popup = PopupMenu::class.java.getDeclaredField("mPopup")
popup.isAccessible = true
val menu = popup.get(drop)
menu.javaClass.getDeclaredMethod("setForceShowIcon",Boolean::class.java)
.invoke(menu,true)
}
override fun onClick(p0: View?) {
if (textViewDe.visibility == View.VISIBLE) {
textViewDe.visibility = View.GONE
textViewEn.visibility = View.VISIBLE
} else {
textViewDe.visibility = View.VISIBLE
textViewEn.visibility = View.GONE
}
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = ArrList[position]
holder.textViewDe.text = currentItem.Deutsch
holder.textViewEn.text = currentItem.Englisch
}
And MainActivity:
class MainActivity : AppCompatActivity() {
//DEFINITION
private lateinit var addButton: FloatingActionButton
private lateinit var ItemList: ArrayList<Item>
private lateinit var recy: RecyclerView
private lateinit var adapter: Adapter
//ONCREATE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//FINDVIEWBYID
addButton = findViewById(R.id.addingBtn)
ItemList = ArrayList()
recy = findViewById(R.id.recycler_view)
//RECYCLERVIEW
adapter = Adapter(this, ItemList)
recy.layoutManager = LinearLayoutManager(this)
recy.adapter = adapter
//FUNCTION-CALL
addButton.setOnClickListener { addInfo() }
}
//FUNKTIONENS
private fun addInfo() {
val inflter = LayoutInflater.from(this)
val v = inflter.inflate(R.layout.add_item_layout, null) //
val eng = v.findViewById<EditText>(R.id.editText)
val deu = v.findViewById<EditText>(R.id.editText2)
val addDialog = AlertDialog.Builder(this)
addDialog.setView(v)
addDialog.setPositiveButton("OK"){ dialog, _->
val eng2 = eng.text.toString()
val deu2 = deu.text.toString()
val UUID = UUID.randomUUID()
ItemList.add(Item(UUID, eng2, deu2))
adapter.notifyDataSetChanged()
//Toast.makeText(this, "Adding Success", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
addDialog.setNegativeButton("Cancel"){ dialog, _->
dialog.dismiss()
}
addDialog.create()
addDialog.show()
}
fun clearData() {
ItemList.clear()
adapter.notifyDataSetChanged()
Toast.makeText(this, "Alles gelöscht", Toast.LENGTH_SHORT).show()
}
fun reset_all_EN() {
//ArrayList = ItemList
val size: Int = ItemList.size
for (i in 0 until size) {
if (text_view_de.visibility == View.VISIBLE) {
text_view_en.visibility = View.VISIBLE
text_view_de.visibility = View.GONE
}
adapter.notifyDataSetChanged()
}
}
fun reset_to_EN() {
// using forEach() method
ItemList.forEach { test_if_german() }
}
fun reset_to_EN2() {
for (item in ItemList) {
test_if_german()
}
}
fun test_if_german(){
if (text_view_de.visibility == View.VISIBLE) {
text_view_en.visibility = View.VISIBLE
text_view_de.visibility = View.GONE
}
adapter.notifyDataSetChanged()
}
//MENU CLASSES
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.open_menu -> {
val intent = Intent(this, InfoActivity::class.java)
startActivity(intent)
}
R.id.open_menu2 -> {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
}
R.id.reset_all -> {
reset_to_EN2()
}
}
return super.onOptionsItemSelected(item)
}
}
Since I don't see any declaration of text_view_de or text_view_en, I'm guessing you're using synthetic view properties from the deprecated Android Kotlin Extensions. Assuming that is the case:
When you use text_view_de, it is performing a search in your view hierarchy for the first view it finds with the matching ID. So even though you are doing it within a for loop that iterates through your list of items, you are only working with the same view, over and over.
Edit:
I realized you want to be able to toggle individual views and you were only asking how to add a button to reset all views back to the same language. If this is the case, it does not make sense to add a property to the adapter that controls the state of all views at once like I had suggested in the previous revision of this answer.
Instead, you need to change your data model to have a Boolean that determines which specific language that specific item should show. The problem with how you're doing it now in your click listener is that it is trying to use the Views themselves to determine what state the item is when you change it, but this will cause weird glitches when items scroll off of the screen and back on because ViewHolders get recycled and assigned to different items when they go off and back on screen.
To get started, add a Boolean for the state of the item to your Item class. I don't know exactly what your class looks like now, so adapt this as needed:
data class Item (
val UUID: Long,
val english: String,
val deutsch: String,
var isShowDeutch: Boolean = true
)
A good practice is to have your Adapter class expose a callback for items being clicked so the outside class (Activity) is responsible for manipulating the data model and the Adapter's responsibility is limited to connecting data to views, not manipulating data. So create a callback that the Activity can implement that toggles a single Item's isShowDeutsch property. And when you bind data to a view, use that item's isShowDeutsch to determine visibility.
In Adapter class:
var onItemClickListener: ((itemPosition: Int)->Unit)? = null
//...
// In ViewHolder:
override fun onClick(view: View) {
itemClickListener?.invoke(adapterPosition)
}
//...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = ArrList[position]
holder.textViewDe.text = currentItem.Deutsch
holder.textViewEn.text = currentItem.Englisch
holder.textViewDe.isVisible = currentItem.isShowDeutsch
holder.textViewEn.isVibible = !currentItem.isShowDeutsch
}
In your Activity when you set up your adapter, you can define a click listener for it that toggles the state of that single item and notifies the adapter of the change:
//RECYCLERVIEW
adapter = Adapter(this, ItemList)
recy.layoutManager = LinearLayoutManager(this)
recy.adapter = adapter
adapter.onItemClickListener = { position ->
ItemList[position].apply { isShowDeutsch = !isShowDeutsch }
adapter.notifyItemChanged(position)
}
And finally, to reset all items back to their original language, you can iterate the items in your list and then notify the adapter. This is more appropriate to do in your Activity, since the Adapter should not be responsible for manipulating data.
fun resetLanguage() {
for (item in ItemList) {
item.isShowDeutsch = true
}
adapter.notifyDataSetChanged()
}
I also recommend you change lateinit var ItemList: ArrayList<Item> to val ItemList = ArrayList<Item>(). It is error prone to have a mutable list type in a mutable var property because there are two different ways to change it and it creates the possibility of having your adapter looking at a different list than the one your Activity is working with.
I want to show progress bar on the screen untill all the required data is fetched from firebase database.
How can I use the below code in the fetchMenu() [in MenuFragment.kt]
.addOnSuccessListener(OnSuccessListener<Void?> { // Write was successful!
// ...
progressBar.visibility = View.GONE
})
.addOnFailureListener(OnFailureListener { // Write failed
// ...
progressBar.visibility = View.GONE
})
MenuFragment.kt
class MenuFragment : Fragment() {
private var dishList: MutableList<DishModel> = mutableListOf()
private lateinit var myRef: DatabaseReference
lateinit var list: RecyclerView
lateinit var proceedToCartLayout: RelativeLayout
lateinit var addToCartBtn: Button
private var selectedCategory = ""
lateinit var progressLayout: RelativeLayout
lateinit var progressBar: ProgressBar
companion object {
fun newInstance(): Fragment {
return MenuFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_menu, container, false)
//retrieve id
val bundle = this.arguments
selectedCategory = bundle!!.getString("CATEGORY_ID")!!
list = view.findViewById(R.id.recyclerMenu)
myRef = FirebaseDatabase.getInstance().getReference("Category")
proceedToCartLayout = view.findViewById(R.id.ProceedToCart)
addToCartBtn = view.findViewById(R.id.btn_cart)
progressLayout = view.findViewById(R.id.progressLayout)
progressBar = view.findViewById(R.id.progressBar)
return view
}
override fun onResume() {
if (ConnectionManager().checkConnectivity(activity as Context)) {
fetchMenu()
} else {
val alterDialog = androidx.appcompat.app.AlertDialog.Builder(activity as
Context)
alterDialog.setTitle("No Internet")
alterDialog.setMessage("Connect to internet to continue")
alterDialog.setIcon(R.drawable.nointernet)
alterDialog.setPositiveButton("Open Settings") { _, _ ->
val settingsIntent = Intent(Settings.ACTION_SETTINGS)//open wifi settings
startActivity(settingsIntent)
}
alterDialog.setNegativeButton("Exit") { _, _ ->
ActivityCompat.finishAffinity(activity as Activity)
}
alterDialog.setCancelable(false)
alterDialog.create()
alterDialog.show()
}
super.onResume()
}
private fun fetchMenu() {
progressLayout.visibility = View.VISIBLE
myRef.child(selectedCategory)
.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
progressLayout.visibility = View.GONE
Toast.makeText(context, "$p0", Toast.LENGTH_SHORT).show()
}
override fun onDataChange(p0: DataSnapshot) {
progressLayout.visibility = View.GONE
if (p0.exists()) {
dishList.clear()
for (i in p0.children) {
val plan = i.getValue(DishModel::class.java)
dishList.add(plan!!)
}
val adapter = MenuAdapter(
context!!,
R.layout.menu_list_item,
dishList,
proceedToCartLayout,
addToCartBtn, selectedCategory
)
list.adapter = adapter
}
}
})
}
}
I got the result after using progress bar but didn't get the required result as I want to display the progress bar untill all the rows of the RecyclerView get filled with the data fetched from the firebase database and all the views correctly placed.
I'm getting this while using progress bar
see the video
According to your shared image, you aren't displaying the "ProgressBar" at all. To solve this, please uncomment the following line of code:
progressLayout.visibility = View.VISIBLE
And add the following line:
progressLayout.visibility = View.GONE
Inside the "onDataChange()" method as well. In this way you are starting to display the ProgressBar when the "fetchMenu()" method is called and once you get a response, either the data or an Exception you can hide it.
I have an ActionBar menu icon that opens a CategoryFragment. This fragment takes in a category object SafeArgs argument passed from another fragment. In the CategoryFragment, I store the category's name and id into the fragment's shared ViewModel as SavedStateHandle values. I've setup it up so that the fragment uses the stored SavedStateHandle values for the category name and id when it needs to. For example, for the first time, the CategoryFragment uses the category object passed from the sending fragment, but subsequent creation of the CategoryFrgament will use the SavedStateHandle values.
The problem is, if after first opening CategoriesFragment and then exiting the app by either pressing the phone's physical back button or terminating the app from the phone's recent's button in the navbar, now opening the CategoryFragment directly by pressing the ActionBar menu icon displays a blank screen. This is because the values returned from SavedStateHandle are null. How can I fix this?
Category Fragment
class CategoryFragment : Fragment(), SearchView.OnQueryTextListener {
lateinit var navController: NavController
private var adapter: TasksRecyclerAdapter? = null
private val viewModel: CategoryTasksViewModel by activityViewModels()
private var fromCategoriesFragment: Boolean = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_category, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
observerSetup()
recyclerSetup()
var searchView = category_tasks_searchview
searchView.setOnQueryTextListener(this)
fab_new_task.setOnClickListener {
navController.navigate(R.id.action_categoryFragment_to_newTaskDialogFragment)
}
showTasks()
}
private fun showTasks() {
if(fromCategoriesFragment){
PomoPlayObservablesSingleton.fromCategoriesFragment.onNext(false)
if (!arguments?.isEmpty!!) {
var args = CategoryFragmentArgs.fromBundle(arguments!!)
category_title.text = args.category?.name
var category = args.category
viewModel.setPomoCategoryName(category.name)
viewModel.setCategoryId(category.id)
viewModel.searchTasksByCategoryId(category.id)
}
}
else{
category_title.text = viewModel.getPomoCategoryName()
viewModel.searchTasksByCategoryId(viewModel.getCategoryId())
Log.i("CategoryFrag-CatName", viewModel.getPomoCategoryName().toString())
Log.i("CategoryFrag-CatId", viewModel.getCategoryId().toString())
}
}
private fun observerSetup() {
viewModel.getSearchTasksByCategoryIdResults().observe(this,androidx.lifecycle.Observer { tasks ->
if(tasks.isNotEmpty()){
adapter?.setTasksList(tasks.sortedBy { task -> task.name?.toLowerCase() })
task_not_found_bubble.visibility = View.GONE
task_not_found_text.visibility = View.GONE
}
else{
task_not_found_bubble.visibility = View.VISIBLE
task_not_found_text.visibility = View.VISIBLE
}
})
PomoPlayObservablesSingleton.fromCategoriesFragment.subscribe {value -> fromCategoriesFragment = value}
}
private fun recyclerSetup() {
adapter = context?.let { TasksRecyclerAdapter(it) }
tasks_list?.layoutManager = LinearLayoutManager(context)
tasks_list?.adapter = adapter
}
override fun onQueryTextSubmit(query: String?): Boolean {
Log.i("Lifecycle-CatFragment", "onQueryTextSubmit() called")
var q = query?.toLowerCase()?.trim()?.replace("\\s+".toRegex(), " ")
setLastSearchQuery(q.toString())
viewModel.searchTasksByName(viewModel.getLastSearchQuery().toString())
return false
}
private fun setLastSearchQuery(lastSearchQuery: String) {
viewModel.setLastSearchQuery(lastSearchQuery)
}
}
CategoryTasksViewModel
class CategoryTasksViewModel(application: Application, state: SavedStateHandle) : AndroidViewModel(application) {
private val repository: PomoPlayRepository = PomoPlayRepository(application)
private val allCategories: LiveData<List<Category>>?
private val allPomoTasks: LiveData<List<PomoTask>>?
private val searchCategoriesByNameResults: MutableLiveData<List<Category>>
private val searchCategoryByIdResults: MutableLiveData<Category>
private val searchTasksByIdResults: MutableLiveData<PomoTask>
private val searchTasksByNameResults: MutableLiveData<List<PomoTask>>
private val searchTasksByCategoryIdResults: MutableLiveData<List<PomoTask>>
private val savedStateHandle = state
companion object{
private const val LAST_SEARCH_QUERY = "lastSearchQuery"
}
init {
allCategories = repository.allCategories
allPomoTasks = repository.allPomoTasks
searchTasksByIdResults = repository.searchTasksByIdResults
searchTasksByNameResults = repository.searchTasksByNameResults
searchTasksByCategoryIdResults = repository.searchTasksByCategoryIdResults
searchCategoryByIdResults = repository.searchCategoriesByIdResults
searchCategoriesByNameResults = repository.searchCategoriesByNameResults
}
fun setLastSearchQuery(lastSearchName: String){
savedStateHandle.set(LAST_SEARCH_QUERY, lastSearchName)
}
fun getLastSearchQuery(): String?{
return savedStateHandle.get<String>(LAST_SEARCH_QUERY)
}
fun setPomoCategoryName(name: String?){
savedStateHandle.set("categoryName", name)
}
fun getPomoCategoryName(): String?{
return savedStateHandle.get<String>("categoryName")
}
fun setCategoryId(id: Int){
savedStateHandle.set("categoryId", id)
}
fun getCategoryId(): Int?{
return savedStateHandle.get<Int>("categoryId")
}
fun insertTask(pomoTask: PomoTask?) {
repository.insertTask(pomoTask)
}
fun deleteTask(pomoTask: PomoTask) {
repository.deleteTask(pomoTask)
}
fun updateTask(pomoTask: PomoTask) {
repository.updateTask(pomoTask)
}
fun searchTasksByName(name: String) {
repository.searchTasksByName(name)
}
fun searchTasksById(pomoTaskId: Int){
repository.searchTasksById(pomoTaskId)
}
fun searchTasksByCategoryId(categoryId: Int?){
repository.searchTasksByCategoryId(categoryId)
}
fun getAllPomoTasks() : LiveData<List<PomoTask>>? {
return allPomoTasks
}
fun getSearchTasksbyNameResults() : MutableLiveData<List<PomoTask>> {
return searchTasksByNameResults
}
fun getSearchTasksByIdResults() : MutableLiveData<PomoTask> {
return searchTasksByIdResults
}
fun getSearchTasksByCategoryIdResults() : MutableLiveData<List<PomoTask>> {
return searchTasksByCategoryIdResults
}
}
SavedStateHandle was not designed to do, what you expect it to do: It ...
... is a key-value map that will let you write and retrieve objects
to and from the saved state. These values will persist after the
process is killed by the system and remain available via the same
object.
Killed by the system, not if the user closes the app willfully or even destroys ("navigates away permanently") the Fragment/Activity acting as its scope. See the docs on Saving UI State - User-initiated UI state dismissal:
The user's assumption in these complete dismissal cases is that they
have permanently navigated away from the activity, and if they re-open
the activity they expect the activity to start from a clean state. The
underlying system behavior for these dismissal scenarios matches the
user expectation - the activity instance will get destroyed and
removed from memory, along with any state stored in it and any saved
instance state record associated with the activity.
Maybe save the information you expect to survive your scenario in SharedPreferences.
In my activity I am setting ViewPager,TabLayout and adding two fragment instance in a list. Then i am passing that list to ViewPagerAdapter .Responsibility of fragment is to fetch data from api call and show it in a list .
I am taking two instance of fragment because api returns two list of data that need to be show in tab fashion(One list in one tab and one in another). But when viewpager adapter returns fragment , if one data list is empty then I am getting empty screen in Tab-0 .
How to dynamically detect data size (here confused , because need to call fragment) and populate tab based on that.
ActivityOne.kt
class ActivityOne : BaseActivity() {
lateinit var item: ArrayList<HistoryTabItem>
lateinit var tabLayout: Tabs
val InfoViewpagerAdapter:InfoVIewPagerAdapter by lazy { InfoVIewPagerAdapter(supportFragmentManager, ArrayList()) }
fun newInstance(context: Context): Intent {
return Intent(context, InfoFragment::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.info_tabview)
getFragments()
InfoViewpagerAdapter.arrayList = item
tabLayout = findViewById(R.id.tabsnfo_type)
val viewPager = findViewById<ViewPager>(R.id.view_pager_info_type)
viewPager.adapter = InfoViewpagerAdapter
tabLayout.setupWithViewPager(viewPager)
}
fun getFragments() {
item = ArrayList()
val HistoryTabItemSeller = HistoryTabItem()
HistoryTabItemSeller.fragment = InfoFragment.createInstance()
item.add(HistoryTabItemSeller)
val HistoryTabItemBuyer = HistoryTabItem()
HistoryTabItemBuyer.fragment = InfoFragment.createInstance()
item.add(HistoryTabItemBuyer)
}
}
InfoViewPageradapter
class InfoVIewPagerAdapter(fm: FragmentManager, var arrayList: ArrayList<HistoryTabItem>) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return arrayList[position].fragment
}
override fun getCount(): Int {
return arrayList.size
}
}
Fragment
class InfoFragment : BaseDaggerFragment(), InfoContract.View {
var isTickerShow: Boolean? = false
var tickerMessage: String? = null
lateinit var allTransactionList: ArrayList<Any>
#Inject
lateinit var infoPresenter: HoldInfoPresenter
val infoAdapter: InfoAdapter by lazy {
InfoAdapter(ArrayList()) }
lateinit var fakelist: ArrayList<Any>
companion object {
fun createInstance(): Fragment {
return InfoFragment()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_container_info, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
infoPresenter.attachView(this)
infoPresenter.getInfo()
}
fun initView() {
rv_container.layoutManager = LinearLayoutManager(context)
rv_container.adapter = infoAdapter
}
override fun renderInfo(depositHistory: DepositHistory?) {
var resultList = ArrayList<Any>()
depositHistory?.let {
resultList = combinedTransactionList(it.sellerData as ArrayList<SellerDataItem>, it.buyerData as ArrayList<BuyerDataItem>)
isTickerShow = it.tickerMessageIsshow
tickerMessage = it.tickerMessageId
}
infoAdapter.list.clear()
infoAdapter.list.addAll(resultList)
infoAdapter.notifyDataSetChanged()
}
fun combinedTransactionList(arrayList: ArrayList<SellerDataItem>, arrayList1: ArrayList<BuyerDataItem>): ArrayList<Any> {
allTransactionList = ArrayList()
allTransactionList.clear()
allTransactionList.addAll(arrayList)
allTransactionList.addAll(arrayList1)
return allTransactionList
}
}
The best option is to fetch data in the activity and then show that tab layout with 2 tabs or just one tab. You would use it with a shared view model, but I see you don't use view models here.
You can also just set the list in createInstance() method in Fragment when it isn't empty.
The third option is to fetch data in Fragment and then send information to activity that the list is empty and hide the specific tab.
This question has been asked a lot but in each solution i can't find a good way to implement it.
Basically i have my FragmentPagerAdaper
internal class FragmentPagerAdapter(fm: androidx.fragment.app.FragmentManager, private val mNumbOfTabs: Int) : androidx.fragment.app.FragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when (position) {
HOME -> HomeFragment()
SHOPPING_CART -> ShoppingCartFragment()
/*COLLECTION -> CollectionFragment()
USER_SETTINGS -> UserSettingsFragment()*/
else -> HomeFragment()
}
}
override fun getCount(): Int {
return mNumbOfTabs
}
companion object {
private const val HOME = 0
private const val SHOPPING_CART = 1
private val COLLECTION = 1
private val USER_SETTINGS = 2
}
}
and the ShoppingCartFragment
class ShoppingCartFragment : Fragment() {
private var inputFragmentView: View? = null
var products: ArrayList<Any> = ArrayList()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val frameLayout = FrameLayout(this.context!!)
populateViewForOrientation(inflater,frameLayout)
return frameLayout
}
private fun RecyclerAnimator(recyclerView: RecyclerView, adapter: ProductCartViewAdapter) {
val itemAnimator = DefaultItemAnimator()
itemAnimator.addDuration = 1000
itemAnimator.removeDuration = 1000
recyclerView.itemAnimator = itemAnimator
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(context)
val divider = requireContext().resources.getDimensionPixelSize(R.dimen.divider_size_default)
recyclerView.addItemDecoration(DividerItemDecorator(divider, SPACE_BOTTOM))//SPACE_LEFT or SPACE_TOP or SPACE_RIGHT or SPACE_BOTTOM concat
}
fun checkList(){
products.clear()
products = database.retrieveShoppingCartList()
val emptyImage = inputFragmentView!!.findViewById<ImageView>(R.id.emptyList)
val recyclerList = inputFragmentView!!.findViewById<RecyclerView>(R.id.shopping_rv)
if (products.isEmpty() || products.size == 0){
recyclerList.visibility = GONE
recyclerList.removeAllViews()
emptyImage.visibility = VISIBLE
} else{
recyclerList.visibility = VISIBLE
emptyImage.visibility = GONE
val adapter = ProductCartViewAdapter(products, context!!, this)
RecyclerAnimator(recyclerList, adapter)
}
}
private fun populateViewForOrientation(inflater: LayoutInflater, viewGroup: ViewGroup) {
viewGroup.removeAllViewsInLayout()
inputFragmentView = inflater.inflate(R.layout.fragment_shopping_cart, viewGroup)
checkList()
}
}
until here everything goes well. This is how the app looks.
But when i reselect that Tab that contains the ShoppingCartFragment i need to refresh the list. to be more specific call the function FragmentPagerAdapter.checkList().
But each time that i try to call that function from the fragment i keep receiving a NullPointer due to the fragment context that cannot be found...
in this:
products = database.retrieveShoppingCartList()
and this is how i handle those context using a synchronizer in the getInstance
SQLiteHandler
private var instance: SQLiteHandler? = null
#Synchronized
fun getInstance(ctx: Context): SQLiteHandler {
if (instance == null) {
instance = SQLiteHandler(ctx.applicationContext)
}
return instance!!
}
// Access property for Context
val Context.database: SQLiteHandler
get() = SQLiteHandler.getInstance(applicationContext)
val androidx.fragment.app.Fragment.database: SQLiteHandler
get() = SQLiteHandler.getInstance(activity!!.applicationContext)
Or any way to recreate the fragment of the viewpager when the tab is reselected