該賞金過期3天。這個問題的答案有資格獲得 50聲望獎勵。 Mahmoud Metawee正在尋找規范的答案。
更新
由于onSaveInstanceState&onRestoreInstanceState在關閉應用程式后無法用于存盤/恢??復值,我嘗試使用 dataStore 來解決它,但它不起作用,這是我的嘗試
DataStoreRepository
@ActivityRetainedScoped
public static class DataStoreRepository {
RxDataStore<Preferences> dataStore;
public static Preferences.Key<Integer> CURRENT_DESTINATION =
PreferencesKeys.intKey("CURRENT_DESTINATION");
public final Flowable<Integer> readCurrentDestination;
@Inject
public DataStoreRepository(@ApplicationContext Context context) {
dataStore =
new RxPreferenceDataStoreBuilder(Objects.requireNonNull(context), /*name=*/ "settings").build();
readCurrentDestination = dataStore.data().map(preferences -> {
if (preferences.get(CURRENT_DESTINATION) != null) {
return preferences.get(CURRENT_DESTINATION);
} else {
return R.id.nav_home;
}
});
}
public void saveCurrentDestination(String keyName, int value){
CURRENT_DESTINATION = PreferencesKeys.intKey(keyName);
dataStore.updateDataAsync(prefsIn -> {
MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
Integer currentKey = prefsIn.get(CURRENT_DESTINATION);
if (currentKey == null) {
saveCurrentDestination(keyName,value);
}
mutablePreferences.set(CURRENT_DESTINATION,
currentKey != null ? value : R.id.nav_home);
return Single.just(mutablePreferences);
}).subscribe();
}
}
在 ViewModel 中讀取并保存
public final MutableLiveData<Integer> currentDestination = new MutableLiveData<>();
@Inject
public PostViewModel(Repository repository, Utils.DataStoreRepository dataStoreRepository) {
this.repository = repository;
getAllItemsFromDataBase = repository.localDataSource.getAllItems();
this.dataStoreRepository = dataStoreRepository;
dataStoreRepository.readCurrentDestination
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new FlowableSubscriber<Integer>() {
@Override
public void onSubscribe(@NonNull Subscription s) {
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
}
@Override
public void onError(Throwable t) {
Log.e(TAG, "onError: " t.getMessage());
}
@Override
public void onComplete() {
}
});
}
public void saveCurrentDestination(int currentDestination) {
dataStoreRepository
.saveCurrentDestination("CURRENT_DESTINATION", currentDestination);
}
最后是 MainActivity
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@SuppressWarnings("unused")
private AppBarConfiguration mAppBarConfiguration;
private NavHostFragment navHostFragment;
private NavController navController;
NavGraph navGraph;
private PostViewModel postViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
postViewModel = new ViewModelProvider(this).get(PostViewModel.class);
setSupportActionBar(binding.appBarMain.toolbar);
mAppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_home, R.id.nav_accessory,
R.id.nav_arcade, R.id.nav_fashion,
R.id.nav_food, R.id.nav_heath,
R.id.nav_lifestyle, R.id.nav_sports, R.id.about)
.setOpenableLayout(binding.drawerLayout)
.build();
navHostFragment = (NavHostFragment)
getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
if(navHostFragment !=null) {
navController = navHostFragment.getNavController();
}
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController);
navGraph = navController.getNavInflater().inflate(R.navigation.mobile_navigation);
postViewModel.currentDestination.observe(this,currentDestination -> {
Log.d(TAG, "currentDestination: " currentDestination);
Toast.makeText(this,"currentDestination" currentDestination,Toast.LENGTH_SHORT).show();
navGraph.setStartDestination(currentDestination);
navController.setGraph(navGraph);
});
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
Log.d(TAG, "addOnDestinationChangedListener: " destination.getId());
postViewModel.saveCurrentDestination(destination.getId());
});
}
@Override
public boolean onSupportNavigateUp() {
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
問題詳細
在這個應用程式,我在抽屜式導航9選單項和片段,我想保存最后打開的片段savedInstanceState或datastore并且在用戶關閉應用程式,然后重新打開它再次顯示上次運行結束片段,但我不知道哪些方法我會用
Navigation.findNavController(activity,nav_graph).navigate();
或者
binding.navView.setNavigationItemSelectedListener(item -> false);
活動_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
android:id="@ id/app_bar_main"
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="@ id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
android:background="@color/color_navigation_list_background"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
activity_main_drawer.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@ id/nav_home"
android:title="@string/home"
android:icon="@drawable/home"
/>
<item
android:id="@ id/nav_accessory"
android:title="@string/accessory"
android:icon="@drawable/necklace"
/>
<item
android:id="@ id/nav_arcade"
android:title="@string/arcade"
android:icon="@drawable/arcade_cabinet"
/>
<item
android:id="@ id/nav_fashion"
android:title="@string/fashion"
android:icon="@drawable/fashion_trend"
/>
<item
android:id="@ id/nav_food"
android:title="@string/food"
android:icon="@drawable/hamburger"
/>
<item
android:id="@ id/nav_heath"
android:title="@string/heath"
android:icon="@drawable/clinic"
/>
<item
android:id="@ id/nav_lifestyle"
android:title="@string/lifestyle"
android:icon="@drawable/yoga"
/>
<item
android:id="@ id/nav_sports"
android:title="@string/sports"
android:icon="@drawable/soccer"
/>
<item
android:id="@ id/nav_favorites"
android:title="@string/favorites_posts"
android:icon="@drawable/ic_favorite"
/>
<item
android:id="@ id/about"
android:title="@string/about"
android:icon="@drawable/about"
/>
</group>
</menu>
導航圖.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/mobile_navigation"
app:startDestination="@id/nav_home">
<fragment
android:id="@ id/nav_home"
android:name="com.blogspot.abtallaldigital.ui.HomeFragment"
android:label="@string/home"
tools:layout="@layout/fragment_home">
<action
android:id="@ id/action_nav_home_to_detailsFragment"
app:destination="@id/detailsFragment"
app:popUpTo="@id/nav_home" />
</fragment>
<fragment
android:id="@ id/nav_accessory"
android:name="com.blogspot.abtallaldigital.ui.AccessoryFragment"
android:label="@string/accessory"
tools:layout="@layout/fragment_accessory" >
<action
android:id="@ id/action_nav_Accessory_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@ id/nav_arcade"
android:name="com.blogspot.abtallaldigital.ui.ArcadeFragment"
android:label="@string/arcade"
tools:layout="@layout/fragment_arcade" >
<action
android:id="@ id/action_nav_Arcade_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@ id/nav_fashion"
android:name="com.blogspot.abtallaldigital.ui.FashionFragment"
android:label="@string/fashion"
tools:layout="@layout/fragment_fashion" >
<action
android:id="@ id/action_nav_Fashion_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@ id/nav_food"
android:name="com.blogspot.abtallaldigital.ui.FoodFragment"
android:label="@string/food"
tools:layout="@layout/food_fragment" >
<action
android:id="@ id/action_nav_Food_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@ id/nav_heath"
android:name="com.blogspot.abtallaldigital.ui.HeathFragment"
android:label="@string/heath"
tools:layout="@layout/heath_fragment" >
<action
android:id="@ id/action_nav_Heath_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@ id/nav_lifestyle"
android:name="com.blogspot.abtallaldigital.ui.LifestyleFragment"
android:label="@string/lifestyle"
tools:layout="@layout/lifestyle_fragment" >
<action
android:id="@ id/action_nav_Lifestyle_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@ id/nav_sports"
android:name="com.blogspot.abtallaldigital.ui.SportsFragment"
android:label="@string/sports"
tools:layout="@layout/sports_fragment" >
<action
android:id="@ id/action_nav_Sports_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
<dialog
android:id="@ id/about"
android:name="com.blogspot.abtallaldigital.ui.AboutFragment"
android:label="about"
tools:layout="@layout/about" />
<fragment
android:id="@ id/detailsFragment"
android:name="com.blogspot.abtallaldigital.ui.DetailsFragment"
android:label="Post details"
tools:layout="@layout/fragment_details" >
<argument
android:name="postItem"
app:argType="com.blogspot.abtallaldigital.pojo.Item" />
</fragment>
<fragment
android:id="@ id/nav_favorites"
android:name="com.blogspot.abtallaldigital.ui.FavoritesFragment"
android:label="Favorites posts"
tools:layout="@layout/fragment_favorites" >
<action
android:id="@ id/action_favoritesFragment_to_detailsFragment"
app:destination="@id/detailsFragment" />
</fragment>
</navigation>
主活動類
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
@SuppressWarnings("unused")
private AppBarConfiguration mAppBarConfiguration;
public static Utils.DataStoreRepository DATA_STORE_REPOSITORY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
mAppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_home, R.id.nav_accessory,
R.id.nav_arcade, R.id.nav_fashion,
R.id.nav_food, R.id.nav_heath,
R.id.nav_lifestyle, R.id.nav_sports, R.id.about)
.setOpenableLayout(binding.drawerLayout)
.build();
NavHostFragment navHostFragment = (NavHostFragment)
getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
assert navHostFragment != null;
NavController navController = navHostFragment.getNavController();
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
uj5u.com熱心網友回復:
使用SharedPreference(遲早SharedPreference會被棄用,下面有一個更新使用DataStore)
onSaveInstanceState&onRestoreInstanceState不能用于在應用程式關閉/關閉后存盤/恢復值。
即使應用程式沒有關閉,您也不能依賴它們來存盤大物件或長時間存盤物件。
取而代之的是,您可以使用它SharedPreference 來存盤在應用程式存在之前映射到最后一個打開片段的值。
在這里,我存盤了一些任意值,因為建議不要存盤應用程式 ID,因為它們會因應用程式啟動而異。因此,您可以存盤任意值并將它們映射到當前應用程式啟動中生成的 ID。
我選擇這些值作為陣列索引:
// Array of fragments
private Integer[] fragments = {
R.id.nav_home,
R.id.nav_accessory,
R.id.nav_arcade,
R.id.nav_fashion,
R.id.nav_food,
R.id.nav_heath,
R.id.nav_lifestyle,
R.id.nav_sports,
R.id.about
};
然后每次啟動應用程式;即在onCreate()方法中,您可以從 中選擇當前索引SharedPreference,然后呼叫graph.setStartDestination():
// Getting the last fragment:
SharedPreferences mSharedPrefs = getSharedPreferences("SHARED_PREFS", MODE_PRIVATE);
int fragIndex = mSharedPrefs.getInt(LAST_FRAGMENT, -1); // The last fragment index
// Check if it's a valid index
if (fragIndex >= 0 && fragIndex < fragments.length) {
// Navigate to this fragment
int currentFragment = fragments[fragIndex];
graph.setStartDestination(currentFragment);
// Change the current navGraph
navController.setGraph(graph);
}
你可以一次目的地使用變更新值sharedPreference注冊OnDestinationChangedListener的navController:
// Listener to the change in fragments, so that we can updated the shared preference
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
int fragmentIndex = Arrays.asList(fragments).indexOf(destination.getId());
SharedPreferences.Editor editor = mSharedPrefs.edit();
editor.putInt(LAST_FRAGMENT, fragmentIndex).apply();
});
將其集成到您的代碼中:
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@SuppressWarnings("unused")
private AppBarConfiguration mAppBarConfiguration;
private NavHostFragment navHostFragment;
private NavController navController;
NavGraph navGraph;
// Array of fragments
private Integer[] fragments = {
R.id.nav_home,
R.id.nav_accessory,
R.id.nav_arcade,
R.id.nav_fashion,
R.id.nav_food,
R.id.nav_heath,
R.id.nav_lifestyle,
R.id.nav_sports,
R.id.about
};
// Key for saving the last fragment in the Shared Preferences
private static final String LAST_FRAGMENT = "LAST_FRAGMENT";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
mAppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_home, R.id.nav_accessory,
R.id.nav_arcade, R.id.nav_fashion,
R.id.nav_food, R.id.nav_heath,
R.id.nav_lifestyle, R.id.nav_sports, R.id.about)
.setOpenableLayout(binding.drawerLayout)
.build();
navHostFragment = (NavHostFragment)
getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
if(navHostFragment !=null) {
navController = navHostFragment.getNavController();
}
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(binding.navView, navController);
navGraph = navController.getNavInflater().inflate(R.navigation.mobile_navigation);
// Getting the last fragment:
SharedPreferences mSharedPrefs = getSharedPreferences("SHARED_PREFS", MODE_PRIVATE);
int fragIndex = mSharedPrefs.getInt(LAST_FRAGMENT, -1); // The last fragment index
// Check if it's a valid index
if (fragIndex >= 0 && fragIndex < fragments.length) {
// Navigate to this fragment
int currentFragment = fragments[fragIndex];
graph.setStartDestination(currentFragment);
// Change the current navGraph
navController.setGraph(graph);
}
// Listener to the change in fragments, so that we can updated the shared preference
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
int fragmentIndex = Arrays.asList(fragments).indexOf(destination.getId());
SharedPreferences.Editor editor = mSharedPrefs.edit();
editor.putInt(LAST_FRAGMENT, fragmentIndex).apply();
});
}
@Override
public boolean onSupportNavigateUp() {
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
更新:使用資料存盤
Since I migrated from sharedpreferences to dataStore in this project, I tried to do the same as your solution but it doesn't work, I'll post my try and you can look at it to see what's wrong, then you can edit your answer with dataStore soultion
So, I am going to use the same approach but with the DataStore (same fragment array, store indices instead of fragment destination IDs).
So, you need to add the array of fragment IDs so that it can be read in the DataStore process:
// Array of fragment IDs
private Integer[] fragments = {
R.id.nav_home,
R.id.nav_accessory,
R.id.nav_arcade,
R.id.nav_fashion,
R.id.nav_food,
R.id.nav_heath,
R.id.nav_lifestyle,
R.id.nav_sports,
R.id.about
};
Then in the Repository change the logic to use the indices instead of the fragment IDs:
@Inject
public DataStoreRepository(@ApplicationContext Context context) {
dataStore =
new RxPreferenceDataStoreBuilder(Objects.requireNonNull(context), /*name=*/ "settings").build();
readCurrentDestination =
dataStore.data().map(preferences -> {
Integer fragIndex = preferences.get(CURRENT_DESTINATION);
if (fragIndex == null) fragIndex = 0;
if (fragIndex >= 0 && fragIndex <= fragments.length) {
// Navigate to the fragIndex
return fragments[fragIndex];
} else {
return R.id.nav_home;
}
});
}
And in the ViewModel, you should not subscribe a permanent Observable to the Flowable because this will submit any change to the observed data permanently, but instead you can convert the Flowable to a Single so that you can just get a single (first) value of the fragment ID only once at the app launch, and no more observers are registered. Check Documentation for more details.
Applying that in your ViewModel:
@Inject
public PostViewModel(Repository repository, Utils.DataStoreRepository dataStoreRepository) {
this.repository = repository;
getAllItemsFromDataBase = repository.localDataSource.getAllItems();
this.dataStoreRepository = dataStoreRepository;
dataStoreRepository.readCurrentDestination.firstOrError().subscribeWith(new DisposableSingleObserver<Integer>() {
@Override
public void onSuccess(@NotNull Integer destination) {
// Must be run at UI/Main Thread
runOnUiThread(() -> {
currentDestination.setValue(destination);
});
}
@Override
public void onError(@NotNull Throwable error) {
error.printStackTrace();
}
}).dispose();
}
Then as you observe the currentDestination MutableLiveData in the activity: change the current destination there (You already did that well):
postViewModel.currentDestination.observe(this,currentDestination -> {
Log.d(TAG, "currentDestination: " currentDestination);
Toast.makeText(this,"currentDestination" currentDestination,Toast.LENGTH_SHORT).show();
navGraph.setStartDestination(currentDestination);
navController.setGraph(navGraph);
});
UPDATE 2
Saving the current fragment to the DataStore:
In the ViewModel:
public void saveCurrentDestination(int value){
int fragmentIndex = Arrays.asList(fragments).indexOf(value);
CURRENT_DESTINATION = PreferencesKeys.intKey(keyName);
dataStore.updateDataAsync(prefsIn -> {
MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
mutablePreferences.set(CURRENT_DESTINATION, fragmentIndex);
return Single.just(mutablePreferences);
}).subscribe();
}
uj5u.com熱心網友回復:
如何在導航圖中存盤當前位置并不重要(我們實際上是在談論存盤單個long值和最終一些引數值)。
先決條件本身應該已經解釋了它是如何作業的:
- 能夠使用全域
NavAction. - 能夠將存盤的目標 ID 決議為全域
NavAction. - 不要忘記導航引數(例如,alike an
itemId)。
與此類似,可能會丟失后臺堆疊條目,但可以直接導航。如果這還不夠,請存盤NavController后堆疊條目并播放它們(不建議)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/325524.html
標籤:爪哇 安卓 android-fragments 导航抽屉 android-架构-导航
上一篇:如何導航到回收站視圖中的片段?
