“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022”
I’m going to build a browser from scratch in 2022. I don’t have a name yet.
Since it is a browser, according to the international principle to achieve a multi-window function
I’m going to use ViewPager+Fragment to do this, but if you think about it, the disadvantage is obvious. Because to ensure that the Fragment does not destroy and rebuild, when there are too many fragments in the ViewPager, it will cause obvious lag
In multiple Windows, each window has its own return stack and each return stack has its own fragmentManager, so a UI-less Fragment is used as a carrier for the return stack
/ * * *@author huangweiliang
*/
class NavHostFragment(var name: String, var windowIndex: Int): Fragment() {
private lateinit var binding: FragmentNavHostBinding
private val TAG: String = "NavHostFragment"
var curSelectFragment: Fragment? = null
var curChildFragmentManager: FragmentManager? = null
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
// Click the return button to process the Fragment of the current stack
requireActivity().onBackPressedDispatcher.addCallback(this.object : OnBackPressedCallback(true) {
override fun handleOnBackPressed(a) {
isEnabled = childFragmentManager.backStackEntryCount > 0
if (isEnabled) childFragmentManager.popBackStackImmediate()
// else requireActivity().onBackPressedDispatcher.onBackPressed()
else requireActivity().finish()
}
})
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? {
val view = inflater.inflate(R.layout.fragment_nav_host, container, false)
binding = FragmentNavHostBinding.bind(view)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
super.onViewCreated(view, savedInstanceState)
var tag = name
// Create the first page of the window, i.e. the home page Fragment
if (childFragmentManager.findFragmentByTag(tag) == null) {
// childFragmentManager is used here
childFragmentManager.commitNow {
val multiChildFragment = MultiChildFragment(name, 1.this@NavHostFragment)
add(R.id.content, multiChildFragment, tag) // Replace cannot be used, otherwise it will be rebuilt each time you return
}
}
Log.i(TAG, "$name: onViewCreated")}override fun onStart(a) {
super.onStart()
Log.i(TAG, "$name: onStart")}override fun onResume(a) {
super.onResume()
Log.i(TAG, "$name: onResume")}override fun onPause(a) {
super.onPause()
Log.i(TAG, "$name: onPause")}override fun onDestroy(a) {
super.onDestroy()
Log.i(TAG, "$name: onDestroy")}}Copy the code
FragmentNavHostBinding
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.fragment.app.FragmentContainerView>
Copy the code
Put an EditView on the search page for now
<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">
<EditText
android:id="@+id/ed_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="166dp"
android:layout_marginEnd="16dp"
android:padding="12dp"
android:background="@drawable/gray_rounded_shape"
android:drawableLeft="@drawable/ic_search_gray_24dp"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
/ * * *@author huangweiliang
* @date2021/12/9 * Multi-window, showing each Fragment */
class MultiChildFragment(var name: String, var depth: Int.var hostFragment: Fragment) : Fragment() {
private lateinit var binding: FragmentMultiChildBinding
val TAG = "MultiChildFragment"
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? {
Log.i(TAG, "$name-$depth: onCreateView")
val view = inflater.inflate(R.layout.fragment_multi_child, container, false)
binding = FragmentMultiChildBinding.bind(view)
// Do a simple pass-through to display the current page
binding.edSearch.text.append("$name-$depth")
init(a)return view
}
private fun init(a){}override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
super.onViewCreated(view, savedInstanceState)
Log.i(TAG, "$name-$depth: onViewCreated")}}Copy the code
Write a MultiStackParentFragment containing multiple return stacks
class MultiStackParentFragment: Fragment() {
companion object {
fun newInstance(a) = MultiStackParentFragment()
}
private lateinit var binding: FragmentMultiStackParentBinding
private var windowNum: Int = 0
/** * The current window Index */
private var curWindowIndex: Int = 0
/** * Records all window objects created */
private val mStackList = ArrayList<NavHostFragment>()
/** * returns the stack order, the storage returns the stack ID */
private val mOrderStack = ArrayDeque<Int> ()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_multi_stack_parent, container, false)
binding = FragmentMultiStackParentBinding.bind(view)
init(a)return view
}
private fun init(a){}}Copy the code
Add Windows inside. ChildFragmentManager manages all child Fragments that have that Fragment as their parent
private fun addWindow(a) {
childFragmentManager.commitNow {
//NavHostFragment represents a window object
val navHostFragment = NavHostFragment("Window${++windowNum}", windowNum)
curWindowIndex = windowNum
mStackList.add(navHostFragment)
add(R.id.content_fragment, navHostFragment) // Add window
}
transWindowIndex(curWindowIndex)
}
Copy the code
rendering