how to send a Cursor instance from one fragment to a fragment which contains a recyclerview with same activity in kotlin - kotlin

I am still doing my first steps with app development, so i would appreciate any kind of feedback for my code.
To my problem. I got two Fragments, "ListFragment.kt" and "SetListFilters.kt". My intention is to create a cursor instance in SetListFilters and then when I press a "apply"-button i perform a popBackStack while sending the cursor instance to ListFrament. To my understanding ListFragment will call OnCreateView() and there i want to pass the new cursor to my adapter so that the Recyclerview get filtered. I tried so many things but nothing worked for me. I hope you can help me. Here is my Code:
class SetListFilters(var transactionDB: SQLiteDatabase, var adapter: TransactionAdapter):Fragment(R.layout.fragment_set_list_filters), DataPassListener {
private lateinit var gridView : GridView
private lateinit var btnYearFilter : Button
private lateinit var btnMonthFilter : Button
private lateinit var btnWeekFilter : Button
private lateinit var btnRevenueFilter : Button
private lateinit var btnExpeneseFilter : Button
private lateinit var btnNeutralFilter : Button
private lateinit var btnSbmtFilter : Button
private lateinit var tvShowAmountFilter : TextView
private lateinit var sbAmountFilters : SeekBar
private val viewModel: ListViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_set_list_filters, container, false)
setUpViews(view)
setUpButtonOnclick()
setUpSeekbar()
val arrayAdapter: ArrayAdapter<CategoryEnum> = ArrayAdapter<CategoryEnum>(activity!!, android.R.layout.simple_list_item_multiple_choice, CategoryEnum.values())
gridView.setAdapter(arrayAdapter)
// When the user clicks on the GridItem
/*gridView.setOnItemClickListener(OnItemClickListener { a, v, position, id ->
val o: Any = gridView.getItemAtPosition(position)
})*/
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.filters.observe(viewLifecycleOwner, Observer { set ->
// Update the selected filters UI
}
}
fun onFilterSelected(filter: Filter) = viewModel.addFilter(filter)
fun onFilterDeselected(filter: Filter) = viewModel.removeFilter(filter)
private fun upDateQueryForDB(){
}
private fun getAllItemsNeu(): Cursor {
val selArgs = arrayOf("r")
return transactionDB!!.query(
TransactionList.TransactionEntry.TABLE_NAME,
null,
"type = ?",
selArgs,
null,
null,
TransactionList.TransactionEntry.COLUMN_CREATEDAT + " DESC")
}
private fun setUpViews(view: View){
gridView = view.findViewById(R.id.gvCategoryFilter )
btnExpeneseFilter = view.findViewById(R.id.btnExpenseFilter)
btnMonthFilter = view.findViewById(R.id.btnMonthFilter)
btnWeekFilter = view.findViewById(R.id.btnWeekFilter)
btnNeutralFilter = view.findViewById(R.id.btnNeutralFilter)
btnRevenueFilter = view.findViewById(R.id.btnRevenueFilter)
btnSbmtFilter = view.findViewById(R.id.btnSbmtFilter)
btnYearFilter = view.findViewById(R.id.btnYearFilter)
sbAmountFilters = view.findViewById(R.id.seekBar)
tvShowAmountFilter = view.findViewById(R.id.tvFilterAmount)
}
private fun setUpButtonOnclick(){
btnExpeneseFilter.setOnClickListener { changeButtonAppearance(btnExpeneseFilter) }
btnMonthFilter.setOnClickListener { changeButtonAppearance(btnMonthFilter) }
btnWeekFilter.setOnClickListener { changeButtonAppearance(btnWeekFilter) }
btnNeutralFilter.setOnClickListener { changeButtonAppearance(btnNeutralFilter) }
btnRevenueFilter.setOnClickListener { changeButtonAppearance(btnRevenueFilter) }
btnYearFilter.setOnClickListener { changeButtonAppearance(btnYearFilter) }
btnSbmtFilter.setOnClickListener{
mainActivity.passDataCom(getAllItemsNeu())
activity!!.supportFragmentManager.popBackStack()
}
}
private fun changeButtonAppearance(button:Button) {
if(button.tag == "notPressed"){
button.setBackgroundResource(R.drawable.rounded_select_button_pressed)
button.setTextColor(Color.WHITE)
button.tag = "pressed"
}else{
button.setBackgroundResource(R.drawable.rounded_select_button)
button.setTextColor(ContextCompat.getColor(context!!,R.color.mainBlue))
button.tag ="notPressed"
}
}
private fun setUpSeekbar(){
sbAmountFilters.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
// Display the current progress of SeekBar
tvShowAmountFilter.text = "< $i"
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
// Do something
//Toast.makeText(context!!,"start tracking",Toast.LENGTH_SHORT).show()
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// Do something
//Toast.makeText(context!!,"stop tracking",Toast.LENGTH_SHORT).show()
if(tvShowAmountFilter.text.toString()=="< 0"){
tvShowAmountFilter.text = "none"
}
}
})
}
override fun onAttach(context: Context) {
super.onAttach(context)
try{
mCallback = (OnImageClickListener) //folled the tutorial, dont know what to replace it with
}
}
}
class ListFragment:Fragment(R.layout.fragment_list) {
lateinit var transactionDB : SQLiteDatabase
lateinit var adapter : TransactionAdapter
private lateinit var filterBtn : ImageButton
private lateinit var flActBtn : FloatingActionButton
var mCursorFilter : Cursor? = null
private val viewModel: ListViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val dbHelper = TransactionDBHelper(activity)
transactionDB = dbHelper.writableDatabase
adapter = TransactionAdapter(activity!!, getAllItems())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.filteredList.observe(viewLifecycleOwner, Observer { list ->
//TODO
}
}
private fun addRandomTransactionItem() {
var a = 1234.56
for (i in 0..10) {
if(i%2==0){
transactionDB.execSQL("INSERT INTO transactionList (type, amount, category, createdAt) VALUES('r', ${a}, 'K', CURRENT_TIMESTAMP)")
adapter.swapCursor(getAllItems())
}else if(i%5==0){
transactionDB.execSQL("INSERT INTO transactionList (type, amount, category, createdAt) VALUES('ne', ${a}, 'K', CURRENT_TIMESTAMP)")
adapter.swapCursor(getAllItems())
}else if(i%6==0){
transactionDB.execSQL("INSERT INTO transactionList (type, amount, category, createdAt) VALUES('ne', ${a}, 'K', CURRENT_TIMESTAMP)")
adapter.swapCursor(getAllItems())
}else{
transactionDB.execSQL("INSERT INTO transactionList (type, amount, category, createdAt) VALUES('e', ${a}, 'K', CURRENT_TIMESTAMP)")
adapter.swapCursor(getAllItems())
}
}
}
private fun getAllItems(): Cursor {
return transactionDB!!.query(
TransactionList.TransactionEntry.TABLE_NAME,
null,
null,
null,
null,
null,
TransactionList.TransactionEntry.COLUMN_CREATEDAT + " DESC")
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_list, container, false)
val rvRecyclerView = view.findViewById<RecyclerView>(R.id.rvRecyclerView)
rvRecyclerView.layoutManager = LinearLayoutManager(activity)
if(mCursorFilter!=null){
adapter.swapCursor(mCursorFilter!!)
}
rvRecyclerView.adapter = this.adapter
rvRecyclerView.addItemDecoration(MarginItemDecoration(10))
flActBtn = view.findViewById(R.id.floatingActionButton)
flActBtn.setOnClickListener {
var ft:FragmentTransaction = activity!!.supportFragmentManager.beginTransaction()
ft.replace(R.id.flFrameLayout, AddItemFragment(transactionDB, adapter))
ft.addToBackStack(null)
ft.commit()
}
filterBtn=view.findViewById(R.id.ibFilterBtn)
filterBtn.setOnClickListener {
var ft:FragmentTransaction = activity!!.supportFragmentManager.beginTransaction()
ft.replace(R.id.flFrameLayout, SetListFilters(transactionDB, adapter))
ft.addToBackStack(null)
ft.commit()
}
addRandomTransactionItem()
return view
}
}
class MainActivity() : AppCompatActivity() {
var mCursorFilters: Cursor? = null
private val viewModel: ItemViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.selectedItem.observe(this, Observer { item ->
// TODO
})
val homeFragment = HomeFragment()
val listFragment = ListFragment()
val profileFragment = ProfileFragment()
setCurrentFragment(homeFragment)
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
bottomNavigationView.setOnNavigationItemSelectedListener {
var ft: FragmentTransaction = this.supportFragmentManager.beginTransaction()
when (it.itemId) {
R.id.pageHome -> ft.replace(R.id.flFrameLayout, homeFragment) // setCurrentFragment(homeFragment)
R.id.pageList -> ft.replace(R.id.flFrameLayout, listFragment) //setCurrentFragment(listFragment)
R.id.pageProfile -> ft.replace(R.id.flFrameLayout, profileFragment) //setCurrentFragment(profileFragment)
}
// ft.replace(R.id.flFrameLayout, AddItemFragment())
ft.addToBackStack(null)
ft.commit()
true
}
}
private fun setCurrentFragment(fragment: Fragment){
supportFragmentManager.beginTransaction().apply {
replace(R.id.flFrameLayout, fragment)
commit()
}
}
}

Related

Synthetics to Viewbinding issues (onBackPress & Navigation Drawer)

I am updating my app to completely remove synthetics and move to viewbinding, however I am struggling with converting the onBackPressed method (now also deprecated) with my Activity, Fragment and Navigation Drawer.
I originally had it like this:
Activity (Before)
override fun onCreate(savedInstanceState: Bundle?) {
Log.d(TAG, "onCreate")
super.onCreate(savedInstanceState)
val activityBinding: BusinessActivityBinding = DataBindingUtil.setContentView(
this,
R.layout.business_activity
)
nav_view.setNavigationItemSelectedListener(this)
val headerView = activityBinding.navView.getHeaderView(0)
headerView.navHeaderEmailText.setOnClickListener { sendSupportEmail() }
navController = findNavController(this, R.id.NavHostFragment)
// other stuff
}
fun openCloseNavigationDrawer() {
if (business_drawer_layout.isDrawerOpen(GravityCompat.START)) {
business_drawer_layout.closeDrawer(GravityCompat.START)
} else {
business_drawer_layout.openDrawer(GravityCompat.START)
}
}
override fun onBackPressed() {
val navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(R.id.NavHostFragment) as NavHostFragment
val backStackEntryCount = navHostFragment?.childFragmentManager?.backStackEntryCount
when {
(business_drawer_layout.isDrawerOpen(GravityCompat.START)) -> business_drawer_layout.closeDrawer(
GravityCompat.START
)
(backStackEntryCount == 0) -> dialogLeaveApp()
else -> super.onBackPressed()
}
}
private fun dialogLeaveApp(){
MaterialDialog(this).show {
title(R.string.business_activity_dialog_leave_app_heading)
message(R.string.business_activity_dialog_leave_app_message)
positiveButton(R.string.business_activity_dialog_confirm_button){
super.onBackPressed()
}
negativeButton(R.string.business_activity_dialog_cancel_button) {
dismiss()
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.nav_sites_fragment -> navigateWithClearStack(R.id.sitesFragment)
R.id.nav_projects_fragment -> navigateWithClearStack(R.id.projectsFragment)
R.id.nav_contacts_fragment -> navigateWithClearStack(R.id.contactsFragment)
R.id.nav_tasks_fragment -> navigateWithClearStack(R.id.tasksFragment)
R.id.nav_profile_fragment -> makeToast("Todo: Profile Fragment")
R.id.nav_settings_fragment -> navigateWithClearStack(R.id.settingsFragment)
}
business_drawer_layout.closeDrawer(GravityCompat.START)
return true
}
private fun navigateWithClearStack(destination: Int) {
businessViewModel.clearCurrentVMData()
val navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(R.id.NavHostFragment) as NavHostFragment
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.business_navigation)
graph.startDestination = destination
navController.graph = graph
}
}
Fragment (already using viewbinding)
class SitesFragment : Fragment() {
private lateinit var navController: NavController
private lateinit var businessViewModel: BusinessViewModel
private lateinit var sitesAdapter: SitesRecyclerViewAdapter
private var _binding: SitesFragmentBinding? = null // this property is only valid between onCreateView & onDestroyView
private val binding get() = _binding!! //https://stackoverflow.com/questions/57647751/android-databinding-is-leaking-memory
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
Log.d(TAG, "onCreateView()")
activity?.let {
businessViewModel = ViewModelProvider(it).get(BusinessViewModel::class.java)
Log.d(TAG, "businessViewModel = $businessViewModel")
}
Log.d(TAG, businessViewModel.currentStatus("${TAG}, onCreateView()"))
// Inflate the layout for this fragment
_binding = SitesFragmentBinding.inflate(inflater, container, false)
val view = binding.root
binding.apply {
sitesDrawerMenuButton.setOnClickListener {
(activity as BusinessActivity).openCloseNavigationDrawer()
Log.d(TAG, "sitesDrawerMenuButton clicked, openCloseNavigationDrawer()")
}
// Other listeners
}
return view
}
Activity (After)
private var _binding: BusinessActivityBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
Log.d(TAG, "onCreate")
super.onCreate(savedInstanceState)
// Inflate the layout for this activity
_binding = BusinessActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.NavHostFragment) as NavHostFragment
this.navController = navHostFragment.navController
binding.navView.setupWithNavController(this.navController)
// Setup OnBackPressedDispatcher
val backPressedCallback = object:OnBackPressedCallback(false){
override fun handleOnBackPressed() {
onBackButtonPressed()
}
}
// Other stuff
}
fun openCloseNavigationDrawer() { // accessed by Fragment
if (binding.businessDrawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.businessDrawerLayout.closeDrawer(GravityCompat.START)
} else {
binding.businessDrawerLayout.openDrawer(GravityCompat.START)
}
}
fun onBackButtonPressed() {
val navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(R.id.NavHostFragment) as NavHostFragment
val backStackEntryCount = navHostFragment.childFragmentManager.backStackEntryCount
when {
(binding.businessDrawerLayout.isDrawerOpen(GravityCompat.START)) -> binding.businessDrawerLayout.closeDrawer(
GravityCompat.START
)
(backStackEntryCount == 0) -> dialogLeaveApp()
}
}
private fun dialogLeaveApp(){
// as before
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// as before
}
private fun navigateWithClearStack(destination: Int) {
// as before
}
So the issue is that when you back press, it simply exits the app, rather than triggering my function onBackButtonPressed() (which replaces the depricated onBackPressed())and variable 'backPressedCallback' is never used..

Obtain a crash message-App keeps stopping

I get the following trace error java.lang.ClassCastException: com.example.myhouse.MainActivity cannot be cast to com.brsthegck.kanbanboard.TasklistFragment$Callbacks at com.brsthegck.kanbanboard.TasklistFragment.onAttach(TasklistFragment.kt:52)at androidx.fragment.app.Fragment.performAttach(Fragment.java:2672) at androidx.fragment.app.FragmentStateManager.attach(FragmentStateManager.java:263) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1170) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
enter image description here
Tasklist Fragment
private const val ARG_TASKLIST_TYPE = "tasklist_type"
private const val TASKLIST_TYPE_TODO = 0
private const val TASKLIST_TYPE_DOING = 1
private const val TASKLIST_TYPE_DONE = 2
class TasklistFragment : Fragment(){
private lateinit var visibleColorPaletteViewList : List<View>
lateinit var taskRecyclerView: RecyclerView
private var tasklistType : Int = -1
private var adapter : TaskViewAdapter? = TaskViewAdapter(LinkedList<Task>())
private var colorPaletteIsVisible : Boolean = false
private var callbacks: Callbacks? = null
//Callback interface to delegate access functions in MainActivity
interface Callbacks{
fun addTaskToViewModel(task: Task, destinationTasklistType: Int)
fun deleteTaskFromViewModel(tasklistType: Int, adapterPosition: Int)
fun getTaskListFromViewModel(tasklistType: Int) : LinkedList<Task>
}
//Attach context as a Callbacks reference to the callbacks variable when fragment attaches to container
override fun onAttach(context: Context) {
super.onAttach(context)
callbacks = activity as Callbacks? // error is here
}
//Detach context (assign to null) when fragment detaches from container
override fun onDetach() {
super.onDetach()
callbacks = null
}
//ItemTouchHelper instance with custom callback, to move task card view positions on hold
private val itemTouchHelper by lazy{
val taskItemTouchCallback = object : ItemTouchHelper.SimpleCallback(UP or DOWN, 0){
override fun onMove(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder): Boolean {
val adapter = recyclerView.adapter as TaskViewAdapter
val from = viewHolder.adapterPosition
val to = target.adapterPosition
adapter.moveTaskView(from, to)
adapter.notifyItemMoved(from, to)
return true
}
//Make taskview transparent while being moved
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if(actionState == ACTION_STATE_DRAG)
viewHolder?.itemView?.alpha = 0.7f
}
//Make taskview opaque while being
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = 1.0f
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { /* Not implemented on purpose. */ }
}
ItemTouchHelper(taskItemTouchCallback)
}
//Get the fragment arguments, tasklist type to be precise, and assign it to the member of this fragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tasklistType = arguments?.getInt(ARG_TASKLIST_TYPE) as Int
}
//Inflate view of fragment, prepare recycler view and it's layout manager, and update the UI
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?{
//Inflate the layout of this fragment, get recyclerview ref, set layout manager for recycler view
val view = inflater.inflate(R.layout.tasklist_fragment_layout, container,false)
taskRecyclerView = view.findViewById(R.id.task_recycler_view) as RecyclerView
taskRecyclerView.layoutManager = LinearLayoutManager(context)
itemTouchHelper.attachToRecyclerView(taskRecyclerView)
//Fill the recyclerview with data from viewmodel
updateInterface()
//Return the created view
return view
}
//Populate recyclerview and set up its adapter
private fun updateInterface(){
val tasks = callbacks!!.getTaskListFromViewModel(tasklistType)
adapter = TaskViewAdapter(tasks)
taskRecyclerView.adapter = adapter
}
Kanban class
class Kanban : Fragment(), TasklistFragment.Callbacks{
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
private lateinit var viewPager: ViewPager2
private lateinit var tabLayout: TabLayout
private lateinit var taskViewModel: TaskViewModel
//When add action bar button is pressed
override fun onOptionsItemSelected(item: MenuItem) = when(item.itemId) {
R.id.action_new_task -> {
val currentTasklistType = viewPager.currentItem
val currentTasklist = when (currentTasklistType) {
TASKLIST_TYPE_TODO -> taskViewModel.todoTaskList
TASKLIST_TYPE_DOING -> taskViewModel.doingTaskList
TASKLIST_TYPE_DONE -> taskViewModel.doneTaskList
else -> throw Exception("Unrecognized tasklist type")
}
val currentFragment = (activity?.supportFragmentManager?.fragments?.get(currentTasklistType) as TasklistFragment)
addTaskToViewModel(Task(), currentTasklistType)
currentFragment.taskRecyclerView.scrollToPosition(currentTasklist.size - 1)
true
}
else -> super.onOptionsItemSelected(item)
}
//Inflate the action bar menu resource on options menu creation
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_action_bar, menu)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
//Get viewmodel
val view: View = inflater.inflate(R.layout.fragment_kanban2, container, false)
taskViewModel = ViewModelProvider(this).get(TaskViewModel::class.java)
//Read tasklist data from shared prefs and populate viewmodel lists with it
readSharedPrefsToViewModel()
//Get ref to viewpager and set up its adapter & attributes
viewPager = view.findViewById(R.id.view_pager)
val viewPagerAdapter = TasklistFragmentStateAdapter(this)
viewPager.apply {
adapter = viewPagerAdapter
offscreenPageLimit = 2
//Get ref to tablayout, set up & attach its mediator
tabLayout = view.findViewById(R.id.tab_layout)
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.tab_label_todo)
1 -> getString(R.string.tab_label_doing)
else -> getString(R.string.tab_label_done)
}
}.attach()
}
return view
}
override fun onStop() {
super.onStop()
writeViewModelToSharedPrefs()
}
//Adapter class for view pager
private inner class TasklistFragmentStateAdapter(fa: Kanban) : FragmentStateAdapter(fa){
override fun createFragment(position: Int): Fragment {
//Create argument bundle with task list type
val tasklistFragmentArguments = Bundle().apply{
putInt(ARG_TASKLIST_TYPE, position)
}
//Attach the argument bundle to new fragment instance and return the fragment
return TasklistFragment().apply{
arguments = tasklistFragmentArguments
}
}
override fun getItemCount(): Int = NUM_TASKLIST_PAGES
}
override fun addTaskToViewModel(task: Task, destinationTasklistType: Int) {
val destinationFragment = (activity?.supportFragmentManager?.fragments?.get(destinationTasklistType) as TasklistFragment)
val taskList = getTaskListFromViewModel(destinationTasklistType)
taskList.add(task)
destinationFragment.taskRecyclerView.adapter?.notifyItemInserted(taskList.size)
}
override fun deleteTaskFromViewModel(tasklistType: Int, adapterPosition: Int) {
val tasklistFragment = (activity?.supportFragmentManager?.fragments?.get(tasklistType) as TasklistFragment)
getTaskListFromViewModel(tasklistType).removeAt(adapterPosition)
tasklistFragment.taskRecyclerView.adapter?.notifyItemRemoved(adapterPosition)
}
override fun getTaskListFromViewModel(tasklistType: Int): LinkedList<Task> =
when(tasklistType){
TASKLIST_TYPE_TODO -> taskViewModel.todoTaskList
TASKLIST_TYPE_DOING -> taskViewModel.doingTaskList
TASKLIST_TYPE_DONE -> taskViewModel.doneTaskList
else -> throw Exception("Unrecognized tasklist type") }
private fun writeViewModelToSharedPrefs(){
val gson = Gson()
//Convert list to json string
val todoJSON = gson.toJson(taskViewModel.todoTaskList)
val doingJSON = gson.toJson(taskViewModel.doingTaskList)
val doneJSON = gson.toJson(taskViewModel.doneTaskList)
var sharedPref : SharedPreferences = requireActivity().getPreferences(Context.MODE_PRIVATE);
//Save json strings into shared preferences
activity?.getPreferences(MODE_PRIVATE)?.edit()?.apply {
putString(KEY_TODO_JSON, todoJSON)
putString(KEY_DOING_JSON, doingJSON)
putString(KEY_DONE_JSON, doneJSON)
}?.apply()
}
private fun readSharedPrefsToViewModel(){
val gson = Gson()
val sharedPrefs = activity?.getPreferences(MODE_PRIVATE)
val todoJSON = sharedPrefs?.getString(KEY_TODO_JSON, "[]")
val doingJSON = sharedPrefs?.getString(KEY_DOING_JSON, "[]")
val doneJSON = sharedPrefs?.getString(KEY_DONE_JSON, "[]")
val type = object: TypeToken<LinkedList<Task>>() {}.type //Gson requires type ref for generic types
taskViewModel.todoTaskList = gson.fromJson(todoJSON, type)
taskViewModel.doingTaskList = gson.fromJson(doingJSON, type)
taskViewModel.doneTaskList = gson.fromJson(doneJSON, type)
}
Main activity
public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private ViewPager2 viewPager2;
private MyFragment adapter;
private TextView register;
GridView contractorsGV;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tabLayout = findViewById(R.id.tabLayout);
viewPager2 = findViewById(R.id.viewPager2);
tabLayout.addTab(tabLayout.newTab().setText("Home"));
tabLayout.addTab(tabLayout.newTab().setText("Idea Book"));
tabLayout.addTab(tabLayout.newTab().setText("My Profile"));
FragmentManager fragmentManager = getSupportFragmentManager();
adapter = new MyFragment(fragmentManager , getLifecycle());
viewPager2.setAdapter(adapter);
4. Fragment manager
public class MyFragment extends FragmentStateAdapter {
public MyFragment(#NonNull FragmentManager fm,Lifecycle lifecycle) {
super(fm,lifecycle);
}
#NonNull
#Override
public Fragment createFragment(int position) {
if(position==1)
{
return new Kanban();
}
if(position==2) {
return new signOut();
}
return new Navigation_Bar();
}

i want to add next page (every page is 10) to my recyclerview but it delet my last page

my recyclerview fragment:
this is the fragment that I set my recycler view , I want when touch the continue button (btn_continue) add the rest of the list, which is actually the next page, to my list when I click the button
But it deletes the previous list and displays the new list
and i use the kotlin coruotine, viewmodel and recyclerview
class AllUnpublishedAdsFragment : Fragment() {
lateinit var unpublishedAdsAdapter: UnpublishedAdsAdapter
var items = ArrayList<M_Ads>()
lateinit var loadMoreItems: ArrayList<M_Ads>
lateinit var scrollListener: RecyclerViewLoadMoreScroll
lateinit var mLayoutManager: RecyclerView.LayoutManager
val page = "1"
var newpage = page.toInt()
var navController: NavController? = null
lateinit var btn_continue: Button
private lateinit var viewModel: MainViewmodels
lateinit var token: String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_all_unpublished_ads, container,
false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_continue = view.findViewById<Button>(R.id.btn_continueAds)
token = arguments?.getString(TOKEN_KEY).toString()
Log.i("TOKEN", token)
setupViewModel(token, page)
continueAds()
unpublishedAdsAdapter.itemClickListener = { item, position ->
val id = item.id
val bundle_id = Bundle()
bundle_id.putString(ID_KEY, id.toString())
navController = Navigation.findNavController(view)
//view.findNavController().navigate(R.id.action_UnpublishedAds_to_Detail,
bundle_id)
navController!!.navigate(
R.id.action_allUnpublishedAdsFragment_to_adsDetailFragment,
bundle_id
)
}
}
private fun setupViewModel(token: String, page: String) {
LoadingDialog.show(requireContext(), { ld ->
recy_unpublished_ads.layoutManager = LinearLayoutManager(activity)
val decoration = DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)
recy_unpublished_ads.addItemDecoration(decoration)
viewModel = ViewModelProvider(this).get(MainViewmodels::class.java)
unpublishedAdsAdapter = UnpublishedAdsAdapter()
recy_unpublished_ads.adapter = unpublishedAdsAdapter
viewModel.adsListObserve().observe({ lifecycle }, { model ->
items.clear()
items.addAll(model.adsList!!)
unpublishedAdsAdapter.setUpdateData(items)
ld.dismiss()
btn_continue.visibility = View.VISIBLE
})
viewModel.getAdsList(token, page)
})
}
fun continueAds() {
var isTouched=false
btn_continue.setOnClickListener {
isTouched=true
newpage = newpage + 1
Log.i("CONTINUE", newpage.toString())
setupViewModel(token, newpage.toString())
unpublishedAdsAdapter.notifyDataSetChanged()
isTouched=false
}
}
RetrofitInstance class
class RetrofitInstance {
companion object{
fun getRetrofitInstance (): Retrofit{
// val interceptor = HttpLoggingInterceptor()
// interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
// val client: OkHttpClient = OkHttpClient.Builder().addInterceptor(interceptor).build()
val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.build()
return Retrofit.Builder()
.baseUrl(BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
}
}
}
my adapter:
class UnpublishedAdsAdapter : RecyclerView.Adapter<UnpublishedAdsAdapter.MyViewHolder>() {
var items = ArrayList<M_Ads>()
var itemClickListener: ((item: M_Ads, position: Int) -> Unit)? = null
lateinit var mcontext: Context
class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class LoadingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
fun setUpdateData(items: ArrayList<M_Ads>) {
this.items = items
notifyDataSetChanged()
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val img_ads = view.findViewById<ImageView>(R.id.img_recy_ads)
val txt_title = view.findViewById<TextView>(R.id.txt_recy_ads_title)
val txt_accountType = view.findViewById<TextView>(R.id.txt_recy_ads_acctype)
fun bind(data: M_Ads) {
txt_title.setText(data.title)
txt_accountType.setText(data.accountType)
if (!data.image1.equals("")) {
Picasso.with(img_ads.context).load(data.image1).into(img_ads)
} else {
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
mcontext = parent.context
return if (viewType == Constant.VIEW_TYPE_ITEM) {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.row_recy_unpublished_ads, parent, false)
MyViewHolder(view)
} else {
val view =
LayoutInflater.from(mcontext).inflate(R.layout.progress_loading, parent, false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
view.progressbar.indeterminateDrawable.colorFilter =
BlendModeColorFilter(Color.WHITE, BlendMode.SRC_ATOP)
} else {
view.progressbar.indeterminateDrawable.setColorFilter(
Color.WHITE,
PorterDuff.Mode.MULTIPLY
)
}
MyViewHolder(view)
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(items.get(position))
handleClick(holder)
}
fun handleClick(holder: UnpublishedAdsAdapter.MyViewHolder) {
holder.itemView.setOnClickListener {
val position = holder.adapterPosition
itemClickListener?.invoke(items.get(position), position)
}
}
override fun getItemCount(): Int {
return items.size
}
my Viewmodel:
class MainViewmodels:ViewModel() {
lateinit var getAds:MutableLiveData<Main_ModelAds>
init {
getAds= MutableLiveData<Main_ModelAds>()
}
fun adsListObserve():MutableLiveData<Main_ModelAds>{
return getAds
}
fun getAdsList(token:String,page:String){
viewModelScope.launch(Dispatchers.IO) {
try {
Log.i("ERRORRES",token + "")
val retrofitInstance=RetrofitInstance.getRetrofitInstance().create(RetrofitService::class.java)
val response=retrofitInstance.unPublishedAdsList(token,page)
getAds.postValue(response)
}catch (ex:Exception){
Log.i("ERRORRES", ex.message.toString() + "")
}
}
}
What you want here is to update the list that you are showing when the user clicks the button.
In this case you should use ListAdapter.
You can refer to this article for implementation:
https://developer.android.com/reference/androidx/recyclerview/widget/ListAdapter

RecyclerView in Fragment not populating

So I've been wrestling with this for days and I need some help. I've made this code work in an activity, but then I move it to a fragment it doesn't work. Everything else is the same between the two.
Using the debugger with the working Activity, the line
apiService = retrofit.create<HomeJsonApiService>(HomeJsonApiService::class.java)
goes to getItemCount(). However in the fragment it goes directly to onCreateView in the Fragment. I've attached my code below. Thanks in advance for the help! And be gentle. I'm still new to this :)
First is my fragment:
class TabHomeActivity : Fragment() {
val itemList = ArrayList<HomeCards>()
lateinit var adapter: HomeCardsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding = FragmentTabHomeActivityBinding.inflate(layoutInflater)
adapter = HomeCardsAdapter()
var rv = binding.rvHomeCards
rv.adapter = adapter
loadData()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.cards_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun loadData() {
ApiManager.getInstance().service.listHeroes()
.enqueue(object : Callback<ResponseData<List<HomeCards>>> {
override fun onResponse(
call: Call<ResponseData<List<HomeCards>>>,
response: Response<ResponseData<List<HomeCards>>>
) {
val listData: List<HomeCards> = response.body()!!.data
// updating data from network to adapter
itemList.clear()
itemList.addAll(listData)
adapter.updateData(itemList)
adapter.notifyDataSetChanged()
}
override fun onFailure(call: Call<ResponseData<List<HomeCards>>>, t: Throwable) {
}
})
}
}
The HTTP request:
data class ResponseData<T> (
val code: Int,
val data: T
)
interface HomeJsonApiService {
#GET("marvel-heroes.asp?h=2")
fun listHeroes(): retrofit2.Call<ResponseData<List<HomeCards>>>
}
class ApiManager {
private var apiService: HomeJsonApiService? = null
init {
createService()
}
val service: HomeJsonApiService get() = apiService!!
private fun createService() {
val loggingInterceptor =
HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Log.i("Retrofit", message)
}
})
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build()
val retrofit: Retrofit = Retrofit.Builder()
.client(client)
.baseUrl("https://www.mywebsite.com/jsonfolder/JSON/")
.addConverterFactory(GsonConverterFactory.create())
.build()
apiService = retrofit.create(HomeJsonApiService::class.java)
}
companion object {
private var instance: ApiManager? = null
fun getInstance(): ApiManager {
return instance ?: synchronized(this) {
ApiManager().also { instance = it }
}
}
}
}
And my adapter:
class HomeCardsAdapter() : RecyclerView.Adapter<HomeCardsAdapter.ViewHolder>() {
private lateinit var itemList: List<HomeCards>
lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
context = parent.context
val view = LayoutInflater.from(context).inflate(R.layout.cards_home, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return if (::itemList.isInitialized) itemList.size else 0
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind()
}
fun updateData(list: List<HomeCards>) {
itemList = list;
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
//var binding = ActivityMainBinding(layoutInflater(inf))
fun bind() {
val item = itemList.get(adapterPosition)
ViewHolder(itemView).itemView.findViewById<TextView>(R.id.cardHomeTitle).text = item.name
ViewHolder(itemView).itemView.findViewById<TextView>(R.id.cardHomeTitle).text = item.superheroName
Glide.with(context)
.load(item.photo)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.circleCrop()
.into(ViewHolder(itemView).itemView.findViewById<ImageView>(R.id.cardHomeIcon))
}
}
}
class HomeCards {
#SerializedName("superhero_name")
var superheroName: String = ""
var name: String = ""
var photo: String = ""
}
The main problem is:
var binding = FragmentTabHomeActivityBinding.inflate(layoutInflater)
That is inside on onCreate but onCreateView is returning another view inflater.inflate(R.layout.cards_home, container, false)
So you are applying the adapter to a recycler that is on the binding, but the view on the screen is inflated from the layout. Change it to this:
private lateinit var binding: FragmentTabHomeActivityBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
binding = FragmentTabHomeActivityBinding.inflate(layoutInflater, container, false)
return binding.root
}
And move the code from from onCreate to onViewCreated but make sure to use the lateinit binding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = HomeCardsAdapter()
var rv = binding.rvHomeCards
rv.adapter = adapter
loadData()
}
After that there is a problem in your adapter: private lateinit var itemList: List<HomeCards> very specifically List<HomeCards>. The method notifyDataSetChanged doesn't work by changing or updating the reference of the data structure but when the collection is modified. Change it to this:
private val list = mutableListOf<HomeCards>()
override fun getItemCount(): Int {
return list.size()
}
fun updateData(list: List<HomeCards>) {
this.itemList.clear()
this.itemList.addAll(list)
notifyDataSetChanged()
}
If onResponse() gets called and provides response, verify that code updating UI is running on main/ui thread. Common source of issue when working with network (other threads).
activity?.runOnUiThread {
itemList.clear()
itemList.addAll(listData)
adapter.updateData(itemList)
adapter.notifyDataSetChanged()
}

RecyclerView adapter inside a fragment is not updating when data changes

I have an activity that contains 2 tabs and each tab has a fragment inside ViewPager. Each fragment have a RecyclerView.
When I navigate to another activity the data inside the Fragments should be updated. Although the data is being sent correctly to the fragment, the original data is displayed.
I tried using notifyDataSetChanged() method inside the fragment but it didn't work.
I also tried calling it from the activity like:
if (!pickedItemsList.isNullOrEmpty() && notScannedItemsFragment != null && notScannedItemsFragment.isAdded)
{
notScannedItemsFragment.notScannedItemsAdapter.notifyDataSetChanged()
}
However, it didn't work too.
That's how I am initiating the fragment:
override fun initFragments(savedInstanceState: Bundle?, pickedItemsList: ArrayList<OrderDetail>, remainigItemsList: ArrayList<OrderDetail>) {
val listener: ItemsInteractionListener = object : ItemsInteractionListener {
override fun onSwipeToRefresh() {
presenter.onSwipeToRefresh()
}
}
if (!pickedItemsList.isNullOrEmpty() && notScannedItemsFragment != null && notScannedItemsFragment.isAdded) {
notScannedItemsFragment.notScannedItemsAdapter.notifyDataSetChanged()
scannedItemsFragment = ScannedItemsFragment().newInstance(remainingItemsList)
notScannedItemsFragment = NotScannedItemsFragment().newInstance(pickedItemsList)!!
} else {
scannedItemsFragment = ScannedItemsFragment().newInstance(arrayListOf())
notScannedItemsFragment = NotScannedItemsFragment().newInstance(allItemsList)!!
}
scannedItemsFragment.setListener(listener)
notScannedItemsFragment.setListener(listener)
}
allItemList is the original list and pickedItemsList and remainingItemsList are the lists after the changes (that I got from the other activity)
This is one of the fragments classes:
class NotScannedItemsFragment : BaseFragment() {
private var listener: ItemsInteractionListener? = null
lateinit var notScannedItemsAdapter: OrderItemListingAdapter
private var itemRemainingCount: Int = 0
lateinit var notScannedItems: ArrayList<OrderDetail>
lateinit var recyclerView: RecyclerView
lateinit var fragmentView: View
fun newInstance(notScannedItems: ArrayList<OrderDetail>): NotScannedItemsFragment? {
val notScannedItemsFragment = NotScannedItemsFragment()
val args = Bundle()
val order = Gson().toJson(notScannedItems)
args.putString(IntentConstants.EXTRA_NOT_SCANNED_ITEM_LIST, order)
notScannedItemsFragment.setArguments(args)
return notScannedItemsFragment
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val str = arguments?.getString(IntentConstants.EXTRA_NOT_SCANNED_ITEM_LIST)
notScannedItems = Gson().fromJson(
str,
object : TypeToken<List<OrderDetail?>?>() {}.type
) as ArrayList<OrderDetail>
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentView = inflater.inflate(R.layout.fragment_not_scanned_items, container, false)
recyclerView = fragmentView.notScannedItemListing
return fragmentView
}
fun setListener(listener: ItemsInteractionListener) {
this.listener = listener
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setUpRecycler(view)
super.onViewCreated(view, savedInstanceState)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
}
private fun setUpRecycler(view: View) {
imageLoader = ImageLoader(context)
notScannedItemsAdapter = OrderItemListingAdapter(
false,
imageLoader,
object : ImageClickListener {
override fun onImageClick(
itemName: String,
itemQuantity: Int,
url: String,
barcodes: List<String>?
) {
startImageFullViewActivity(itemName, itemQuantity, url, barcodes)
}
})
notScannedItemsAdapter.addItem(notScannedItems)
notScannedItemsAdapter.notifyDataSetChanged()
view.notScannedItemListing.apply {
view.notScannedItemListing.layoutManager = LinearLayoutManager(context)
view.notScannedItemListing.setHasFixedSize(true)
view.notScannedItemListing.isNestedScrollingEnabled = false
adapter = notScannedItemsAdapter
}
notScannedItemsAdapter.printList()
}
fun showOrderItemListing(notScannedItems: ArrayList<OrderDetail>) {
this.notScannedItems = notScannedItems
itemRemainingCount = notScannedItems.size
}
fun getItemsRemainingCount(): Int{
return notScannedItems.size
}
fun clearItems() {
notScannedItemsAdapter.clearItems()
}
fun updateAdapterContent(pickedItemsList: ArrayList<OrderDetail>) {
if(this::notScannedItemsAdapter.isInitialized ) {
notScannedItemsAdapter.clearItems()
notScannedItemsAdapter.addItem(notScannedItems)
notScannedItemsAdapter.notifyDataSetChanged()
}
}
}
It turns out since I'm getting the list from arguments it's not updating with the new list. As stated here: Anything initialized in onCreate() is preserved if the Fragment is paused and resumed.
So I added a boolean variable loadListFromArgs and I only loaded the list from args if it's true and when I call updateAdapterContent I set it to false.