It is an architectural infrastructure that establishes a relationship between activity and fragment in android, and also provides transitions and management between fragments.
- Existing fragment initialization according to fragment type
- Switching between fragments by command type
- Base class that runs and organizes the whole process: MyFragmentManager.kt
- Custom backstack management
- Interface for fragment interaction
First of all, view binding, which is recommended by Google, was used in the project.
In the build.gradle file, it is stated that we will use view binding.
buildFeatures {
viewBinding true
}Binding is done in Activity and Fragment files
privatelateinitvar binding:ActivityMainBindingoverridefunonCreate(savedInstanceState:Bundle?) {
super.onCreate(savedInstanceState)
binding =ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
The application starts with MainActivity. It is used to hold frame layout fragments in activity_main.xml.
All fragments must implement the MyFragment class.
Types of all fragments are specified with enum class.
OnFragmentInteractionListener interface is used to switch between fragments.
When the application is first opened, it starts with MainActivity. The first fragment is displayed by calling the initFragment method under OnCreate. The initFragment method sets the current fragment using the myFragmentManager base class.
privatelateinitvar binding:ActivityMainBindingprivateval myFragmentManager =MyFragmentManager(this, supportFragmentManager)
overridefunonCreate(savedInstanceState:Bundle?) {
super.onCreate(savedInstanceState)
binding =ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
initFragment()
}
privatefuninitFragment(){
myFragmentManager.setCurrentFragmentType(MyFragmentType.HOME_FRAGMENT, null, false)
}The fragment manager determines the current fragment with the setCurrentFragmentType method.
funsetCurrentFragmentType(fragmentType:MyFragmentType, arguments:Bundle?, addToBackStack:Boolean) {
if (currentFragment?.fragmentType === fragmentType) returnif (!addToBackStack) clearBackStack()
currentFragment =when (fragmentType) {
MyFragmentType.MY_FRAGMENT_TYPE_A->FragmentA()
MyFragmentType.MY_FRAGMENT_TYPE_B->FragmentB()
MyFragmentType.HOME_FRAGMENT->HomeFragment()
}
currentFragment?.arguments = arguments
if (addToBackStack){
myFragmentStack.add(currentFragment)
}else{
baseFragment = currentFragment
}
val fragmentTransaction = fragmentManager.beginTransaction()
if (addToBackStack){
fragmentTransaction.replace(R.id.activity_main_fragment_container, currentFragment asFragment)
.addToBackStack(fragmentType.toString())
}else{
fragmentTransaction.replace(R.id.activity_main_fragment_container, currentFragment asFragment)
}
try {
fragmentTransaction.commit()
}catch (e:Exception){
e.printStackTrace()
}
}The setCurrentFragmentType method takes an argument of type MyFragmentType in order to traverse the fragment. In addition, if we want to send an argument (Bundle) while switching between fragments, we accept it as a parameter and if we want to add the relevant fragment to bacstack, we need to send the addToBackStack parameter as true.
currentFragment and baseFragment are objects of type MyFragment. myFragmentStack is a Stack structure containing MyFragment.
privatevar currentFragment:MyFragment?=nullprivatevar baseFragment:MyFragment?=nullprivateval myFragmentStack =Stack<MyFragment>()myFragment is the class that determines the structure of all fragments.
abstractclassMyFragment : Fragment() {
abstractval fragmentType:MyFragmentType
}Every fragment must have a type. The setCurrentFragmentType method determines the current fragment according to this type. The type here is specified with the enum class. There should be as many types as the number of fragments.
enumclassMyFragmentType {
HOME_FRAGMENT,
MY_FRAGMENT_TYPE_A,
MY_FRAGMENT_TYPE_B
}The scenario for displaying homeFragment is as above. Since the addToBackStack parameter is false in the initFragment, the homeFragment is not added to the backstack and the application exits when the back button is pressed.
Thanks to the buttons in homeFragment, transitions can be made between fragments. The OnFragmentInteraction interface runs the setCurrentFragmentType method in the fragment manager according to the command type.
privatevar mListener:OnFragmentInteractionListener?=nulloverridefunonViewCreated(view:View, savedInstanceState:Bundle?) {
super.onViewCreated(view, savedInstanceState)
val isUserPushed = arguments?.get("isUserPushedScreen")
Log.e("TEST", "Fragment Home isUserPushed: ${isUserPushed}")
binding.homeFragmentGotoFragmentA.setOnClickListener {
mListener?.onFragmentInteraction(MyFragmentCommand.GO_TO_FRAGMENT_A, null, true)
}
binding.homeFragmentGotoFragmentB.setOnClickListener {
mListener?.onFragmentInteraction(MyFragmentCommand.GO_TO_FRAGMENT_B, null, true)
}
}When the listener is triggered, the onFragmentInteraction method in MainActivity is called and fragment passage is provided.
overridefunonFragmentInteraction(command:MyFragmentCommand, argumentData:Bundle?, addToBackStack:Boolean) {
var argBundle:Bundle?= argumentData
if (argBundle ==null){
argBundle =Bundle()
}
//default arguments
argBundle.putBoolean("isUserPushedScreen", true)
when(command){
MyFragmentCommand.GO_TO_FRAGMENT_A-> {
myFragmentManager.setCurrentFragmentType(MyFragmentType.MY_FRAGMENT_TYPE_A, argBundle, addToBackStack)
}
MyFragmentCommand.GO_TO_FRAGMENT_B-> {
myFragmentManager.setCurrentFragmentType(MyFragmentType.MY_FRAGMENT_TYPE_B, argBundle, addToBackStack)
}
}
}

