Keep fragments info between activities - android

I have my main activity actionbaractivity One where you can screenslide through some fragmets, on each fragment you have an imageView and a ListView where you can click any item and the image will change. Also in the menu options you have a button where you change to an almost exact activity: actiobbaractivity Two which also have this button to change to activity One
What I'm able to do is to keep the image when sliding the fragments, but unable to keep the fragments state's through the change of activities.
For example
I'm in activity One on fragment 3 with the image: "something". I click on the button to change to activity Two, I do things here and then, I click on the button to change to activity One and I want to see my fragment 3 with the image: "something" and not the default fragment 1 and default image
Im using ActionBarActivity, FragmentStatePagerAdapter and Fragment for each activity
Thanks for the help

According to the Activity and Fragment lifecycles (http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle and http://developer.android.com/guide/components/fragments.html#Lifecycle), the most reliable way of persisting states between activity/fragment changes is to use the default API for saving and restoring states:
When the activity/fragment is being dismissed (either because of a configuration change such as screen rotation or because the user requested to go to another activity/fragment), you can save its state in a Bundle object. When it is being created, you can restore its saved state, thus recreating a new instance exactly like the one the user left - so the user feels nothing has changed. This does not depend on the specific subclass of activity/fragment you are using.
I have implemented something like what you want: in my case, a fragment containing a menu with buttons that would each lead the user to another fragment containing a submenu with a "back" button. So if the user went from menu to submenu 1, then back to menu, then to submenu 2, then back to menu and finally again to submenu 1, I wanted that submenu 1 to appear just like the user has left it in the first time.
For that I have created:
1) an interface defining my submenu types, implemented by my activities so they could change between my submenus
2) a master generic class, which all my submenus would extend, that had a Bundle object to store their state
3) in my activities, I had an array of Bundle capable of storing one instance of each of my submenus (because I am only interested in restoring the last state, so I don't need more than one)
The interface (item 1):
public interface SubmenusManager {
public static enum Submenus {
ROOTMENU,
SUBMENU1,
SUBMENU2;
private static final int size = Submenus.values().length;
public static int size() {
return size;
}
public static int getId(Submenus test) {
switch(test) {
case SUBMENU1:
return 1;
case SUBMENU2:
return 2;
case ROOTMENU:
default:
return 0;
}
}
}
public void cloneCurrentSubmenuState(Parcelable toOverwrite);
public Bundle getLastStoredSubmenuState(Submenus submenu);
public void setCurrentSubmenuTo(Submenus submenu);
}
The generic class (item 2):
public class MenuFragment extends Fragment {
private Bundle menuData = new Bundle();
public static String RESTORE_MAIN_OBJECT = "restore_main";
public Bundle getMenuData() {
return menuData;
}
public Bundle cloneMenuData() {
return new Bundle(menuData);
}
public void setMenuData(Bundle menuData) {
this.menuData = menuData;
}
}
One of the activities (item 3):
public class ExampleAct extends FragmentActivity implements SubmenusManager {
/**
* instance variables
*/
private MenuFragment mMenu;
private Bundle [] menuData; // the Array of Bundles!
private static final String CONTAINER = "parcelable_container";
private static final String SUBMENU = "saved_submenu";
private Submenus curSubmenu = Submenus.ROOTMENU; // the default state is the ROOTMENU
private boolean restoreLastSavedState = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) { // first time creating this activity
menuData = new Bundle[Submenus.size()];
} else { // this activity has a saved state from before
// restore all the data from all the submenus
menuData = (Bundle[]) savedInstanceState.getParcelableArray(CONTAINER);
// restore the info about which is the current active submenu
curSubmenu = (Submenus) savedInstanceState.getSerializable(SUBMENU);
}
buildMenuFragment(true);
//(...) stuff
}
private void buildMenuFragment(boolean restoreState) {
// (re)builds fragment inside menu.
// restoreState flags whether activity should look for
// saved state data and restore it
restoreLastSavedState = restoreState;
switch(curSubmenu) {
// Eclipse warns you about which are the constants in your enum
case ROOTMENU:
mMenu = new FragmentRootMenu();
break;
case SUBMENU1:
mMenu = new FragmentSubmenu1();
break;
case SUBMENU2:
mMenu = new FragmentSubmenu2();
break;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, mMenu)
.commit();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SUBMENU, curSubmenu);
cloneCurrentSubmenuState(mMenu.getMenuData().
getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
outState.putParcelableArray(CONTAINER, menuData);
// (...) stuff
}
#Override
public void cloneCurrentSubmenuState(Parcelable toOverwrite) {
if (menuData == null) menuData = new Bundle[Submenus.size()];
if (toOverwrite != null)
mMenu.getMenuData().putParcelable(MenuFragment.RESTORE_MAIN_OBJECT, toOverwrite);
menuData[Submenus.getId(curSubmenu)] = mMenu.cloneMenuData();
}
#Override
public Bundle getLastStoredSubmenuState(Submenus forThisSubmenu) {
return
(menuData == null || !restoreLastSavedState) ? new Bundle() : menuData[Submenus.getId(forThisSubmenu)];
}
#Override
public void setCurrentSubmenuTo(Submenus toThisSubmenu) {
if (mMenu != null) {
cloneCurrentSubmenuState(mMenu.getMenuData().
getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
}
curSubmenu = toThisSubmenu;
buildMenuFragment(true);
}
One of the submenus (extension of item 2):
public class FragmentSubmenu1 extends MenuFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_submenu1, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
}
public void init() {
// (...) stuff
MyParcelableObject tmp = null; // MyParcelableObject is a class
// that implements Parcelable and stores
// relevant info to rebuild this menu
// from a saved state
SubmenusManager m = (SubmenusManager) getActivity(); // remember activity implements SubmenusManager
Bundle bnd = m.getLastStoredSubmenuState(SubmenusManager.Submenus.SUBMENU1);
if (bnd != null) tmp = bnd.getParcelable(MenuFragment.RESTORE_MAIN_OBJECT);
if (tmp == null) {
tmp = new MyParcelableObject();
tmp.buildFromScratch(); // initializes with default data
}
// back button
Button backToMainMenu = (Button) getView().findViewById(R.id.submenu1_back);
backToMainMenu.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.ROOTMENU);
}
});
// (...) stuff
}
}
The Root menu (extension of item 2):
public class FragmentRootMenu extends MenuFragment {
View myView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.fragment_rootmenu, null);
return myView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
}
public void init() {
Button btnSubmenu1 = (Button) myView.findViewById(R.id.btn_call_submenu1);
btnSubmenu1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU1);
}
});
Button btnSubmenu2 = (Button) myView.findViewById(R.id.btn_call_submenu2);
btnSubmenu2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU2);
}
});
}
}
For that to work between activities, all you need to do is pass that object that stores the last state of all fragments (in my case, that would be Bundle [] menuData) to the activity that is being called through its Intent; you would recover it the same way as my ExampleAct did in its onCreate(). You could also wrap that Bundle [] inside a custom Parcelable object (very similar to my example MyParcelableObject; inside that one I had stuff like HashMap) if using an array is a problem.
Here how to pass a Parcelable between activities:
How to send an object from one Android Activity to another using Intents?

Related

how to prevent recyclerview scroll to top automatically in fragment onResume

I faced a problem with my recyclerview and fragments . It made me crazy . My English is not so good but I try to explain it.
I have a view pager with 2 different fragments and these two fragments load a same fragments with different data . this inner fragment load data to a recyclerview .
loading data run on inner fragment OnResume() method (first checked if adapter is null then load data to recyclerview . )
The problem is here when I clicked on recyclerview items it start new activity and when I back from activity to fragment recyclerview jump to top of the list . However it is not loading new data .
I tried to save recyclerview state on onPause method and restore it on onResume but it is not work . if I do it with delay it works but first it jump to top and then back to last item and it is not good at all .bellow code
/* new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// if(state !=null)
recyclerViewFragmentChannel.getLayoutManager().onRestoreInstanceState(mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE));
}
}, 1000);*/
I am wondering if anybody can help me to overcome with this problem .I want to prevent recyclerview jump to top. I attached my inner fragment codes to make my explain more clear .
my inner fragment :
public class Fragment_Channels extends Fragment {
private static final String ARG_COUNT = "param1";
private Integer counter;
private Adapter adapter;
private View view;
private List<Fragment> fragmentsList;
private RecyclerView recyclerViewFragmentChannel;
private GridLayoutManager layoutManagerPortrait, layoutManagerLandScape;
private Parcelable state;
private Bundle mBundleRecyclerViewState;
private String KEY_RECYCLER_STATE = "recycler_state";
public Fragment_Channels() {
// Required empty public constructor
}
public static Fragment_Channels newInstance(Integer counter) {
Fragment_Channels fragment = new Fragment_Channels();
Bundle args = new Bundle();
args.putInt(ARG_COUNT, counter);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentsList = getActivity().getSupportFragmentManager().getFragments();
if (getArguments() != null) {
counter = getArguments().getInt(ARG_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (view == null) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_channels, container, false);
recyclerViewFragmentChannel = view.findViewById(R.id.recyclerViewFragmentChannel);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
layoutManagerLandScape = new GridLayoutManager(view.getContext(), 5);
recyclerViewFragmentChannel.setLayoutManager(layoutManagerLandScape);
} else {
layoutManagerPortrait = new GridLayoutManager(view.getContext(), 2);
recyclerViewFragmentChannel.setLayoutManager(layoutManagerPortrait);
}
}
return view;
}
#Override
public void onPause() {
super.onPause();
mBundleRecyclerViewState = new Bundle();
state = layoutManagerPortrait.onSaveInstanceState();
}
#Override
public void onResume() {
super.onResume();
if ((recyclerViewFragmentChannel.getAdapter() == null)) {
//Load data to recyclerview here
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// if(state !=null)
layoutManagerPortrait.onRestoreInstanceState(mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE));
}
}, 1000);
}
}
If You want to make RecyclerView be in the same position as it was before going to 2nd fragment You can save the actual position in shared preferences. When You back to 1st fragment just load value from shared preferences and use method to scroll to a given position.
Scroll RecyclerView programmatically
recyclerView.smoothScrollToPosition(savedPosition); // if You want smooth scroll
recyclerView.scrollToPosition(savedPosition); // if You want instant scroll
Add android:descendantFocusability="blocksDescendants" in outer or parent layout of recyclerview.

Android 8 nullpointer exception with function call in fragment (only on certain devices!!!)

I currently see a very strange nullpointer-exception that I can not explain myself.
I have an Activity which hosts two fragments.
The context-menu is evaluated in the host-activity and then calls a public function in one of the fragments.
While this works well for several phones with android 7 I usually get a nullpointer-exception on my Samsung S7 with Android 8.
The nullpointer-exception occurs when I try to access any UI-elements of the fragment within this function call!
I already checked that the fragment instances are valid, and they are ok. They are fully initialized and added on the onCreate of the host.
Whenever I trigger the function from inside the fragment it is ok, but not if I call the same function from the context menu of the hosting activity!
At first it looks like a timing problem, because sometimes it works, although relativ seldom.
What is the reason of this behaviour and how can I get over this strange error?
Thanks
Andreas
public class EpaperFragmentHost extends AppCompatActivity
{
private CustomViewPager mViewPager;
private Toolbar toolbar;
private EpaperPicture_Fragment EpaperPictureFrag;
private int Picture_Fragment_Position = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.activity_connectfragmenthost);
toolbar = findViewById(R.id.toolbar);
toolbar.setTitle( String.format( Locale.GERMAN,
getString(R.string.Connectingto_STRING) , mDeviceName) );
setSupportActionBar(toolbar);
mSectionsPagerAdapter = new Connection_fragment_adapter(
getSupportFragmentManager() );
EpaperPictureFrag = EpaperPicture_Fragment.newInstance( );
mSectionsPagerAdapter.setFragment( Picture_Fragment_Position,
EpaperPictureFrag );
mViewPager = findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.epapermenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id){
case R.id.menuitem_savepicture:
// This call will fail on some phones, but works on others!
// And not because EpaperPictureFrag would be null, but the
// myDrawView inside this instance is null!
EpaperPictureFrag.takeScreenshot( true);
break;
default:
break;
}
boolean result = super.onOptionsItemSelected(item);
return result;
}
}
and this is the fragment:
public class EpaperPicture_Fragment extends Fragment
{
public DrawView myDrawView;
public EpaperPicture_Fragment() {
// Required empty public constructor
}
public static EpaperPicture_Fragment newInstance( ) {
EpaperPicture_Fragment fragment = new EpaperPicture_Fragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_epaper_picture, container,
false);
btn_Clear = v.findViewById(R.id.clear_btn);
btn_addText = v.findViewById(R.id.addtext_btn);
btn_Transmit = v.findViewById(R.id.transmit_btn);
myDrawView = v.findViewById(R.id.epaper);
btn_Clear.setOnClickListener( this );
btn_addText.setOnClickListener(this);
btn_Transmit.setOnClickListener( this );
return v;
}
#Override
public void onClick(View v) {
switch( v.getId() ){
case R.id.transmit_btn:
// This call works!
takeScreenshot( true );
break;
default:
break;
}
}
public File takeScreenshot(boolean showToast) {
// THIS IS THE PROBLEMATIC SECTION!
// Why can myDrawView be null, if the fragment exists?
myDrawView.setDrawingCacheEnabled(true);
Bitmap cachedBitmap = myDrawView.getDrawingCache();
Bitmap copyBitmap = cachedBitmap.copy(Bitmap.Config.RGB_565, true);
myDrawView.destroyDrawingCache();
// ...
}
}
Ok, the solution is:
I changed the check for storage access in the base-class to a new mode.
And this check caused each activity which was inherited by the baseclass to re-create after checking the access rights!
I found it by generating all overwrite methods of the base class and made a log-output in each. It was a terrible destroy and create-sequence.
Unfortunately I changed my test-phone around the same time...

What is wrong with how I am using a FragmentStatePagerAdapter?

I have an Activity using a FragmentStatePagerAdapter. If I launch another activity that changes some data involved with what is displayed, the view is not updated.
If the adapter is handling tabs, each to show different aspects of the same object via Fragments,
if an object attribute is changed by an activity launched from a page handled by the adapter,
and the adapter notifyDataSetChanged is called in onActivityResult, the data in the tab view is not getting updated, as I expect it should be.
I cannot figure out why.
In the activity class:
public class EventDetailActivity extends AppCompatActivity
{
public ViewPager viewPager;
public PagerAdapter adapter; // This extends FragmentStatePagerAdapter
public TabLayout tabLayout;
public Event currentEvent; // ****** Contains the data to display in tabs
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_event_detail);
currentEvent = (Event)getIntent().getSerializableExtra(Event.EVENT_KEY); // ***** The object on display was serialized to pass in the intent.
// Serializing it in the initial intent is not a problem, because it is saved in the database within this activity,
// and the calling activity gets the update via the database.
tabLayout = (TabLayout) findViewById (R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().
setText(getResources().getString(R.string.details)));
... Add other tabs. ...
tabLayout.setTabGravity (TabLayout.GRAVITY_FILL);
viewPager = (ViewPager) findViewById (R.id.pager);
adapter = new PagerAdapter(currentEvent, getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter (adapter);
viewPager.addOnPageChangeListener (new TabLayout.TabLayoutOnPageChangeListener (tabLayout));
tabLayout.setOnTabSelectedListener (new TabLayout.OnTabSelectedListener () {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem (tab.getPosition ());
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId ())
{
case R.id.menu_edit:
Intent intent = new Intent(EventDetailActivity.this, EditEventActivity.class);
intent.putExtra(Event.EVENT_KEY, currentEvent); // TODO Event via serialization, or event id only ?
//intent.putExtra("id", currentEvent.getId());
startActivityForResult(intent, EDIT_EVENT_REQUEST);
return true;
... other cases
}
}
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
if (requestCode == EDIT_EVENT_REQUEST)
{
switch (resultCode)
{
case RESULT_CANCELED:
// Nothing to do
return;
case RESULT_EVENT_MODIFIED:
// Cause event view for this activity to update.
// When the edit activity was started, the Event was serialized.
// An updated Event is passed back in the result.
//currentEvent = (Event)data.getSerializableExtra(Event.EVENT_KEY);
//System.out.println("Modified event returned: " + currentEvent.getEventTitle());
// Alternatively, Load the Event from the database:
try
{
HashMap attr = MyApp.getDatabase().getEventById(currentEvent.getId());
currentEvent.load(attr);
System.out.println("Event reloaded: " + currentEvent.getEventTitle());
}
catch (PersistenceException ex)
{
// TODO handle error
}
// FIXME: In both cases the received event is correct, but the UI is not updated.
// The adapter still references the object that was passed to the edit activity as serialized data
// So must give the adapter the object just deserialized/loaded here.
adapter.setEvent(currentEvent); // ***** notifyDataSetChanged() is called within this, but not updating the view !!!!!!!!
return;
case RESULT_EVENT_UPDATE_FAILED:
// Nothing to do
return;
}
}
}
...
}
The adapter:
public class PagerAdapter extends FragmentStatePagerAdapter
{
/** The event on display */
private Event m_event;
public PagerAdapter (Event event, FragmentManager fm)
{
super(fm);
m_event = event;
}
public void setEvent (Event event)
{
m_event = event;
notifyDataSetChanged(); // ****** Attempting to trigger update of displayed data, but the view does not update.
}
#Override
public Fragment getItem (int position)
{
Fragment f;
switch (position)
{
case 0:
f = new DetailsFragment();
break;
... other tab fragments
default:
return null;
}
// ******* FIXME?: The problem with passing serialized event to the fragment is that the fragment does not reference our event.
... each fragment references a COPY of the event.
// The updated event is passed back in the result... then set in the adapter.... BUT NOT IN FRAGMENTS
... BUT FRAGMENTS GET CREATED HERE AS NECESSARY TO VIEW, AND WILL GET THE MODIFIED EVENT IN THIS ARGUMENTS BUNDLE:
Bundle bundle = new Bundle();
bundle.putSerializable(Event.EVENT_KEY, m_event);
// Maybe just pass the event id in arguments, and the fragment gets the event from the database?? Sounds inefficient, and I think should not be necessary.
//bundle.putLong(Event.EVENT_ID_KEY, m_event.getId());
f.setArguments(bundle);
return f;
}
...
}
public class DetailsFragment extends Fragment
{
/** Event to display */
private Event m_event = null;
... UI TextView object declarations to show various attributes ...
public DetailsFragment() {
// Required empty public constructor
}
private void update ()
{
if (m_event == null)
{
... set views empty ...
return;
}
... set views for attributes of m_event ...
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate (R.layout.fragment_event_details, container, false);
//initalize widgets ...
// Get event for view
Bundle args = getArguments();
m_event = (Event)args.getSerializable(Event.EVENT_KEY); // ***** Get the event passed in arguments to this fragment
update();
return view;
}
}
Try override method in your FragmentStatePagerAdapter
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}

Saving values when closing app or switching fragments

I am new to Android and have an integer weekNumber that needs to be kept when closing the app. My idea was to set the value in the activity that handled the fragments that need to use the value, and always get the value from there with a getter, but the weekNumber keeps resetting when I close the app or switch fragments, even though I use SharedPreferences. I load the saved data in onCreate of the activity, and save the data in onPause. Maybe this is wrong. Why does this happen? I would appreciate any help and would love to learn more!
Here is my MainActivity that handles Fragments:
public class MainActivity extends AppCompatActivity {
private int weekNumber;
private static final String SHARED_PREFS = "sharedPrefs";
private static final String WEEK_NUMBER = "weekNumber";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigation);
bottomNavigationView.setOnNavigationItemSelectedListener(navListener);
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new HomeFragment()).commit();
loadData();
}
private BottomNavigationView.OnNavigationItemSelectedListener navListener =
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.nav_home:
selectedFragment = new HomeFragment();
break;
case R.id.nav_add:
selectedFragment = new CalculatorFragment();
break;
case R.id.nav_settings:
selectedFragment = new SettingsFragment();
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,selectedFragment).commit();
return true;
}
};
#Override
protected void onPause() {
super.onPause();
saveData();
}
public void saveData() {
SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS,MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt(WEEK_NUMBER,weekNumber);
}
public void loadData() {
SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS,MODE_PRIVATE);
weekNumber = sharedPreferences.getInt(WEEK_NUMBER,1);
}
public int getWeekNumber() {
return weekNumber;
}
public void setWeekNumber(int weekNumber) {
this.weekNumber = weekNumber;
}
One of the Fragments:
public class HomeFragment extends Fragment {
View rootView;
private TextView[] textViews;
MainActivity mainActivity = new MainActivity();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_home,container,false);
textViews = new TextView[16];
for(int i=0; i<textViews.length; i++) {
{
String buttonID = "textView" + (i+1);
int resID = getResources().getIdentifier(buttonID, "id", getActivity().getPackageName());
textViews[i] = ((TextView) rootView.findViewById(resID));
}
}
setWeekText();
textViews[9].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(),ActivityDay1.class));
}
});
Button buttonNextWeek = rootView.findViewById(R.id.buttonNextWeek);
buttonNextWeek.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mainActivity.setWeekNumber(mainActivity.getWeekNumber()+1);
setWeekText();
}
});
return rootView;
}
private void setWeekText() {
textViews[8].setText(String.valueOf(mainActivity.getWeekNumber()));
}
Call editor.apply(); at the end of your saveData function in order to actually save the value in the SharedPreferences.
Also, you can't call MainActivity mainActivity = new MainActivity(); in the fragment. Set it with mainActivity = getActivity(); in onCreateView or directly in your onClick listeners.
Why? What you have creates a new instance of MainActivity inside the fragment rather than referring to the one on which you have set the data you need.
Also, if you keep the current design, it's probably safer to call loadData in your Activity onCreate before you create the fragment that is going to try to access the data.
Perhaps a better option would be to just use the shared preferences in the fragment directly though.
A couple of things.
You need to make sure data is actually being saved. Add editor.commit()in your saveData()
You should do the saving in onSaveInstance and restoring in onSaveInstance since this is what android will call when the system kill your activity on cases such as low memory

WebView with FragmentStatePagerAdapter goes blank on calling setCurrentItem

I am working on Swipe Views with Tabs. The code provided in the "EffectiveNavigation" project at the Creating Swipe Views with Tabs page provides a solid starting ground. Experimenting further I added an OnClickListener to the given TextView and added a setCurrentItem to the onClick method. This behaves as expected and the ViewPager jumps to the requested page.
/**
* A dummy fragment representing a section of the app, but that simply displays dummy text.
*/
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(android.R.id.text1)).setText(
Integer.toString(args.getInt(ARG_OBJECT)));
((TextView) rootView.findViewById(android.R.id.text1)).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
/*
*setCurrentPagerItem(5); -> omitted here to reduce complexity
*/
mViewPager.setCurrentItem(5);
}
});
return rootView;
}
}
As the project I'm working on requires the loading of static webpages instead of text. I replaced the TextView with a WebView to load a different webpage at every swipe. This works perfectly well. Click events from the HTML side are handled by a JavascriptInterface I have implemented.
It is here that I'm facing a problem. The setCurrentPagerItem method works perfectly well when called outside of the JavascriptInterface. When called from within the JavascriptInterface the WebView shows a blank screen and stays so until a swipe to the right or left is made. A swipe to the right displays the next page to the one requested and a swipe to the left displays the requested page. LogCat shows no errors and this behaviour is consistent across a 4.3 based emulator and a Nexus 7 running 4.4.4. I shall provide the entire code below.
public class CollectionDemoActivity extends FragmentActivity {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide fragments representing
* each object in a collection. We use a {#link android.support.v4.app.FragmentStatePagerAdapter}
* derivative, which will destroy and re-create fragments as needed, saving and restoring their
* state in the process. This is important to conserve memory and is a best practice when
* allowing navigation between objects in a potentially large collection.
*/
DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
/**
* The {#link android.support.v4.view.ViewPager} that will display the object collection.
*/
ViewPager mViewPager;
private static Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collection_demo);
context = this;
// Create an adapter that when requested, will return a fragment representing an object in
// the collection.
//
// ViewPager and its adapters use support library fragments, so we must use
// getSupportFragmentManager.
mDemoCollectionPagerAdapter = new DemoCollectionPagerAdapter(getSupportFragmentManager());
// Set up action bar.
final ActionBar actionBar = getActionBar();
// Specify that the Home button should show an "Up" caret, indicating that touching the
// button will take the user one step up in the application's hierarchy.
actionBar.setDisplayHomeAsUpEnabled(true);
final OnPageChangeListener mPageChangeListener = new OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onPageSelected(int pos) {
final Toast pageNo;
pageNo = Toast.makeText(context,"PAGE "+(Integer.toString(pos+1))+"/100",Toast.LENGTH_SHORT);
pageNo.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
pageNo.cancel();
}
}, 100);
}
};
// Set up the ViewPager, attaching the adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mDemoCollectionPagerAdapter);
mViewPager.setOnPageChangeListener(mPageChangeListener);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This is called when the Home (Up) button is pressed in the action bar.
// Create a simple intent that starts the hierarchical parent activity and
// use NavUtils in the Support Package to ensure proper handling of Up.
Intent upIntent = new Intent(this, MainActivity.class);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is not part of the application's task, so create a new task
// with a synthesized back stack.
TaskStackBuilder.from(this)
// If there are ancestor activities, they should be added here.
.addNextIntent(upIntent)
.startActivities();
finish();
} else {
// This activity is part of the application's task, so simply
// navigate up to the hierarchical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
return true;
}
return super.onOptionsItemSelected(item);
}
private void setCurrentPagerItem(int item) {
mViewPager.setCurrentItem(item);
}
/**
* A {#link android.support.v4.app.FragmentStatePagerAdapter} that returns a fragment
* representing an object in the collection.
*/
public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
public DemoCollectionPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new DemoObjectFragment();
Bundle args = new Bundle();
args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1); // Our object is just an integer :-P
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// For this contrived example, we have a 100-object collection.
return 100;
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
/**
* A dummy fragment representing a section of the app, but that simply displays dummy text.
*/
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
final WebView webView = (WebView) rootView.findViewById(R.id.webView);
switch(args.getInt(ARG_OBJECT)) {
case 1 :
webView.loadUrl("file:///android_asset/html/index.html");
break;
default :
webView.loadUrl("file:///android_asset/html/page_"+(Integer.toString(args.getInt(ARG_OBJECT)-1))+".html");
break;
}
WebSettings ws = webView.getSettings();
ws.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new Object()
{
#JavascriptInterface
public void toPage(String pageNo) {
((CollectionDemoActivity) getActivity()).setCurrentPagerItem(4);
}
}, "external");
return rootView;
}
}
}
I could be wrong but it sounds like you are not updating on the UIThread.
You could try something like this.
getActivity().runOnUiThread(new Runnable(){
#Override
public void run() {
((CollectionDemoActivity) getActivity()).setCurrentPagerItem(4);
}
});

Categories

Resources