自从Google I / O宣布新Navigation Component以来,我就一直在使用它,并且也开始尽可能地接受单一活动。
“单一活动”使我可以在视图之间共享ViewModels,以提供令人赞叹的体验,而且如果我不被迫放弃,我真的不想回到多活动性。
但是有一些障碍:单个活动概念的AppBar /主题(状态栏)。
这是我正在设计的一部分:
如您所见,操作栏/状态栏的外观有不同的要求。
CollapsingToolbarLayout
变成标准操作栏离开单个活动会引起的问题列表:
ViewModel
如果可能的话,我想避免这些问题,但是你们如何通过导航组件在单一活动中管理这种情况。有想法吗?
答案 0 :(得分:0)
我也有同样的想法,但是却没有时间做一些实验。因此,这不是解决方案,这是一个实验,我想用另一个视图替换这里的视图,这里是带有包含ImageView的工具栏的工具栏。
因此,我使用“基本活动”模板创建了一个新的应用程序。然后在图形中创建两个目的地,即“首页”和“目的地”。最后,为工具栏创建了另一种布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar 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="?actionBarSize">
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher_round" />
</androidx.appcompat.widget.Toolbar>
activity_main.xml
具有:
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
...
然后在“活动”中,当然取决于设置,但是假设我要使用工具栏设置支持动作栏:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
Toolbar toolbar2 = (Toolbar) getLayoutInflater().inflate(R.layout.destination_toolbar, null);
AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
navController = Navigation.findNavController(this, R.id.nav_host_fragment);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph())
.build();
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
switch (destination.getId()) {
case R.id.homeFragment:
appBarLayout.removeAllViews();
appBarLayout.addView(toolbar);
setSupportActionBar(toolbar);
toolbar.setTitle("Home Fragment");
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
break;
case R.id.destinationFragment:
appBarLayout.removeAllViews();
appBarLayout.addView(toolbar2);
setSupportActionBar(toolbar2);
toolbar2.setTitle("");
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
break;
}
});
}
因此,这可行,随着目的地的增加和新工具栏/其他视图的添加,它变得有些难看。
P.S。如前所述,这只是一个实验,如果有人有更好的解决方案,请发布新答案。
答案 1 :(得分:0)
基于@Rajarshi原始实验,我为此问题提供了可行的解决方案。我不确定是最优雅的,还是有更好的方法。但是经过数小时的研究和调查,这是我找到的最佳解决方案。
分别为工具栏充气并存储它们的引用,以便垃圾收集器不会选择它们。
然后将其按需加载到主AppBarLayout中,该自定义项是为navController定义的自定义OnDestinationChangedListener
示例
这是我在Kotlin中编写的示例。
在您的activity.xml布局上,定义一个空的AppBarLayout。
layout / activity.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
...
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay" />
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
在单独的布局文件中定义应用程序所需的工具栏。
layout / toolbar_defaul.xml
<com.google.android.material.appbar.MaterialToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/default_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/menu_default"
app:popupTheme="@style/AppTheme.PopupOverlay" />
layout / toolbar2.xml
<com.google.android.material.appbar.MaterialToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar2"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/menu2"
app:popupTheme="@style/AppTheme.PopupOverlay" />
在您的主要活动(也是唯一活动)中,将与AppBar相关的组件声明为类属性,以使垃圾收集器不会拾取它们。
Activity.kt
class Activity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var appBarLayout: AppBarLayout
private lateinit var defaultToolbar: MaterialToolbar
private lateinit var toolbar2: MaterialToolbar
...
最后,在onCreate
方法中,为navController定义一个OnDestinationChangedListener
。使用它可以按需加载每个工具栏。
Activity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ryvod)
// Set up AppBar
appBarLayout = findViewById(R.id.appbar)
appBarConfiguration = AppBarConfiguration(setOf(R.id.StartFragment))
defaultToolbar = layoutInflater.inflate(R.layout.toolbar_default, appBarLayout, false) as MaterialToolbar
toolbar2 = layoutInflater.inflate(R.layout.toolbar2, appBarLayout, false) as MaterialToolbar
val host =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
?: return
val navController = host.navController
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.locationPickerFragment -> {
appBarLayout.removeAllViews()
appBarLayout.addView(toolbar2)
setSupportActionBar(toolbar2)
}
else -> {
appBarLayout.removeAllViews()
appBarLayout.addView(defaultToolbar)
setSupportActionBar(defaultToolbar)
}
}
setupActionBarWithNavController(navController, appBarConfiguration)
}
}
应该可以解决问题
答案 2 :(得分:0)
我前一段时间遇到了这个问题,与您的UX / UI类似:
我的解决方案是针对每种情况使用不同的.xml应用栏,并在每个片段xml中使用<include/>
标签:
<include
android:id="@+id/include"
layout="@layout/default_toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
case 1和case 2的窗口配置是相同的,但是对于半透明的Appbar,窗口配置已更改,请参见case 3。
因此,每次出现/替换片段时,我都必须进行配置更改:
public static void transparentStatusBar(Activity activity, boolean isTransparent, boolean fullscreen) {
if (isTransparent){
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}else {
if (fullscreen){
View decorView = activity.getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
| View.SYSTEM_UI_FLAG_VISIBLE);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
然后在半透明的appbar / status条片段的生命周期中使用此方法:
@Override
public void onResume() {
super.onResume();
UtilApp.transparentStatusBar(requireActivity(), true, true);
}
@Override
public void onStop() {
super.onStop();
UtilApp.transparentStatusBar(requireActivity(), false, false);
}
答案 3 :(得分:0)
如here所述,开发人员文档说
当应用程序中每个目标位置的应用程序栏布局相似时,将顶部应用程序栏添加到活动中的效果很好。但是,如果您的顶部应用程序栏在各个目标之间发生很大变化,请考虑从活动中删除顶部应用程序栏,并在每个目标片段中进行定义。