Skip to content

Commit b66cbf0

Browse files
committed
feat(ui): implement adaptive navigation patterns for toolbars and template list
1 parent 5a56a46 commit b66cbf0

File tree

5 files changed

+78
-197
lines changed

5 files changed

+78
-197
lines changed

app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ import android.view.MotionEvent
4141
import android.view.View
4242
import android.view.ViewGroup
4343
import android.view.ViewTreeObserver.OnGlobalLayoutListener
44-
import android.widget.LinearLayout
45-
import android.widget.TextView
4644
import androidx.activity.OnBackPressedCallback
4745
import androidx.activity.result.ActivityResult
4846
import androidx.activity.result.ActivityResultLauncher
@@ -596,7 +594,6 @@ abstract class BaseEditorActivity :
596594
}
597595

598596
setupToolbar()
599-
syncProjectToolbarRowForOrientation(resources.configuration.orientation)
600597
setupDrawers()
601598
content.tabs.addOnTabSelectedListener(this)
602599

@@ -634,7 +631,6 @@ abstract class BaseEditorActivity :
634631

635632
override fun onConfigurationChanged(newConfig: Configuration) {
636633
super.onConfigurationChanged(newConfig)
637-
syncProjectToolbarRowForOrientation(newConfig.orientation)
638634
}
639635

640636
private fun setupToolbar() {
@@ -688,90 +684,6 @@ abstract class BaseEditorActivity :
688684
}
689685
}
690686

691-
private fun syncProjectToolbarRowForOrientation(currentOrientation: Int) {
692-
val appBar = content.editorAppBarLayout
693-
val titleToolbar = content.titleToolbar
694-
val actionsToolbar = content.projectActionsToolbar
695-
696-
val titleParent = titleToolbar.parent as? ViewGroup ?: return
697-
val actionsParent = actionsToolbar.parent as? ViewGroup ?: return
698-
if (titleParent != actionsParent) return
699-
700-
val isLandscape = currentOrientation == Configuration.ORIENTATION_LANDSCAPE
701-
702-
if (isLandscape && titleParent === appBar) {
703-
val insertAt =
704-
minOf(
705-
appBar.indexOfChild(titleToolbar),
706-
appBar.indexOfChild(actionsToolbar),
707-
).coerceAtLeast(0)
708-
val row =
709-
LinearLayout(this).apply {
710-
orientation = LinearLayout.HORIZONTAL
711-
gravity = Gravity.CENTER_VERTICAL
712-
layoutParams =
713-
com.google.android.material.appbar.AppBarLayout.LayoutParams(
714-
ViewGroup.LayoutParams.MATCH_PARENT,
715-
ViewGroup.LayoutParams.WRAP_CONTENT,
716-
)
717-
}
718-
719-
appBar.removeView(titleToolbar)
720-
appBar.removeView(actionsToolbar)
721-
722-
titleToolbar.layoutParams =
723-
LinearLayout.LayoutParams(
724-
0,
725-
ViewGroup.LayoutParams.WRAP_CONTENT,
726-
1f,
727-
)
728-
actionsToolbar.layoutParams =
729-
LinearLayout
730-
.LayoutParams(
731-
ViewGroup.LayoutParams.WRAP_CONTENT,
732-
ViewGroup.LayoutParams.WRAP_CONTENT,
733-
).apply { marginEnd = SizeUtils.dp2px(8f) }
734-
735-
content.root.findViewById<TextView>(R.id.title_text)?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
736-
marginEnd = SizeUtils.dp2px(8f)
737-
}
738-
739-
row.addView(titleToolbar)
740-
row.addView(actionsToolbar)
741-
appBar.addView(row, insertAt)
742-
return
743-
}
744-
745-
if (!isLandscape && titleParent is LinearLayout && titleParent.parent === appBar) {
746-
val row = titleParent
747-
val insertAt = appBar.indexOfChild(row).coerceAtLeast(0)
748-
row.removeView(titleToolbar)
749-
row.removeView(actionsToolbar)
750-
appBar.removeView(row)
751-
752-
titleToolbar.layoutParams =
753-
com.google.android.material.appbar.AppBarLayout.LayoutParams(
754-
ViewGroup.LayoutParams.MATCH_PARENT,
755-
ViewGroup.LayoutParams.WRAP_CONTENT,
756-
)
757-
actionsToolbar.layoutParams =
758-
com.google.android.material.appbar.AppBarLayout
759-
.LayoutParams(
760-
ViewGroup.LayoutParams.MATCH_PARENT,
761-
ViewGroup.LayoutParams.WRAP_CONTENT,
762-
).apply {
763-
topMargin = SizeUtils.dp2px(4f)
764-
}
765-
766-
content.root.findViewById<TextView>(R.id.title_text)?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
767-
marginEnd = SizeUtils.dp2px(16f)
768-
}
769-
770-
appBar.addView(titleToolbar, insertAt)
771-
appBar.addView(actionsToolbar, insertAt + 1)
772-
}
773-
}
774-
775687
private fun onSwipeRevealDragProgress(progress: Float) {
776688
_binding?.apply {
777689
contentCard.progress = progress

app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package com.itsaky.androidide.adapters
2020
import android.view.LayoutInflater
2121
import android.view.View
2222
import android.view.ViewGroup
23-
import androidx.recyclerview.widget.DiffUtil
2423
import androidx.recyclerview.widget.RecyclerView
2524
import com.blankj.utilcode.util.ConvertUtils
2625
import com.google.android.material.shape.CornerFamily
@@ -81,39 +80,11 @@ class TemplateListAdapter(
8180
}
8281

8382
root.setOnLongClickListener {
84-
template.tooltipTag?.let { tag ->
83+
template.tooltipTag?.let { _ ->
8584
onLongClick?.invoke(template, it)
8685
}
8786
true // Consume the event
8887
}
8988
}
9089
}
91-
92-
internal fun fillDiff(extras: Int) {
93-
val count = itemCount
94-
for (i in 1..extras) {
95-
templates.add(Template.EMPTY)
96-
}
97-
98-
val diff =
99-
DiffUtil.calculateDiff(
100-
object : DiffUtil.Callback() {
101-
override fun getOldListSize(): Int = count
102-
103-
override fun getNewListSize(): Int = count + extras
104-
105-
override fun areItemsTheSame(
106-
oldItemPosition: Int,
107-
newItemPosition: Int,
108-
): Boolean = newItemPosition < count && oldItemPosition == newItemPosition
109-
110-
override fun areContentsTheSame(
111-
oldItemPosition: Int,
112-
newItemPosition: Int,
113-
): Boolean = areItemsTheSame(oldItemPosition, newItemPosition)
114-
},
115-
)
116-
117-
diff.dispatchUpdatesTo(this)
118-
}
11990
}

app/src/main/java/com/itsaky/androidide/fragments/TemplateListFragment.kt

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,19 @@ package com.itsaky.androidide.fragments
1919

2020
import android.os.Bundle
2121
import android.view.View
22-
import android.view.ViewTreeObserver.OnGlobalLayoutListener
22+
import android.content.res.Configuration
2323
import androidx.core.view.ViewCompat
2424
import androidx.core.view.WindowInsetsCompat
2525
import androidx.core.view.updatePadding
2626
import androidx.fragment.app.viewModels
27-
import com.google.android.flexbox.FlexDirection
28-
import com.google.android.flexbox.FlexboxLayoutManager
29-
import com.google.android.flexbox.JustifyContent
27+
import androidx.recyclerview.widget.GridLayoutManager
3028
import com.itsaky.androidide.R
3129
import com.itsaky.androidide.adapters.TemplateListAdapter
3230
import com.itsaky.androidide.databinding.FragmentTemplateListBinding
3331
import com.itsaky.androidide.idetooltips.TooltipManager
3432
import com.itsaky.androidide.idetooltips.TooltipTag.EXIT_TO_MAIN
3533
import com.itsaky.androidide.templates.ITemplateProvider
3634
import com.itsaky.androidide.templates.ProjectTemplate
37-
import com.itsaky.androidide.utils.FlexboxUtils
3835
import com.itsaky.androidide.viewmodel.MainViewModel
3936
import org.slf4j.LoggerFactory
4037

@@ -49,9 +46,6 @@ class TemplateListFragment :
4946
FragmentTemplateListBinding::bind,
5047
) {
5148
private var adapter: TemplateListAdapter? = null
52-
private var layoutManager: FlexboxLayoutManager? = null
53-
54-
private lateinit var globalLayoutListener: OnGlobalLayoutListener
5549

5650
private val viewModel by viewModels<MainViewModel>(ownerProducer = { requireActivity() })
5751

@@ -65,41 +59,31 @@ class TemplateListFragment :
6559
) {
6660
super.onViewCreated(view, savedInstanceState)
6761

68-
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
69-
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
70-
v.updatePadding(bottom = insets.bottom)
71-
windowInsets
72-
}
73-
74-
layoutManager = FlexboxLayoutManager(requireContext(), FlexDirection.ROW)
75-
layoutManager!!.justifyContent = JustifyContent.SPACE_EVENLY
76-
77-
binding.list.layoutManager = layoutManager
78-
79-
// This makes sure that the items are evenly distributed in the list
80-
// and the last row is always aligned to the start
81-
globalLayoutListener =
82-
FlexboxUtils.createGlobalLayoutListenerToDistributeFlexboxItemsEvenly(
83-
{ adapter },
84-
{ layoutManager },
85-
) { adapter, diff ->
86-
adapter.fillDiff(diff)
87-
}
62+
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
63+
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
64+
v.updatePadding(bottom = insets.bottom)
65+
windowInsets
66+
}
67+
68+
val screenWidthDp = resources.configuration.screenWidthDp
69+
val minItemWidthDp = 160
70+
val initialSpans = (screenWidthDp / minItemWidthDp).coerceIn(1, 4)
8871

89-
binding.list.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
72+
val gridLayoutManager = GridLayoutManager(requireContext(), initialSpans)
73+
binding.list.layoutManager = gridLayoutManager
9074

9175
binding.exitButton.setOnClickListener {
9276
viewModel.setScreen(MainViewModel.SCREEN_MAIN)
9377
}
9478

95-
binding.exitButton.setOnLongClickListener {
96-
TooltipManager.showIdeCategoryTooltip(
97-
context = requireContext(),
98-
anchorView = binding.root,
99-
tag = EXIT_TO_MAIN,
100-
)
101-
true
102-
}
79+
binding.exitButton.setOnLongClickListener {
80+
TooltipManager.showIdeCategoryTooltip(
81+
context = requireContext(),
82+
anchorView = binding.root,
83+
tag = EXIT_TO_MAIN,
84+
)
85+
true
86+
}
10387

10488
viewModel.currentScreen.observe(viewLifecycleOwner) { current ->
10589
if (current == MainViewModel.SCREEN_TEMPLATE_DETAILS) {
@@ -110,8 +94,16 @@ class TemplateListFragment :
11094
}
11195
}
11296

97+
override fun onConfigurationChanged(newConfig: Configuration) {
98+
super.onConfigurationChanged(newConfig)
99+
100+
val minItemWidthDp = 160
101+
val optimalSpans = (newConfig.screenWidthDp / minItemWidthDp).coerceIn(1, 4)
102+
103+
(binding.list.layoutManager as? GridLayoutManager)?.spanCount = optimalSpans
104+
}
105+
113106
override fun onDestroyView() {
114-
binding.list.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
115107
super.onDestroyView()
116108
}
117109

@@ -138,16 +130,14 @@ class TemplateListFragment :
138130
},
139131
onLongClick = { template, itemView ->
140132
template.tooltipTag?.let { tag ->
141-
TooltipManager.showIdeCategoryTooltip(
142-
context = requireContext(),
143-
anchorView = itemView,
144-
tag = tag
145-
)
146-
}
147-
},
133+
TooltipManager.showIdeCategoryTooltip(
134+
context = requireContext(),
135+
anchorView = itemView,
136+
tag = tag
137+
)
138+
}
139+
},
148140
)
149-
150141
binding.list.adapter = adapter
151142
}
152-
153143
}

app/src/main/res/layout/content_editor.xml

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,40 +31,48 @@
3131
android:fitsSystemWindows="false"
3232
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
3333

34-
<com.google.android.material.appbar.MaterialToolbar
35-
android:id="@+id/title_toolbar"
34+
<com.google.android.flexbox.FlexboxLayout
3635
android:layout_width="match_parent"
3736
android:layout_height="wrap_content"
38-
android:minHeight="0dp"
39-
android:padding="0dp"
40-
app:contentInsetStartWithNavigation="0dp"
41-
app:contentInsetStart="0dp"
42-
app:contentInsetEnd="0dp">
43-
44-
<TextView
45-
android:id="@+id/title_text"
46-
style="@style/TextAppearance.Material3.TitleMedium"
47-
android:layout_width="match_parent"
37+
app:flexWrap="wrap"
38+
app:alignItems="center">
39+
<com.google.android.material.appbar.MaterialToolbar
40+
android:id="@+id/title_toolbar"
41+
android:layout_width="wrap_content"
42+
android:layout_height="wrap_content"
43+
app:layout_flexGrow="1"
44+
android:minHeight="0dp"
45+
android:padding="0dp"
46+
app:contentInsetStartWithNavigation="0dp"
47+
app:contentInsetStart="0dp"
48+
app:contentInsetEnd="0dp">
49+
50+
<TextView
51+
android:id="@+id/title_text"
52+
style="@style/TextAppearance.Material3.TitleMedium"
53+
android:layout_width="match_parent"
54+
android:layout_height="wrap_content"
55+
android:layout_marginStart="16dp"
56+
android:layout_marginEnd="16dp"
57+
android:paddingTop="0dp"
58+
android:paddingBottom="0dp"
59+
android:gravity="start|center_vertical"
60+
android:maxLines="1"
61+
android:ellipsize="end" />
62+
63+
</com.google.android.material.appbar.MaterialToolbar>
64+
65+
<com.itsaky.androidide.ui.ProjectActionsToolbar
66+
android:id="@+id/project_actions_toolbar"
67+
android:layout_width="wrap_content"
4868
android:layout_height="wrap_content"
49-
android:layout_marginStart="16dp"
50-
android:layout_marginEnd="16dp"
69+
app:layout_flexShrink="0"
70+
android:layout_marginTop="4dp"
71+
android:layout_marginBottom="0dp"
72+
android:minHeight="0dp"
5173
android:paddingTop="0dp"
52-
android:paddingBottom="0dp"
53-
android:gravity="start|center_vertical"
54-
android:maxLines="1"
55-
android:ellipsize="end" />
56-
57-
</com.google.android.material.appbar.MaterialToolbar>
58-
59-
<com.itsaky.androidide.ui.ProjectActionsToolbar
60-
android:id="@+id/project_actions_toolbar"
61-
android:layout_width="match_parent"
62-
android:layout_height="wrap_content"
63-
android:layout_marginTop="4dp"
64-
android:layout_marginBottom="0dp"
65-
android:minHeight="0dp"
66-
android:paddingTop="0dp"
67-
android:paddingBottom="0dp" />
74+
android:paddingBottom="0dp" />
75+
</com.google.android.flexbox.FlexboxLayout>
6876

6977
<com.google.android.material.progressindicator.LinearProgressIndicator
7078
android:id="@+id/progress_indicator"

app/src/main/res/layout/layout_template_list_item.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
xmlns:android="http://schemas.android.com/apk/res/android"
44
xmlns:app="http://schemas.android.com/apk/res-auto"
55
xmlns:tools="http://schemas.android.com/tools"
6-
android:layout_width="wrap_content"
6+
android:layout_width="match_parent"
77
android:layout_height="wrap_content"
88
android:layout_margin="8dp"
99
android:background="?attr/selectableItemBackground"
@@ -14,7 +14,7 @@
1414
app:strokeWidth="0dp">
1515

1616
<LinearLayout
17-
android:layout_width="wrap_content"
17+
android:layout_width="match_parent"
1818
android:layout_height="wrap_content"
1919
android:background="?attr/colorSurface"
2020
android:orientation="vertical"

0 commit comments

Comments
 (0)