class Rove3LiveVideoFragment : BaseFragment(){
#Inject
lateinit var roveR3LiveVideoFragmentViewModel: RoveR3LiveVideoFragmentViewModel
#Inject
lateinit var videoControlWidget: VideoControlWidget
#Inject
lateinit var notConnectedWidget: NotConnectedWidget
#Inject
lateinit var appPreference: AppPreference
#Inject
lateinit var videoFullViewWidget: VideoFullViewWidget
lateinit var customLoader: CustomLoader
private lateinit var onFullScreenListener: OnFullScreenListener
var isChangeScreenButtonClicked = false
var isPortraitMode = true
override fun onAttach(context: Context) {
inject(this)
super.onAttach(context)
if (activity is Rove3MainActivity) {
onFullScreenListener = activity as OnFullScreenListener
} else {
throw ClassCastException(
activity.toString()
+ " must implement MyListFragment.OnItemSelectedListener"
)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_rove_r3_live_video, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initWidget(view_live_vide_page)
customLoader = CustomLoader(requireContext())
val wifiManager =
requireContext().applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
wifiManager?.let {
if (wifiManager.isWifiEnabled) {
roveR3LiveVideoFragmentViewModel.apply {
customLoader.show(getString(R.string.title_please_wait))
if (appPreference.isAppGoesBackGroundExceptHomePage) {
appPreference.isAppGoesBackGroundExceptHomePage = false
getRove3CameraConnectionStatus(true)
} else {
getRove3CameraConnectionStatus(false)
}
observe(stateLiveData, ::getR3ConnectedInLiveVideFragment)
}
} else {
notConnectedView()
}
}
}
private fun initWidget(view: View) {
videoControlWidget.apply {
initView(view)
addWidget(this)
activity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
observe(onClicked, ::onClickedButton)
}
notConnectedWidget.apply {
initView(view)
addWidget(this)
}
videoFullViewWidget.apply {
initView(view)
activity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
addWidget(this)
observe(onClicked, ::onFullVideoViewControllClick)
}
}
private fun onFullVideoViewControllClick(callToAction: VideoFullViewWidget.CallToAction) {
when (callToAction) {
is VideoFullViewWidget.CallToAction.PortraitMode -> {
activity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
showVideoView(false)
isChangeScreenButtonClicked = true
}
}
}
private fun onClickedButton(callToAction: VideoControlWidget.CallToAction) {
when (callToAction) {
is VideoControlWidget.CallToAction.LandScapeMode -> {
activity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
isChangeScreenButtonClicked = true
showVideoView(true)
}
}
}
override fun onPause() {
super.onPause()
Log.d("CALLBACKK", "onPause")
}
private fun getR3ConnectedInLiveVideFragment(state: BaseViewModel.LiveVideFragmentState) {
}
private fun notConnectedView() {
customLoader.hide()
notConnectedWidget.show()
view_surface.visibility = View.GONE
videoControlWidget.hide()
}
private fun showLiveVideoWhenR3Connected() {
notConnectedWidget.hide()
view_surface.visibility = View.VISIBLE
videoControlWidget.show()
}
private fun addWidget(widget: Widget) {
lifecycle.addObserver(widget)
}
private fun showVideoView(isFullVideoView: Boolean) {
onFullScreenListener.onFullSreen(isFullVideoView)
if (isFullVideoView) {
videoControlWidget.hide()
videoFullViewWidget.show()
val params = view_surface.layoutParams as RelativeLayout.LayoutParams
params.width = ViewGroup.LayoutParams.MATCH_PARENT
params.height = ViewGroup.LayoutParams.MATCH_PARENT
view_surface.layoutParams = params
} else {
val params = view_surface.layoutParams as RelativeLayout.LayoutParams
params.width = ViewGroup.LayoutParams.MATCH_PARENT
params.height =
(240 * requireContext().applicationContext.resources.displayMetrics.density).toInt()
view_surface.layoutParams = params
videoControlWidget.show()
videoFullViewWidget.hide()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Log.d("ORIENTAIONNNNNN", "ORIENTATIONCHANGE")
requireActivity().let {
if (isChangeScreenButtonClicked) {
Log.d("ORIENTAIONNNNNN", "BUTTONCLICKED")
stopAutoOrientationFor3Seconds()
} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.d("ORIENTAIONNNNNN", "LANDSCAPE")
onFullScreenListener.onFullSreen(false)
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
showVideoView(true)
isPortraitMode = false
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Log.d("ORIENTAIONNNNNN", "POTRIAT")
onFullScreenListener.onFullSreen(true)
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
showVideoView(false)
isPortraitMode = true
}
}
}
private fun stopAutoOrientationFor3Seconds() {
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
run {
isChangeScreenButtonClicked = false
activity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
}
}
}, 5000)
}
}
This is my Activity call back
override fun onFullSreen(fullscreen: Boolean) {
if (fullscreen) {
nav_view.visibility = GONE
window.decorView.systemUiVisibility = (android.view.View.SYSTEM_UI_FLAG_IMMERSIVE
or android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
if (supportActionBar != null) {
supportActionBar!!.hide()
}
Log.d("ORIENTAIONNNNNN", "FULLVIEW")
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
} else {
nav_view.visibility = VISIBLE
Log.d("ORIENTAIONNNNNN", "HALFVIEW")
window.decorView.systemUiVisibility = android.view.View.SYSTEM_UI_FLAG_VISIBLE
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
if (supportActionBar != null) {
supportActionBar!!.show()
}
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
This is my fragment i have button click for full screen and also screen rotation on sensor on button click its working fine i am able to rotate screen because we are not depending on onConfigurationChanged method while when rotate device manually then first time when i launch app and try to rotate then portrait to landscape then onConfigurationChanged method calling but when i try to rotate to landscape to portrait then onConfigurationChanged method is not calling please help me what i am doing wrong i am following mvvm pattern .
Sorry it was my mistake i got solution for this question .
when i move it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR this line of code at end in fragment onconfig change method its work fine
I have this app in which I have a parent fragment, which has 2 child fragments in a ViewPager2 object.
One of the child fragments has an interface to communicate changes on its menu to the parent fragment.
The child fragment in question is TasksListFragment.kt and the parent fragment is TodayFragment.kt
When I try to initialize the interface in the child fragment onAttach() function, I get
FATAL EXCEPTION: main Process: com.rajchenbergstudios.hoygenda, PID:
java.lang.ClassCastException: com.rajchenbergstudios.hoygenda.ui.activity.MainActivity cannot be cast to com.rajchenbergstudios.hoygenda.ui.todaylists.taskslist.TasksListFragment$ChildFragmentListener
I don't understand why I get this error, and it says MainActivity, when the parent is a fragment which is the one implementing the interface in the first place, not the MainActivity.
I have everything set up correctly:
I have an interface in the child fragment
The interface is used on the child fragment onCreateMenu to pass the menu object to its interface function onFragmentMenuChanged(menu: Menu)
I override the child fragment's onAttach() and initialize the interface:
override fun onAttach(context: Context) {
super.onAttach(context)
childFragmentListener = context as ChildFragmentListener
}
I write a function called setListener() which is called from the parent fragment to pass its context this to the function parameter which assigns it to the childFragment listener
fun setListener(listener: ChildFragmentListener) {
this.childFragmentListener = listener
}
The parent fragment implements the child fragment listener as seen in the TodayFragment.kt file
Can you tell me what am I doing wrong or how to implement an interface to effectively communicate from child fragment back to its parent fragment?
TasksListFragment.kt
#ExperimentalCoroutinesApi
#AndroidEntryPoint
class TasksListFragment : Fragment(R.layout.fragment_child_tasks_list), TasksListAdapter.OnItemClickListener {
private val viewModel: TasksListViewModel by viewModels()
private lateinit var searchView: SearchView
private lateinit var childFragmentListener: ChildFragmentListener
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentChildTasksListBinding.bind(view)
val tasksListAdapter = TasksListAdapter(this)
binding.apply {
tasksListRecyclerview.layoutTasksListRecyclerview.apply {
adapter = tasksListAdapter
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
}
ItemTouchHelper(object: ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT){
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val task = tasksListAdapter.currentList[viewHolder.adapterPosition]
viewModel.onTaskSwiped(task)
}
}).attachToRecyclerView(tasksListRecyclerview.layoutTasksListRecyclerview)
}
loadObservable(binding, tasksListAdapter)
loadTasksEventCollector()
loadMenu()
}
private fun loadObservable(binding: FragmentChildTasksListBinding, tasksListAdapter: TasksListAdapter) {
viewModel.tasks.observe(viewLifecycleOwner){ tasksList ->
binding.apply {
HGDAViewStateUtils.apply {
if (tasksList.isEmpty()) {
setViewVisibility(tasksListRecyclerview.layoutTasksListRecyclerview, visibility = View.INVISIBLE)
setViewVisibility(tasksListLayoutNoData.layoutNoDataLinearlayout, visibility = View.VISIBLE)
} else {
setViewVisibility(tasksListRecyclerview.layoutTasksListRecyclerview, visibility = View.VISIBLE)
setViewVisibility(tasksListLayoutNoData.layoutNoDataLinearlayout, visibility = View.INVISIBLE)
tasksListAdapter.submitList(tasksList)
}
}
}
}
}
/**
* TasksListViewModel.TaskEvent.ShowUndoDeleteTaskMessage: Stays in this class. It asks for components relevant to this class.
* TasksListViewModel.TaskEvent.NavigateToEditTaskScreen: Stays in this class. The method it overrides comes from task list adapter.
* TasksListViewModel.TaskEvent.NavigateToDeleteAllCompletedScreen: Stays in this class. Relevant to menu which is in this class.
* TasksListViewModel.TaskEvent.NavigateToDeleteAllScreen: Stays in this class. Relevant to menu which is in this class.
*/
private fun loadTasksEventCollector() {
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.tasksEvent.collect { event ->
when (event) {
is TasksListViewModel.TaskEvent.ShowUndoDeleteTaskMessage -> {
Snackbar
.make(requireView(), "Task deleted", Snackbar.LENGTH_LONG)
.setAction("UNDO"){
viewModel.onUndoDeleteClick(event.task)
}
.show()
}
is TasksListViewModel.TaskEvent.NavigateToEditTaskScreen -> {
val action = TodayFragmentDirections
.actionTodayFragmentToTaskAddEditFragment(task = event.task, title = "Edit task", taskinset = null, origin = 1)
findNavController().navigate(action)
}
is TasksListViewModel.TaskEvent.NavigateToAddTaskToSetBottomSheet -> {
val action = TasksListFragmentDirections.actionGlobalSetBottomSheetDialogFragment(task = event.task, origin = 1)
findNavController().navigate(action)
}
is TasksListViewModel.TaskEvent.NavigateToDeleteAllCompletedScreen -> {
val action = TasksListFragmentDirections
.actionGlobalTasksDeleteAllDialogFragment(origin = 1)
findNavController().navigate(action)
}
is TasksListViewModel.TaskEvent.NavigateToDeleteAllScreen -> {
val action = TasksListFragmentDirections
.actionGlobalTasksDeleteAllDialogFragment(origin = 3)
findNavController().navigate(action)
}
}.exhaustive
}
}
}
private fun loadMenu(){
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(object: MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
childFragmentListener.onFragmentChanged(menu)
menuInflater.inflate(R.menu.menu_tasks_list_fragment, menu)
val searchItem = menu.findItem(R.id.tasks_list_menu_search)
searchView = searchItem.actionView as SearchView
val pendingQuery = viewModel.searchQuery.value
if (pendingQuery != null && pendingQuery.isNotEmpty()) {
searchItem.expandActionView()
searchView.setQuery(pendingQuery, false)
}
searchView.OnQueryTextChanged{ searchQuery ->
viewModel.searchQuery.value = searchQuery
}
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
menu.findItem(R.id.tasks_list_menu_hide_completed).isChecked =
viewModel.preferencesFlow.first().hideCompleted
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.tasks_list_menu_sort_by_date -> {
viewModel.onSortOrderSelected(SortOrder.BY_DATE)
true
}
R.id.tasks_list_menu_sort_by_name -> {
viewModel.onSortOrderSelected(SortOrder.BY_NAME)
true
}
R.id.tasks_list_menu_hide_completed -> {
menuItem.isChecked = !menuItem.isChecked
viewModel.onHideCompletedSelected(menuItem.isChecked)
true
}
R.id.tasks_list_menu_delete_completed -> {
viewModel.onDeleteAllCompletedClick()
true
}
R.id.tasks_list_menu_delete_all -> {
viewModel.onDeleteAllClick()
true
}
else -> false
}
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
interface ChildFragmentListener {
fun onFragmentChanged(menu: Menu)
}
fun setListener(listener: ChildFragmentListener) {
this.childFragmentListener = listener
}
override fun onItemClick(task: Task) {
viewModel.onTaskSelected(task)
}
override fun onItemLongClick(task: Task) {
viewModel.onTaskLongSelected(task)
}
override fun onCheckboxClick(task: Task, isChecked: Boolean) {
viewModel.onTaskCheckedChanged(task, isChecked)
}
override fun onAttach(context: Context) {
super.onAttach(context)
childFragmentListener = context as ChildFragmentListener
}
override fun onPause() {
super.onPause()
Logger.i(TAG, "onPause", "TasksListFragment paused")
}
override fun onDestroyView() {
super.onDestroyView()
searchView.setOnQueryTextListener(null)
}
}
TodayFragment.kt
#ExperimentalCoroutinesApi
#AndroidEntryPoint
class TodayFragment : Fragment(R.layout.fragment_parent_today), TasksListFragment.ChildFragmentListener {
private val viewModel: TodayViewModel by viewModels()
private lateinit var binding: FragmentParentTodayBinding
private var fabClicked: Boolean = false
private lateinit var tasksListMenu: Menu
private lateinit var viewPager: ViewPager2
private val rotateOpen: Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.rotate_open_anim) }
private val rotateClose: Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.rotate_close_anim) }
private val fromBottom: Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.from_bottom_anim) }
private val toBottom: Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.to_bottom_anim) }
private val fadeIn: Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.fade_in) }
private val fadeOut: Animation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.fade_out) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentParentTodayBinding.bind(view)
binding.apply {
tasksListTransparentWhiteScreen.setOnClickListener {
fabAnimationsRollBack(binding)
fabClicked = !fabClicked
}
}
setChildFragmentMenus()
initViewPagerWithTabLayout(binding)
todayDateDisplay(binding)
initFabs(binding)
loadTodayEventCollector()
getFragmentResultListeners()
}
private fun setChildFragmentMenus(){
val tasksListFragment = TasksListFragment()
tasksListFragment.setListener(this)
Logger.i(TAG, "setChildFragmentMenus", "TasksListFragment menu set")
}
private fun getFragmentResultListeners() {
setFragmentResultListener("add_edit_request"){_, bundle ->
val result = bundle.getInt("add_edit_result")
onFragmentResult(result)
}
setFragmentResultListener("create_set_request_2"){_, bundle ->
val result = bundle.getInt("create_set_result_2")
onFragmentResult(result)
}
setFragmentResultListener("task_added_to_set_request"){_, bundle ->
val result = bundle.getInt("task_added_to_set_result")
val message = bundle.getString("task_added_to_set_message")
onFragmentResult(result, message)
}
setFragmentResultListener("task_added_from_set_request"){_, bundle ->
val result = bundle.getInt("task_added_from_set_result")
val message = bundle.getString("task_added_from_set_message")
onFragmentResult(result, message)
}
}
private fun onFragmentResult(result: Int, message: String? = ""){
viewModel.onFragmentResult(result, message)
}
/**
* TodayViewModel.TodayEvent.NavigateToAddTaskScreen: Relevant to this class. Belongs to Fab which are all in this class.
* TodayViewModel.TodayEvent.ShowTaskSavedConfirmationMessage: Relevant to this class. Belongs to onFragmentResultListener which is here.
* TodayViewModel.TodayEvent.ShowTaskSavedInNewOrOldSetConfirmationMessage: Relevant to this class. Belongs to onFragmentResultListener which is here.
* TodayViewModel.TodayEvent.ShowTaskAddedFromSetConfirmationMessage: Relevant to this class. Belongs to onFragmentResultListener which is here.
* TodayViewModel.TodayEvent.NavigateToAddTasksFromSetBottomSheet: Relevant to this class. Belongs to Fab which are all in this class.
*/
private fun loadTodayEventCollector() {
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.todayEvent.collect { event ->
when (event) {
is TodayViewModel.TodayEvent.NavigateToAddTaskScreen -> {
val action = TodayFragmentDirections
.actionTodayFragmentToTaskAddEditFragment(task = null, title = "Add task"
, taskinset = null, origin = 1)
findNavController().navigate(action)
}
is TodayViewModel.TodayEvent.ShowTaskSavedConfirmationMessage -> {
Snackbar.make(requireView(), event.msg, Snackbar.LENGTH_LONG).show()
setViewPagerPage(0)
}
is TodayViewModel.TodayEvent.ShowTaskSavedInNewOrOldSetConfirmationMessage -> {
Snackbar.make(requireView(), event.msg.toString(), Snackbar.LENGTH_LONG).show()
}
is TodayViewModel.TodayEvent.ShowTaskAddedFromSetConfirmationMessage -> {
Snackbar.make(requireView(), event.msg.toString(), Snackbar.LENGTH_LONG).show()
fabClicked = true
setFabAnimationsAndViewStates(binding)
setViewPagerPage(0)
}
is TodayViewModel.TodayEvent.NavigateToAddTasksFromSetBottomSheet -> {
val action = TasksListFragmentDirections
.actionGlobalSetBottomSheetDialogFragment(task = null, origin = 2)
findNavController().navigate(action)
}
}.exhaustive
}
}
}
// This will soon be used to be 1
private fun setViewPagerPage(index: Int){
viewModel.postActionWithDelay(300, object: TodayViewModel.PostActionListener{
override fun onDelayFinished() {
viewPager.setCurrentItem(index, true)
}
})
}
private fun todayDateDisplay(binding: FragmentParentTodayBinding) {
binding.apply {
tasksListDateheader.apply {
dateHeaderDayofmonth.text = viewModel.getCurrentDayOfMonth()
dateHeaderMonth.text = viewModel.getCurrentMonth()
dateHeaderYear.text = viewModel.getCurrentYear()
dateHeaderDayofweek.text = viewModel.getCurrentDayOfWeek()
}
}
}
private fun initViewPagerWithTabLayout(binding: FragmentParentTodayBinding) {
viewPager = binding.todayViewpager
val tabLayout: TabLayout = binding.todayTablayout
viewPager.adapter = activity?.let { TodayPagerAdapter(it) }
Logger.i(TAG, "initViewPagerWithTabLayout", "viewPager is not null")
TabLayoutMediator(tabLayout, viewPager) { tab, index ->
tab.text = when (index) {
0 -> "Tasks"
1 -> "Journal"
else -> throw Resources.NotFoundException("Tab not found at position")
}.exhaustive
when (index) {
0 -> {
}
1 -> {
fabClicked = false
}
}
}.attach()
}
private fun initFabs(binding: FragmentParentTodayBinding) {
binding.apply {
tasksListFab.setOnClickListener {
onMainFabClick(binding)
}
tasksListSubFab1.setOnClickListener {
Logger.i(TAG, "initFabs", "Coming soon")
}
tasksListSubFab2.setOnClickListener {
viewModel.onAddTasksFromSetClick()
}
tasksListSubFab3.setOnClickListener {
viewModel.onAddNewTaskClick()
}
}
}
private fun onMainFabClick(binding: FragmentParentTodayBinding) {
setFabAnimationsAndViewStates(binding)
}
private fun setFabAnimationsAndViewStates(binding: FragmentParentTodayBinding) {
setFabAnimationVisibilityAndClickability(binding, fabClicked)
fabClicked = !fabClicked
}
private fun setFabAnimationVisibilityAndClickability(binding: FragmentParentTodayBinding, clicked: Boolean) {
if (!clicked) fabAnimationsRollIn(binding) else fabAnimationsRollBack(binding)
}
private fun fabAnimationsRollIn(binding: FragmentParentTodayBinding) {
binding.apply {
HGDAAnimationUtils.apply {
HGDAViewStateUtils.apply {
setViewAnimation(v1 = tasksListFab, a = rotateOpen)
setViewAnimation(v1 = tasksListSubFab1, v2 = tasksListSubFab2, v3 = tasksListSubFab3, a = fromBottom)
setViewAnimation(v1 = tasksListSubFab1Tv, v2 = tasksListSubFab2Tv, v3 = tasksListSubFab3Tv, a = fromBottom)
setViewAnimation(v1 = tasksListTransparentWhiteScreen, a = fadeIn)
setViewVisibility(v1 = tasksListSubFab1, v2 = tasksListSubFab2, v3 = tasksListSubFab3
, v4 = tasksListSubFab1Tv, v5 = tasksListSubFab2Tv, v6 = tasksListSubFab3Tv, visibility = View.VISIBLE)
setViewVisibility(v1 = tasksListTransparentWhiteScreen, visibility = View.VISIBLE)
setViewClickState(v1 = tasksListSubFab1, v2 = tasksListSubFab2, v3 = tasksListSubFab3, clickable = true)
setViewClickState(v1 = tasksListTransparentWhiteScreen, clickable = true)
}
}
}
}
private fun fabAnimationsRollBack(binding: FragmentParentTodayBinding) {
binding.apply {
HGDAAnimationUtils.apply {
HGDAViewStateUtils.apply {
setViewAnimation(v1 = tasksListFab, a = rotateClose)
setViewAnimation(v1 = tasksListSubFab1, v2 = tasksListSubFab2, v3 = tasksListSubFab3, a = toBottom)
setViewAnimation(v1 = tasksListSubFab1Tv, v2 = tasksListSubFab2Tv, v3 = tasksListSubFab3Tv, a = toBottom)
setViewAnimation(v1 = tasksListTransparentWhiteScreen, a = fadeOut)
setViewVisibility(v1 = tasksListSubFab1, v2 = tasksListSubFab2, v3 = tasksListSubFab3
, v4 = tasksListSubFab1Tv, v5 = tasksListSubFab2Tv, v6 = tasksListSubFab3Tv, visibility = View.INVISIBLE)
setViewVisibility(v1 = tasksListTransparentWhiteScreen, visibility = View.INVISIBLE)
setViewClickState(v1 = tasksListSubFab1, v2 = tasksListSubFab2, v3 = tasksListSubFab3, clickable = false)
setViewClickState(v1 = tasksListTransparentWhiteScreen, clickable = false)
}
}
}
}
override fun onFragmentChanged(menu: Menu) {
tasksListMenu = menu
}
override fun onPause() {
super.onPause()
tasksListMenu.clear()
}
}
Fragment is not a Context i.e fragment is not a child of context .
So when you try to cast context as ChildFragmentListener you are actually casting your Activity to ChildFragmentListener which is giving you this RuntimeException . to make it work you can use childFragmentListener = parentFragment as ChildFragmentListener
Also if your Doing this you do not need setListener method anymore.
On other hand i would suggest you do not use listeners to communicate b/w fragments . I see you already using viewModel so just use a shared one to communicate . You can get a shared ViewModel inside child by creating it with parentFragment.
I have framelayout in activity and two fragments that I need to attach to the activity depending on the user's choise. When I attach one fragment and change orientation, my fragment gest destrotroyed with activity. How to keep the state of fragment inside activity?
This is my menuActivity which has framelayout.
class MenuActivity : AppCompatActivity(), OptionsFragment.PassFragment {
private val TAG = "Menu"
private lateinit var binding: ActivityMenuBinding
private lateinit var informationViewModel: InformationViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMenuBinding.inflate(layoutInflater)
setContentView(binding.root)
informationViewModel = ViewModelProvider(this).get(InformationViewModel::class.java)
Log.i(TAG, "onCreate: code: ${informationViewModel.hashCode()}")
if (informationViewModel.fragment != null) {
showFragment(informationViewModel.fragment!!)
} else {
showFragment(OptionsFragment())
}
}
private fun showFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction().replace(R.id.container, fragment)
.commit()
}
override fun onPassFragment(fragment: Fragment) {
informationViewModel.fragment = fragment
}
}
This is my first fragment which should appear first in activity and it has two textviews for two fragments.
class OptionsFragment : Fragment(R.layout.options_fragment) {
private lateinit var txtPersonal: TextView
private lateinit var txtSecond: TextView
private lateinit var informationViewModel: InformationViewModel
private lateinit var passFragment: PassFragment
private val TAG = "OptionsFragment"
interface PassFragment {
fun onPassFragment(fragment: Fragment)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
txtPersonal = view.findViewById(R.id.person_det)
txtSecond = view.findViewById(R.id.trading_currency)
informationViewModel = ViewModelProvider(this).get(InformationViewModel::class.java)
Log.i(TAG, "onViewCreated: code: ${informationViewModel.hashCode()} ")
txtPersonal.setOnClickListener {
passFragment(PersonalInfFragment())
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.container, PersonalInfFragment()).commit()
}
txtSecond.setOnClickListener {
passFragment(TradingFragment())
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.container, TradingFragment()).commit()
}
}
private fun passFragment(fragment: Fragment) {
try {
passFragment = activity as PassFragment
passFragment.onPassFragment(fragment)
} catch (ex: ClassCastException) {
}
}
}
Let's say from above options fragment, I opened my second TradingFragment and showed some information from retrofit using the same viewmodel.
class TradingFragment : Fragment(R.layout.currency_trading) {
private val TAG = "TradingFragment"
private lateinit var informationViewModel: InformationViewModel
private lateinit var rec_view: RecyclerView
private lateinit var btnSearch: Button
private lateinit var adapter: TradingAdapter
private lateinit var rangeSeek: RangeSlider
private lateinit var txtStartDate: TextView
private lateinit var txtFinishData: TextView
private lateinit var spinner: Spinner
private var from = 0
private var to = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btnSearch = view.findViewById(R.id.btnSearch)
rec_view = view.findViewById(R.id.rec_view)
rangeSeek = view.findViewById(R.id.rangeSlider)
txtStartDate = view.findViewById(R.id.txtFrom)
txtFinishData = view.findViewById(R.id.txtTo)
spinner = view.findViewById(R.id.spinner)
informationViewModel = ViewModelProvider(this).get(InformationViewModel::class.java)
val cur_time = "${System.currentTimeMillis()}".substring(0, 10)
rangeSeek.valueTo = Integer.valueOf(cur_time) * 1.0f
initRecyclerView()
rangeSeek.addOnSliderTouchListener(object : RangeSlider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: RangeSlider) {
}
override fun onStopTrackingTouch(slider: RangeSlider) {
changeDateValueForText(slider.values.get(0).toInt(), slider.values.get(1).toInt())
}
})
btnSearch.setOnClickListener {
startSearchingCurrencyTrading()
}
}
private fun startSearchingCurrencyTrading() {
val pair = spinner.selectedItem.toString()
informationViewModel.startRequestingCurrency(pair, from, to)
}
private fun changeDateValueForText(start: Int, finish: Int) {
from = start
to = finish
val startDate = Date(start * 1000L)
val finishDate = Date(finish * 1000L)
val df = SimpleDateFormat("dd:MM:yyyy")
txtStartDate.text = df.format(startDate)
txtFinishData.text = df.format(finishDate)
}
private fun initRecyclerView() {
adapter = TradingAdapter()
rec_view.adapter = adapter
rec_view.layoutManager = LinearLayoutManager(requireActivity())
informationViewModel.returnCurrentTrading().observe(this) {
if (it != null) {
adapter.updateTradingItems(it)
} else {
adapter.updateTradingItems(listOf())
}
}
}
}
This is my current solution it worked, but I lost my previous query result to retrofit that was being showed on the tradingfragment before orientation change.
Rotation of the screen is defined a configuration change and If you want to preserve the state of your UI I suggest you to implent a ViewModel as described here
However, you have several options to preserve the state of the UI, the choose depends on your needs, all possible scenarios are well explained in the official documentation.
I'm trying to build an app that has an intro slider when it's installed. I followed a tutorial.
However, I have an error and the app doesn't even open.
Here is some code:
Class Intro
class Intro : AppCompatActivity(), View.OnClickListener {
lateinit var mPager : ViewPager
var layouts : IntArray = intArrayOf(R.layout.first_slide,R.layout.second_slide,R.layout.third_slide)
lateinit var dotsLayout : LinearLayout
lateinit var dots: Array<ImageView>
lateinit var mAdapter : PageAdapter
lateinit var btnNext: Button
lateinit var btnSkip: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_intro)
if(PrefManager(this).checkPreferences()){goToHomePage()}
if(Build.VERSION.SDK_INT >=19)
{
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}else{
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}
mPager=findViewById(R.id.pager)
mAdapter= PageAdapter(layouts,this)
mPager.adapter= mAdapter
dotsLayout = findViewById(R.id.dots)
btnNext = findViewById(R.id.btnNext)
btnSkip=findViewById(R.id.btnSkip)
btnSkip.setOnClickListener { this }
btnNext.setOnClickListener { this }
createDots(0)
mPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
createDots(position)
if(position == layouts.size){
btnNext.setText("DONE")
btnSkip.visibility=View.INVISIBLE
}else{
btnNext.setText("NEXT")
btnSkip.visibility=View.VISIBLE
}
}
})
}
fun createDots(position:Int){
if(dotsLayout!=null){
dotsLayout.removeAllViews()
}
dots = Array(layouts.size,{i -> ImageView(this)})
for(i in 0..layouts.size -1) {
dots[i] = ImageView(this)
if (i == position) {
dots[i].setImageDrawable(ContextCompat.getDrawable(this, R.drawable.active_dots))
} else {
dots[i].setImageDrawable(ContextCompat.getDrawable(this, R.drawable.inactive_dots))
}
var params: LinearLayout.LayoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
params.setMargins(4,0,4,0)
dotsLayout.addView(dots[i],params)
}
}
override fun onClick(v: View?) {
when(v!!.id){
R.id.btnSkip ->{
goToHomePage()
PrefManager(this).writeSharedPreferences()
}
R.id.btnNext ->{
loadNextSlide()
}
}
}
private fun goToHomePage() {
startActivity(Intent(this, HomePage::class.java))
finish()
}
private fun loadNextSlide() {
var nextSlide: Int = mPager.currentItem+1
if(nextSlide<layouts.size){
mPager.setCurrentItem(nextSlide)
}
else{
goToHomePage()
PrefManager(this).writeSharedPreferences()
}
}
}
Class PrefManager
class PrefManager {
lateinit var context : Context
lateinit var pref: SharedPreferences
constructor(context: Context) {
this.context = context
getSharedPreferences()
}
private fun getSharedPreferences(){
pref = context.getSharedPreferences(context.getString(R.string.pref_name), Context.MODE_PRIVATE)
}
fun writeSharedPreferences(){
var editor : SharedPreferences.Editor=pref.edit()
editor.putString(context.getString(R.string.pref_key),"NEXT")
editor.commit()
}
fun checkPreferences() : Boolean
{
var status: Boolean = false
status = !pref.getString(context.getString(R.string.pref_key),null).equals("null")
return status
}
fun clearPreferences(){
pref.edit().clear().commit()
context.startActivity(Intent(context, HomePage::class.java))
(context as AppCompatActivity).finish()
}
}
The app works but the buttons next and skip don't respond whe clicked. Why is this happening?
Update: I changed some redundant thing on the code and uncommented the part that I've previous commented, and I don't have the error anymore. But the buttons still don't work
I have two activities. Activity A contains a recycler view of card view elements with images in them. On selecting an element in Activity A. The full landscape version of the image is displayed in Activity B.
The layout file for Activity B is a follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
The codebase for Activity B is as follows:
class PosterViewActivity : AppCompatActivity()
{
private lateinit var imageView: AppCompatImageView
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_poster_view)
init()
}
private fun init()
{
findViewsByID()
getBundle()
toggleHide()
}
private fun getBundle()
{
if (intent != null)
{
var imageUrl = intent.getStringExtra(ARG_IMAGE_URL)
if (imageUrl != null)
{
Glide.with(this).load(imageUrl).centerInside().into(imageView)
}
}
}
private fun findViewsByID()
{
imageView = findViewById(R.id.imageView)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean
{
if (item.itemId == android.R.id.home)
{
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed()
{
super.onBackPressed()
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
override fun onWindowFocusChanged(hasFocus: Boolean)
{
super.onWindowFocusChanged(hasFocus)
}
private fun toggleHide()
{
val uiOptions = window.decorView.systemUiVisibility
var newUiOptions = uiOptions
val isImmersiveModeEnabled = uiOptions or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY == uiOptions
if (isImmersiveModeEnabled)
{
Log.i("PosterViewActivity", "Turning immersive mode mode off. ")
}
else
{
Log.i("PosterViewActivity", "Turning immersive mode mode on.")
}
if (Build.VERSION.SDK_INT >= 14)
{
newUiOptions = newUiOptions xor View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}
// Status bar hiding: Backwards compatible to Jellybean
if (Build.VERSION.SDK_INT >= 16)
{
newUiOptions = newUiOptions xor View.SYSTEM_UI_FLAG_FULLSCREEN
}
if (Build.VERSION.SDK_INT >= 18)
{
newUiOptions = newUiOptions xor View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
window.decorView.systemUiVisibility = newUiOptions
}
}
However, when I press the back button on the device instead of returning to Activity A. The status and navigation buttons are displayed and hidden and it takes 4 presses of the back button to return to Activity A. I have checked the Back stack and two activities are in the stack namely A and B
The code from Activity A calling Activity B:
if(promoList.size >0)
{
noPromoImageView.visibility = View.GONE
noPromoTextView.visibility = View.GONE
promoAdapter = PromotionsAdapter(this, promoList!!)
promoRecyclerView.layoutManager = GridLayoutManager(this, 3)
promoRecyclerView.adapter = promoAdapter
promoRecyclerView.addOnItemTouchListener(
RecyclerTouchListener(applicationContext,
promoRecyclerView,
object : ClickListener
{
override fun onClick(view: View, position: Int)
{
openPosterView(position)
}
override fun onLongClick(view: View?, position: Int)
{
}
}))
}
else
{
noPromoImageView.visibility = View.VISIBLE
noPromoTextView.visibility = View.VISIBLE
}
}
private fun openPosterView(position: Int)
{
val posterImageDetails = displayedRules!![position]!!.imageDetails
if (posterImageDetails.isNotEmpty())
{
var cur = -1
for ((pos, item) in posterImageDetails.withIndex())
{
val key = item.containsValue("Fullscreen")
if (key)
{
cur = pos
}
}
var imagesLink: Map<String, String>
if (cur > -1)
{
imagesLink = posterImageDetails[cur]
var imageUrl = imagesLink?.get("ImageUrl")
if (imageUrl!!.isNotEmpty())
{
var intent = Intent(this, PosterViewActivity::class.java)
var bundle = Bundle()
bundle.putString(ARG_IMAGE_URL, imageUrl)
intent.putExtras(bundle)
this.startActivity(intent)
}
}
}
}
internal class RecyclerTouchListener(context: Context, recyclerView: RecyclerView, private val clickListener: ClickListener) : RecyclerView.OnItemTouchListener
{
private val gestureDetector: GestureDetector
init
{
gestureDetector = GestureDetector(
context,
object : GestureDetector.SimpleOnGestureListener()
{
override fun onSingleTapUp(e: MotionEvent): Boolean
{
return true
}
override fun onLongPress(e: MotionEvent)
{
val child = recyclerView.findChildViewUnder(e.x, e.y)
if (child != null && clickListener != null)
{
clickListener.onLongClick(child, recyclerView.getChildPosition(child))
}
}
})
}
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean
{
val child = rv.findChildViewUnder(e.x, e.y)
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e))
{
clickListener.onClick(child, rv.getChildPosition(child))
}
return false
}
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent)
{
}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean)
{
}
}
In my opinion Activity A's clickListener is wrong. The count of items can be 4 and when the item is clicked, the item's onClickListener can call activity B 4 times.
As suggested by Farid by extending the recyclerview's ontouchlistener it added more complexity. The simplest solution, which I initially had and moved away from was to add an OnClick listener in the Adapter
inner class PromoViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener
{
override fun onClick(v: View?)
{
openPosterView(adapterPosition)
}
private val description: JustifiedTextView
private val duration: AppCompatTextView
private val startDate: AppCompatTextView
private val endDate: AppCompatTextView
private val imageViewCompact: AppCompatImageView
init
{
description = itemView.findViewById(R.id.textViewProductOnPromo)
duration = itemView.findViewById(R.id.textViewPromoDates)
imageViewCompact = itemView.findViewById(R.id.imageViewCompact)
startDate = itemView.findViewById(R.id.textViewStartDate)
endDate = itemView.findViewById(R.id.textViewEndDate)
view.setOnClickListener(this)
}
}