gpt4 book ai didi

java - 如何打开关闭应用程序后打开的最后一个 fragment 并使用抽屉导航和导航组件重新打开它

转载 作者:行者123 更新时间:2023-12-04 14:48:36 25 4
gpt4 key购买 nike

更新
由于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 个菜单项和 fragment ,我想将最后打开的 fragment 保存在 savedInstanceState 中或 datastore并在用户关闭应用程序并重新打开后再次显示最后打开的 fragment ,但我不知道我将使用哪种方法
Navigation.findNavController(activity,nav_graph).navigate();
或者
binding.navView.setNavigationItemSelectedListener(item -> false);
activity_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>
nav_graph.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>
MainActivity类
@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();
}

}

最佳答案

免责声明:
作为 SharedPreference迟早会被弃用,下面有一个使用 DataStore 的更新.

使用 SharedPreferenceonSaveInstanceState & onRestoreInstanceState应用程序关闭/关闭后不能用于存储/恢​​复值。
即使应用程序没有关闭,您也不能依赖它们来存储大型对象或长时间存储对象。
取而代之的是,您可以使用 SharedPreference在应用程序存在之前存储一个映射到最后一个打开 fragment 的值。
这里我存储了一些任意值,因为建议不要存储应用程序 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);
}
一旦目标更改,您可以使用 OnDestinationChangedListener 将新值注册到 sharedPreference 中。的 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


因此,我将使用相同的方法,但使用 DataStore(相同的 fragment 数组,存储索引而不是 fragment 目标 ID)。
因此,您需要添加 fragment ID 数组,以便可以在 DataStore 进程中读取它:
// 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
};
然后在存储库中更改逻辑以使用索引而不是 fragment ID:
@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;
}
});

}
ViewModel ,你不应该订阅 Flowable 的永久 Observable因为这将永久提交对观察数据的任何更改,但您可以转换 FlowableSingle这样您就只能在应用程序启动时获得 fragment ID 的单个(第一个)值,并且不再注册观察者。 Check Documentation更多细节。
在你的 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();

}
然后当你观察 currentDestination Activity 中的 MutableLiveData:更改那里的当前目的地(你已经做得很好了):
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);
});
将当前 fragment 保存到 DataStore每当目的地改变时:
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();

}

关于java - 如何打开关闭应用程序后打开的最后一个 fragment 并使用抽屉导航和导航组件重新打开它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69511294/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com