fai*_*gir 19 android android-layout android-fragments kotlin
这个问题已经被问过几次了,但我们现在是 2020 年,有没有人找到一个很好的可用解决方案呢?
我希望能够使用底部导航控件进行导航,而无需在每次选择片段时刷新它们。这是我目前所拥有的:
导航/main.xml:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
app:startDestination="@id/home">
<fragment
android:id="@+id/home"
android:name="com.org.ftech.fragment.HomeFragment"
android:label="@string/app_name"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/news"
android:name="com.org.ftech.fragment.NewsFragment"
android:label="News"
tools:layout="@layout/fragment_news"/>
<fragment
android:id="@+id/markets"
android:name="com.org.ftech.fragment.MarketsFragment"
android:label="Markets"
tools:layout="@layout/fragment_markets"/>
<fragment
android:id="@+id/explore"
android:name="com.org.ftech.ExploreFragment"
android:label="Explore"
tools:layout="@layout/fragment_explore"/>
</navigation>
Run Code Online (Sandbox Code Playgroud)
活动邮件.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- Use DrawerLayout as root container for activity -->
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/main" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemIconTint="@color/nav"
app:itemTextColor="@color/nav"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/main">
</com.google.android.material.bottomnavigation.BottomNavigationView>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
app:menu="@menu/main"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/navigationView"
android:layout_gravity="start">
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>
Run Code Online (Sandbox Code Playgroud)
主活动.kt:
class MainActivity : AppCompatActivity() {
private var drawerLayout: DrawerLayout? = null
private var navigationView: NavigationView? = null
private var bottomNavigationView: BottomNavigationView? = null
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
drawerLayout = findViewById(R.id.drawer_layout)
navigationView = findViewById(R.id.navigationView)
bottomNavigationView = findViewById(R.id.bottomNavigationView)
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(setOf(R.id.markets, R.id.explore, R.id.news, R.id.home), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
findViewById<NavigationView>(R.id.navigationView)
.setupWithNavController(navController)
findViewById<BottomNavigationView>(R.id.bottomNavigationView)
.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.search) {
startActivity(Intent(applicationContext, SearchableActivity::class.java))
}
return super.onOptionsItemSelected(item)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.options_menu, menu)
return super.onCreateOptionsMenu(menu)
}
}
Run Code Online (Sandbox Code Playgroud)
在片段中,我对我的服务进行了几次调用以获取 中的数据onCreateView,当恢复片段时,我假设这些调用将不再被执行,并且片段的状态应该被保留。
bas*_*nce 11
Kotlin 2020 谷歌推荐的解决方案
许多这些解决方案在主活动中调用 Fragment 构造函数。但是,按照 Google 推荐的模式,这不是必需的。
设置导航图选项卡
首先为目录下的每个选项卡创建一个导航图xml res/navigation。
文件名:tab0.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tab0"
app:startDestination="@id/fragmentA"
tools:ignore="UnusedNavigation">
<fragment
android:id="@+id/fragmentA"
android:label="@string/fragment_A_title"
android:name="com.app.subdomain.fragA"
>
</fragment>
</navigation>
Run Code Online (Sandbox Code Playgroud)
对其他选项卡重复上述模板。重要的所有片段和导航图都有一个 id(例如@+id/tab0、@+id/fragmentA)。
设置底部导航视图
确保导航 ID 与底部菜单 xml 中指定的相同。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/fragment_A_title"
android:id="@+id/tab0"
android:icon="@drawable/ic_baseline_book_24"/>
<item android:title="@string/fragment_B_title"
android:id="@+id/tab1"
android:icon="@drawable/ic_baseline_add_alert_24"/>
<item android:title="@string/fragment_C_title"
android:id="@+id/tab2"
android:icon="@drawable/ic_baseline_book_24"/>
<item android:title="@string/fragment_D_title"
android:id="@+id/tab3"
android:icon="@drawable/ic_baseline_more_horiz_24"/>
</menu>
Run Code Online (Sandbox Code Playgroud)
设置活动主 XML
确保FragmentContainerView正在使用并且不<fragment设置 app:navGraph 属性。这将在稍后的代码中设置
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/main_toolbar"
/>
Run Code Online (Sandbox Code Playgroud)
主要活动 XML
将以下代码复制到您的主要活动 Kotlin 文件中,并setupBottomNavigationBar在 OnCreateView 中调用。确保您使用 navGraphIdsR.navigation.whatever而不是R.id.whatever
private lateinit var currentNavController: LiveData<NavController>
private fun setupBottomNavigationBar() {
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
val navGraphIds = listOf(R.navigation.tab0, R.navigation.tab1, R.navigation.tab2, R.navigation.tab3)
val controller = bottomNavigationView.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = supportFragmentManager,
containerId = R.id.fragmentContainerView,
intent = intent
)
controller.observe(this, { navController ->
val toolbar = findViewById<Toolbar>(R.id.main_toolbar)
val appBarConfiguration = AppBarConfiguration(navGraphIds.toSet())
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
setSupportActionBar(toolbar)
})
currentNavController = controller
}
override fun onSupportNavigateUp(): Boolean {
return currentNavController?.value?.navigateUp() ?: false
}
Run Code Online (Sandbox Code Playgroud)
复制 NavigationExtensions.kt 文件
将以下文件复制到您的代码库
[编辑] 上面的链接已损坏。在分叉仓库中找到它
来源
Mah*_*Raj 10
在同一导航项上多次单击时停止刷新的简单解决方案可能是
binding.navView.setOnNavigationItemSelectedListener { item ->
if(item.itemId != binding.navView.selectedItemId)
NavigationUI.onNavDestinationSelected(item, navController)
true
}
Run Code Online (Sandbox Code Playgroud)
其中binding.navView是用于参考BottomNavigationView使用Android数据绑定。
尝试这个:
public class MainActivity extends AppCompatActivity {
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new DashboardFragment();
final Fragment fragment3 = new NotificationsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
fm.beginTransaction().add(R.id.main_container, fragment3, "3").hide(fragment3).commit();
fm.beginTransaction().add(R.id.main_container, fragment2, "2").hide(fragment2).commit();
fm.beginTransaction().add(R.id.main_container,fragment1, "1").commit();
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fm.beginTransaction().hide(active).show(fragment1).commit();
active = fragment1;
return true;
case R.id.navigation_dashboard:
fm.beginTransaction().hide(active).show(fragment2).commit();
active = fragment2;
return true;
case R.id.navigation_notifications:
fm.beginTransaction().hide(active).show(fragment3).commit();
active = fragment3;
return true;
}
return false;
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
Run Code Online (Sandbox Code Playgroud)
或者你可以按照谷歌推荐的解决方案:谷歌链接
如果您使用的是 Jetpack,解决此问题的最简单方法是使用 ViewModel
每次从另一个片段转到一个片段时,您必须保存所有有价值的数据,而不是进行不必要的数据库加载或网络调用。
UI controllers such as activities and fragments are primarily intended to display UI data, react to user actions, or handle operating system communication, such as permission requests.
这是我们使用时 ViewModels
ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance.
因此,如果重新创建片段,您的所有数据将立即存在,而不是再次调用数据库或网络。重要的是要知道,如果持有 的活动或片段ViewModel被重新创建,您将收到ViewModel之前创建的相同实例。
但是在这种情况下,您必须独立地指定 ViewModel 具有活动范围而不是片段范围,如果您为所有片段使用共享的 ViewModel,或者为每个片段使用不同的 ViewModel。
这里也是一个使用LiveData的小例子:
//Using KTX
val model by activityViewModels<MyViewModel>()
model.getData().observe(viewLifecycleOwner, Observer<DataModel>{ data ->
// update UI
})
//Not using KTX
val model by lazy {ViewModelProvider(activity as ViewModelStoreOwner)[MyViewModel::class.java]}
model.getData().observe(viewLifecycleOwner, Observer<DataModel>{ data ->
// update UI
})
Run Code Online (Sandbox Code Playgroud)
就是这样!Google 正在积极致力于为底部选项卡导航提供多个后退堆栈支持,并声称它将Navigation 2.4.0按照此处和此问题跟踪器的说明到达,如果您愿意和/或您的问题与多个后退堆栈更相关,您可以查看这些链接
记住片段仍然会被重新创建,通常你不会改变组件的行为,而是让你的数据适应它们!
我给你留下了一些有用的链接:
如何使用 ViewModel 在片段和活动之间进行通信 - 在 Medium
使用 ViewModels 恢复 UI 状态 - 在 Medium
| 归档时间: |
|
| 查看次数: |
10428 次 |
| 最近记录: |