Reloading fragment with button - Android, Kotlin - android

im trying to have a button that will refresh the current Fragment with default values (i have checkboxes which, after checking all three, gets disabled.
I tried this, unfortunately after clicking the button, nothing happens.
private fun refreshFragment(){
binding.buttonRefresh.setOnClickListener {
reloadFragment()
}
}
private fun reloadFragment(){
context?.let {
val fragManager = (context as? AppCompatActivity)?.supportFragmentManager
fragManager?.let {
val currentFrag = fragManager.findFragmentById(R.id.mainContainer)
currentFrag?.let {
val fragTransaction = fragManager.beginTransaction()
fragTransaction.detach(currentFrag)
fragTransaction.attach(currentFrag)
fragTransaction.commit()
}
}
}
}

Related

Android/Kotlin - Select current fragment

On my action, I use the onBackPressed() method. When I compare the fragment that triggered the event, the id is never the same and always falls into the else-statement. What's wrong?
override fun onBackPressed() {
val navHost = this.supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val pressed = navHost?.childFragmentManager.fragments?.get(0) as IOnBackPressed
var currentFragment = navHost?.childFragmentManager.fragments?.get(0)
pressed?.onBackPressed()?.takeIf { it }?.let {
when (currentFragment.id) {
R.id.myfragmet -> {
// implementation
}
else -> {
super.onBackPressed()
}
}
}
}
I want to know from what fragment I returning when I press the back button, so that I can do the necessary action. Let me simplify the code:
override fun onBackPressed() {
// get the nav host in the action
val navHost = this.supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
// get the current fragment in the nav host
var currentFragment = navHost?.childFragmentManager.fragments?.get(0)
// Verify the fragment to do a action
when (currentFragment.id) {
R.id.myfragmet -> {
// implementation
}
else -> {
super.onBackPressed()
}
}
}
But the currentFragment.Id is never equals R.id.myfragemnt

ActionMode callback never calls onCreateActionMode when started in fragment's OnViewCreated

#ExperimentalCoroutinesApi
class WeekdayTimesFragment #Inject constructor(viewModelFactory: SavedStateViewModelFactory.Factory)
:Fragment(R.layout.fragment_weekday_times), WeekdayAlarmTimesAdapter.OnWeekdayAlarmTimePressed, ActionModeListener {
private lateinit var weekdayAlarmTimesAdapter: WeekdayAlarmTimesAdapter
private val weekdayTimesViewModel
by viewModels<WeekdayTimesViewModel> { viewModelFactory.create(this) }
override val actionCallback = PrimaryActionCallback(this)
private var weekday: Int = 0
private val isActionModeActive: Boolean
get() = weekdayTimesViewModel.isActionModeActive
private val numSelected: Int
get() = weekdayTimesViewModel.selectedTimePositions.size
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
weekday = requireArguments().get("WEEKDAY") as Int
if(isActionModeActive) { //action mode may be active prior to rotation
startActionMode() //for some reason, starting action mode here does not work....
}
initRecyclerView() //will determine what recyclerview items are selected via the viewmodel
weekdayTimesViewModel.getTimesAndAlarmsForSelectedWeekday(Weekday[weekday])
.observe(viewLifecycleOwner) { times->
weekdayAlarmTimesAdapter.submitWeekdayTimes(times)
}
}
override fun alarmTimePressed(wasSelected: Boolean, position: Int, time: WeekdayTime) {
if(wasSelected) { //this item was selected prior to being selected, effectively unselecting it
weekdayTimesViewModel.selectedTimePositions.remove(position)
} else {
weekdayTimesViewModel.selectedTimePositions[position] =
Time(time.hour, time.minute, Weekday[weekday], time.alarm.alarmId, time.isDisabled, time.id)
}
if(numSelected == 0 && isActionModeActive) { //no more times are selected, turn off action mode
destroyActionMode() //need to de-select recyclerview items
} else {
if (!isActionModeActive) {
startActionMode()
} else {
actionCallback.changeTitle("$numSelected selected")
}
}
}
private fun startActionMode() {
actionCallback
.startActionMode(
requireView(),
R.menu.toolbar_contextual_menu,
"$numSelected selected")
//fragment hosting the viewpager
(requireParentFragment() as ActionModeListenerController).currentActionModeListener = this
weekdayTimesViewModel.isActionModeActive = true
}
override fun onActionItemClick(item: MenuItem?) {
item?.let {
when(it.itemId){
R.id.toolbar_disable_alarm-> {
requireContext().createGenericAlertDialog(
message = if(numSelected == 1) "Are you sure you want to disable this alarm?"
else "Are you sure you want to disable these alarms?",
positiveListener = { _, _ ->
weekdayTimesViewModel.disableSelectedTimes()
showUndoSnackbar(true)
}
).show()
}
R.id.toolbar_delete_alarms-> {
requireContext().createGenericAlertDialog(
message = if(numSelected == 1) "Are you sure you want to delete this alarm?"
else "Are you sure you want to delete these alarms?",
positiveListener = { _, _ ->
weekdayTimesViewModel.deleteSelectedTimes()
showUndoSnackbar(false)
}
).show()
}
}
}
}
//when we aren't destroying the view but need to end action mode (changing tab or after we confirm an action)
//we may want to do specific things specific to certain listeners, e.g. de-select certain recyclerview items.
override fun destroyActionMode() {
actionCallback.finishActionMode()
weekdayTimesViewModel.isActionModeActive = false
//also un-select all of the elements that have been selected
weekdayTimesViewModel.selectedTimePositions.clear()
//redraw recyclerview as well
rv_weekday_times.resetAdapterState()
}
private fun showUndoSnackbar(disablingTimes: Boolean){
val message =
if(disablingTimes) "Your alarm times have been disabled." else "Your alarm times have been deleted."
Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG).setAction("UNDO"){
if(disablingTimes)
weekdayTimesViewModel.restoreDisabledTimes()
else
weekdayTimesViewModel.restoreDeletedTimes()
}.setActionTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary))
.setAnchorView(requireActivity().bottom_nav_view).show()
}
//when rotating screen or navigating we can just end action mode. The state of whether we are
//in action mode either doesn't matter (navigation) or is already persisted, along with the selected
//items in the viewmodel(rotation)
override fun onDestroyView() {
actionCallback.finishActionMode()
super.onDestroyView()
}
private fun initRecyclerView() {
weekdayAlarmTimesAdapter = WeekdayAlarmTimesAdapter(this, weekdayTimesViewModel.selectedTimePositions.keys)
rv_weekday_times.apply {
adapter = weekdayAlarmTimesAdapter
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
}
weekdayAlarmTimesAdapter.selectedItems.clear()
//clears selected items as these items no longer remain consistently for the lifecycle of the view
}
}
So my ActionMode works great when I get a callback (alarmTimePressed) which is through a click listener set in the recyclerview's OnBindViewHolder. I start my ActionMode and it works great, I save whether ActionMode is active in my viewmodel, which works well upon rotation, as if it is active prior to rotation, it still remains active. As you can see in my OnViewCreated, I call startActionMode() when ActionMode is meant to be active. The problem is that when I start it from there, the onCreateActionMode callback is never called. This is strange because even after rotating, if I select an item in the RecyclerView it correctly launches ActionMode. The view I am passing to startActionMode is the same in both cases. If you need the code for PrimaryActionCallback, it's here: https://hastebin.com/vepujuhefe.m. Didn't think it was very necessary but in case you felt like it was necessary to see :). Anyone know what's going on here?

android - how to update mutablieLiveData from another class

I've a mutableLiveData of list of items in my fragment and I have a custom dialog that I pass a single item from this list to it . this is my code:
adapter.itemPosPriceListener.observe(this, Observer { pos ->
activity?.let { act ->
val dialog = DialogRoomPrices(act)
dialog.item.value = it.get(pos)
}
})
this is my dialog :
class DialogRoomPrices(act: Activity) {
private var dialog: Dialog
private var act: Activity
val item = MutableLiveData<RoomItem>()
init {
dialog = Dialog(act, R.style.DialogAnimation)
dialog.setContentView(R.layout.dialog_room_prices)
this.act = act
makeDialog()
val lp = WindowManager.LayoutParams()
lp.copyFrom(dialog.window?.attributes)
lp.width = WindowManager.LayoutParams.MATCH_PARENT
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
dialog.getWindow()?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
dialog.show()
dialog.window?.attributes = lp
}
private fun makeDialog() {
item?.value?.let { row ->
dialog.et_roomp_price_paye.setText(row.price_paye)
dialog.et_roomp_price_akhathafte.setText(row.price_akhathafte)
////and 10 more rows of these
}
dialog.bt_next.setOnClickListener {
/////need to update the item and observe it in my fragment
dialog.dismiss()
}
}
as you can see ,dialog has a form and after filling the data and clicking on submit button , I need to update my item in fragment class .
the question is , how can I update the this item and notify my fragment's item about it ?
I don't see the whole code, but such communication can be done simply with a callback.
Solution will look like
class DialogRoomPrices(act: Activity) {
interface Listener() {
fun onNext(data: SomeData)
}
private var listener: Listener?
private var dialog: Dialog
private var act: Activity
val item = MutableLiveData<RoomItem>()
...
dialog.bt_next.setOnClickListener {
val dataToBeSetToHostingFragment = getData()
listener?.onNext(dataToBeSetToHostingFragment)
dialog.dismiss()
}
In the Fragment
class HostingFragment(): Listener {
adapter.itemPosPriceListener.observe(this, Observer { pos ->
activity?.let { act ->
val dialog = DialogRoomPrices(act)
dialog.item.value = it.get(pos)
dialog.listener = object : DialogRoomPrices.Listener {
override onNext(data: SomeData) {
// Assign data to your liveData, force update recycler view or do any other action you want to
}
}
}
})

How to make RecyclerView scroll to last position in onCreate?

I was trying to just make the RecyclerView move to last position when the fragment is started, but cant find a solution. The code below works just fine when a new "Pagina" in inserted in database, it moves to last position. But, if I close and reopen it goes back to the top. So, how to make the smoothscroll work when the fragment is created?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appDatabase = AppDatabase.getDatabase(requireContext())
val smoothScroller = object : LinearSmoothScroller(context) {
override fun getVerticalSnapPreference(): Int = SNAP_TO_START
}
viewModelo = ViewModelProviders.of(this).get(ViewModelo::class.java)
// Create the observer which updates the UI.
val listPaginas = Observer <List<Pagina>> {
// Update the UI, in this case, a TextView.
recyclerViewAdapter?.addItens(it)
if (it.size < 1) {
smoothScroller.targetPosition = it.size
} else {
smoothScroller.targetPosition = it.size-1
}
recyclerView?.layoutManager?.startSmoothScroll(smoothScroller)
}
viewModelo?.todasPaginas()?.observe(this, listPaginas)
}
Try below changes, it worked!
LinearLayoutManager manager = new LinearLayoutManager(Application.Context);
manager.setStackFromEnd(true);
mRecyclerView.SetLayoutManager(manager);

How to remove duplicate entries from FragmentManager?

I have a simple activity with a BottomNavigationView. I'm using fragments to implement the contents of the activity for the different pages.
When the user presses the back button, it's supposed to go back to the previously looked at page. The problem is, when you repeatedly switch back and forth between the pages (fragments), this entire history is recorded. Take this example:
A -> B -> A -> B -> C -> A -> C
Pressing the back button would result in the reverse, but instead I want this behaviour (I noticed it in the Instagram app):
C -> A -> B -> Exit App
So every fragment should only have one entry in the backstack. How do I do this? I do I remove the previous transactions for a fragment from the stack?
Is this at all possible using a FragmentManager? Or do I have to implement my own?
My Activity with the BottomNavigationView:
class ActivityOverview : AppCompatActivity() {
// Listener for BottomNavigationView
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
// "Home" menu item pressed
setActiveFragment(resources.getString(R.string.tag_fragment_home))
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
// "Dashboard" menu item pressed
return#OnNavigationItemSelectedListener true
}
R.id.navigation_settings -> {
// "Settings" menu item pressed
setActiveFragment(resources.getString(R.string.tag_fragment_settings))
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_overview)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
navigation.menu.findItem(R.id.navigation_home).setChecked(true)
// Set initial fragment
setActiveFragment(resources.getString(R.string.tag_fragment_home))
}
override fun onBackPressed() {
// > 1 so initial fragment addition isn't removed from stack
if (fragmentManager.backStackEntryCount > 1) {
fragmentManager.popBackStack()
} else {
finish()
}
}
// Update displayed fragment
fun setActiveFragment(tag: String) {
val fragment = if (fragmentManager.findFragmentByTag(tag) != null) {
// Fragment is already initialized
if (fragmentManager.findFragmentByTag(tag).isVisible) {
// Fragment is visible already, don't add another transaction
null
} else {
// Fragment is not visible, add transaction
fragmentManager.findFragmentByTag(tag)
}
} else {
// Fragment is not initialized yet
when (tag) {
resources.getString(R.string.tag_fragment_home) -> FragmentHome()
resources.getString(R.string.tag_fragment_settings) -> FragmentSettings()
else -> null
}
}
if (fragment != null) {
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.container_fragment, fragment, tag)
transaction.addToBackStack(null)
transaction.commit()
}
}
}
At this point I'm pretty sure it doesn't work with FragmentManager, so I created a class to implement a stack that doesn't allow duplicates:
class NoDuplicateStack<T> {
val stack: MutableList<T> = mutableListOf()
val size: Int
get() = stack.size
// Push element onto the stack
fun push(p: T) {
val index = stack.indexOf(p)
if (index != -1) {
stack.removeAt(index)
}
stack.add(p)
}
// Pop upper element of stack
fun pop(): T? {
if (size > 0) {
return stack.removeAt(stack.size - 1)
} else {
return null
}
}
// Look at upper element of stack, don't pop it
fun peek(): T? {
if (size > 0) {
return stack[stack.size - 1]
} else {
return null
}
}
}
I then integrated this class into my activity:
class ActivityOverview : AppCompatActivity() {
val fragmentsStack = NoDuplicateStack<String>()
val fragmentHome = FragmentHome()
val fragmentSettings = FragmentSettings()
val fragmentHistory = FragmentHistory()
// Listener for BottomNavigationView
private val mOnNavigationItemSelectedListener = ...
override fun onCreate(savedInstanceState: Bundle?) {
...
}
override fun onBackPressed() {
if (fragmentsStack.size > 1) {
// Remove current fragment from stack
fragmentsStack.pop()
// Get previous fragment from stack and set it again
val newTag = fragmentsStack.pop()
if (newTag != null) {
setActiveFragment(newTag)
}
} else {
finish()
}
}
// Update displayed fragment
fun setActiveFragment(tag: String) {
val fragment = when (tag) {
resources.getString(R.string.tag_fragment_home) -> fragmentHome
resources.getString(R.string.tag_fragment_settings) -> fragmentSettings
resources.getString(R.string.tag_fragment_history) -> fragmentHistory
else -> null
}
if (fragment != null && !fragment.isVisible) {
fragmentManager.beginTransaction()
.replace(R.id.container_fragment, fragment, tag)
.commit()
fragmentsStack.push(tag)
}
}
}
I also faced the same problem, I did this which uses the system stack
val totalFragments = supportFragmentManager.backStackEntryCount
if (totalFragments != 0) {
val removed = supportFragmentManager.getBackStackEntryAt(totalFragments - 1)
poppedFragments.add(removed.name!!)
for (idx in totalFragments - 1 downTo 0) {
val fragment = supportFragmentManager.getBackStackEntryAt(idx)
if (!poppedFragments.contains(fragment.name)) {
supportFragmentManager.popBackStack(fragment.name, 0)
return
}
}
finish()
return
}
super.onBackPressed()
and then added this while launching the fragment
if (poppedFragments.contains(tag)) {
poppedFragments.remove(tag)
}

Categories

Resources