I have a main activity using BottomNavigationView
, where the first Fragment
has a nested ViewPager
with some tabs. When I navigate to the second or third "main fragment" and then go back to the first (the one holding the tabs) I would like to restore the tab that was active when I left.
To achieve this I'm saving the viewPager's current item in the fragment's onPause
...
@Override
public void onPause() {
super.onPause();
App.homeTab = viewPager.getCurrentItem();
Log.v(DEBUG_TAG, "item: " + App.homeTab);
}
being App.homeTab
a static filed into my Application
's subclass:
public class MyApp extends Application {
public static int homeTab = 0;
// [...]
... and then I'm retrieving that value in the fragment's onCreateView
like this:
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home, container, false);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(
getContext(),
getChildFragmentManager(),
HomeCategoryContent.Elements
);
viewPager = root.findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
Log.d(DEBUG_TAG,"restored tab: " + App.homeTab);
viewPager.setCurrentItem(App.homeTab);
// [...]
SectionsPagerAdapter
extends FragmentPagerAdapter
, straight from the Android Studio's template.
It works, but somehow I feel a bit uncomfortable every time I have to use a static reference to some UI component...
Are there better solutions? Is this one prone to memory leakage?
static
variables are not mandatory in this case. You can store the value everywhere what is still "alive" for example - activity instance variable. It will look like this
((MyActivity)getActivity()).setFirstPagerPosition(position)
((MyActivity)getActivity()).getFirstPagerPosition()
The better approach is to pass activity to fragment as instance of some interface it will implement like this
interface FirstPagerPositionStorage {
void setFirstPagerPosition(position);
int getFirstPagerPosition();
}
class MyActivity extends Activity implements FirstPagerPositionStorage{
...
}
MyFragment.newInstance((FirstPagerPositionStorage)this)
class MyFragment extends Fragment {
FirstPagerPositionStorage listener;
static MyFragment newInstance(FirstPagerPositionStorage listener){
MyFragment fragment = new MyFragment();
fragment.listener = listener;
return fragment;
}
}
Don't use onPause
method to store such values use OnPageSelectedListener
of ViewPager
there is a method onPageSelected(int position)
- use it to store the value
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int arg0) { }
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) { }
@Override
public void onPageSelected(int position) {
listener.setFirstPagerPosition(position);
//or if you will go with activity methods
((MyActivity)getActivity()).setFirstPagerPosition(position);
}
});
If you chose listener way don't forget to check if current activity implements this interface in fragments onAttach
method
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof FirstPagerPositionStorage)){
throw IllegalStateException("Activity must implement FirstPagerPositionStorage");
}
}
This was easier and more classic Android way. Modern way is to use shared between all the bottom navigation fragments ViewModel
Most of the modern apps will be implemented this way. It is pretty cool to use in conjunction with Jetpack Navigation
and BottomNavigation
- more details here
Either way all the values stored this way will be lost after the activity is closed as well as static
value will be lost after the app is closed. If you want to persist this value between app runs - use SharedPreference
or some other storage like file cache or database.
Using static
variables is not very good but it is not at all bad. The major downfall of this - you can get used to it and eventually the app will become much harder to maintain due to the lack of proper dataflow.
Hope it helps