I have 3 fragments in my app and I want to log every time one of them is displayed. This is what I have in the onResume() in each fragment class:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
AppController application = (AppController) getActivity().getApplication();
mTracker = application.getDefaultTracker();
}
#Override
public void onResume() {
super.onResume();
if (mTracker != null) {
mTracker.setScreenName("Fragment_1");
mTracker.send(new HitBuilders.ScreenViewBuilder().build());
mTracker.enableAdvertisingIdCollection(true);
}
Log.i("RESUME", "Fragment_1");
}
However, when the app is launched, I receive all the three logs and when I scroll to another fragment, the onResume is not activated. I believe what happens is when the app is launched then all three fragments are loaded (I see that the data are downloaded from my server to populate the lists in the other 2 even if I'm just looking at the first fragment) and that's why onResume is not called when I scroll to the other fragments.
This and this is no different from this approach.
Related
I'm wondering if there is a way to restore, after rotation, my list of results without retaining the fragment.
Basically I have a fragment which calls, through a presenter, some api (using RxJava and Retrofit). I added pagination so I can make a call only when I need more data scrolling down.
I'm in the following scenario:
- I scroll the list down in order to call the second page from the web
- I rotate the screen
In this case what I would need is to show all the items, from page 1 and 2, and then scrolling to the correct position (for this I can use the onSaveInstanceState of the LayoutManager).
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FwApplication.component(getActivity()).inject(this);
if (savedInstanceState != null) {
mRxRunning = savedInstanceState.getBoolean(EXTRA_RX);
mQuery = savedInstanceState.getString(QUERY_ARG);
mCurrentPage = savedInstanceState.getInt(CURRENT_PAGE);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(QUERY_ARG, mQuery);
outState.putInt(CURRENT_PAGE, mCurrentPage);
outState.putParcelable(LAYOUT_STATE, mRecyclerView.getLayoutManager().onSaveInstanceState());
super.onSaveInstanceState(outState);
}
#Override
public void onResume() {
super.onResume();
mPresenter.onResume(mRxRunning, mCurrentPage);
}
Is there a way to save the items without calling setRetainInstance? Moreover I would avoid to call the api in order to get all the items back.
The items are POJOs so it won't be a list of simple strings.
Try to edit the Manifest file. Add this line to block with activity:
android:configChanges="keyboardHidden|orientation|screenSize">
It will prevent Activity restart on such events (specially on orientation change)
I have two fragments, fragment A and fragment B. Fragment A uses a shaking gesture to switch to fragment B, and fragment B uses a different gesture to switch back to fragment A. So when I'm in fragment A, I register gesture A with the SensorManager, and when a shake is detected, I unregister gesture A, switch to fragment B, and register gesture B with the SensorManager.
Fragment A:
public class FragmentA extends Fragment {
private MainWearActivity mMainWearActivity;
private SensorManager mSensorMgr;
private GestureA gestureA;
private OnShakeListener gestureAListener;
private View view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainWearActivity = (MainWearActivity) getActivity();
mSensorMgr = (SensorManager) mMainWearActivity.getSystemService(Activity.SENSOR_SERVICE);
gestureA = new GestureA();
gestureAListener = new OnShakeListener() {
#Override
public void onShake() {
gestureADetected();
}
};
gestureA.setOnShakeListener(gestureAListener);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_a, container, false);
return view;
}
#Override
public void onResume() {
super.onResume();
startListening();
}
#Override
public void onPause() {
stopListening();
super.onPause();
}
private void gestureADetected(){
mMainWearActivity.replaceFragment(mMainWearActivity.getFrag("B"));
}
private void startListening(){
mMainWearActivity.registerListener(gestureA);
}
private void stopListening(){
mMainWearActivity.unregisterListener(gestureA);
}
}
Fragment B:
public class FragmentB extends Fragment {
private MainWearActivity mMainWearActivity;
private FragmentManager fm;
private SensorManager mSensorMgr;
private GestureB gestureB;
private OnShakeListener gestureBListener;
private View view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainWearActivity = (MainWearActivity) getActivity();
fm = mMainWearActivity.getFragmentManager();
mSensorMgr = (SensorManager) mMainWearActivity.getSystemService(Activity.SENSOR_SERVICE);
gestureB = new GestureB();
gestureBListener = new OnShakeListener() {
#Override
public void onShake() {
gestureBDetected();
}
};
gestureB.setOnShakeListener(gestureBListener);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_b, container, false);
return view;
}
#Override
public void onResume() {
super.onResume();
startListening();
}
#Override
public void onPause() {
stopListening();
super.onPause();
}
private void gestureBDetected(){
fm.popBackStackImmediate();
}
private void startListening(){
mMainWearActivity.registerListener(gestureB);
}
private void stopListening(){
mMainWearActivity.unregisterListener(gestureB);
}
}
If I run this app, and continuously switch between fragment A and fragment B, after a few times the SensorManager stops detecting gestures. This is not the case on an Android phone where this setup works fine.
This is just a small test app I made to check whether the behaviour could be replicated, but my actual app is much larger and with many more gestures, so simply registering all the gestures once with the SensorManager and checking for the different fragments/gestures is not an ideal solution as it becomes very messy and complicated. Does anyone know of a way to "clean" the SensorManager so that it loses all references to any previously registered/unregistered listeners? Or is this just a bug in Android Wear. The device I'm using is a Moto360. Thanks.
In your questions of recent days, which contain similar code and I presume are related, I have not seen a posting of the code for your event-listener/gesture-detectors. It appears that at least one of them uses the gyro sensor. In one posted version of your code you were using a fast update rate (SENSOR_DELAY_GAME).
Is there logic in your listening/detection processing to ensure that a given wrist motion only causes the gesture to be reported once? I'm thinking about the control flow in your design: sensor event, gesture detection, fragment transaction, listener unregister. Because all this is happening on the main thread, and the fragment events are asynchronous, I suspect your listener will get a few more events after the one that triggered the gesture detection. This is more likely with a faster update rate. In my experience, the execution of the steps involved in a fragment transaction, along with the fragment callbacks (onCreateView(), onResume(), etc.), can take 20 milliseconds or more. Until onPause() for the fragment being removed is called, the listener for that fragment is still registered and events are being queued for the listener. If multiple gesture detections are fired, your fragment management could get messed up, perhaps causing the same fragment to be added multiple times. Adding Log statements to your gestureXDetected() would confirm that the gestures are being detected once, as your design expects.
To your question about a bug in the SensorManager, well, anything is possible, but I think it's very unlikely. You could easily confirm your suspicion by putting debug code in the onCreate() method of your MainActivity to reg/unreg a listener 100 times before starting normal operation, then observe if your app is immediately sluggish or the delays develop after you switch fragments repeatedly.
I have a main fragment with a viewpager inside it. This viewpager has 2 pages (list fragments). When I start the activty, the main fragment is shown and I also show the first paged fragment. This paged fragment displays data from a db using AsyncTask.
In the main fragment I have:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
onPageSelected(0);
}
#Override
public void onPageSelected(int position) {
Fragment fragment = (Fragment) pagerAdapter.instantiateItem(viewPager, position);
if (fragment instanceof IPagedFragment) {
((IPagedFragment) fragment).onShown(getActivity());
}
}
And the interface is:
public interface IPagedFragment {
void onShown(FragmentActivity activity);
}
The first issue I have is that I have to pass the activity as a parameter because when onShown gets called, the activity is still null.
Furthermore, the paged fragments use progressbar logic similar to the LoginActivity sample. I also get the following exception:
IllegalStateException: Fragment PagedFragment1{4201f758} not attached to Activity
at android.support.v4.app.Fragment.getResources(Fragment.java:620)
So what is the correct stage to start retrieving data from db once the paged fragment is fully available to the UI?
Issues like yours is the reason some developers are starting to question if fragments are really that good or useful.
Also "the correct" is debatable as you can do it in a variety of places and different developers will give you different answers, But let me try to supply you some useful info.
The attach/detach callbacks:
public void onAttach(Activity activity);
public void onDetach();
between those two methods any call to getActivity() will return the non-null activity the fragments is connected to. You can override them and use a private boolean isAttached to keep track of that call.
Also useful is the:
public void onActivityCreated (Bundle savedInstanceState)
this method is called AFTER the Activity.onCreate method. That is very important if you rely on some initialisation that happened there.
Also it's important to remember that on the moment the fragment transaction happens, the Fragment.onCreate happens after the Activity.onCreate and during rotation it happens before it.
As a general rule of thumb I use the Fragment.onStart() / Fragment.onStop() for getting/listening to data. On those calls, all the UI have been created, the fragment is attached to the activity and those callbacks don't get called if there's a dialog/popup (pause/resume does)
From the documentation:
public void onActivityCreated (Bundle savedInstanceState)
[...] tells the fragment when it is fully associated with the new activity instance.
source: http://developer.android.com/reference/android/app/Fragment.html#onActivityCreated(android.os.Bundle)
To get the reference of your activity, create a local object of fragmentActivity and get your activity reference as shown below.
private FragmentActivity fragmentActivity;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivity=activity;
}
I ask you a suggestion for my application. I have to develop one app with this characteristics :
1- only portrait
2- when open the app it shows the bluetooth devices presents
3- when you click on one device the app ask to the user an unlock code and show the connect button
4- after press connect button the app show a loading spinner bar whith two o three buttons
The point is : is It better that I used three different fragment for each behavior or not ?
For now I have did :
one activity for the scan device
one activity for the unlock code
but i don't know where I can put the loading screen (loading spinner bar and the three buttons)
Now I'm thinking of develop in different way. One central activity that handles the loading of 3 different fragments :
one for scan device
one for unlock code and one for loading screen
But I'm new in the Android programming, and I always wonder if I think in correct way or in the wrong way .
And in last : for communicate the chosen device from fragment to the activity I think I will implement a listener in mainactivity. Is it right ?
****EDIT :** *I have another doubt regarding the main question.***
Now after your advice I want develop this app in this way :
Main Activity
Scan DEvice fragment
Unlock Device fragment
Loading fragment
Started Under Service
In the precedent version I thought to develop three different activity and to use binder and messange to communicate in two direction way to/from the service
Now,instead, there are three different fragments inside the main activity. My question is : for you is better implement the comunication to the service inside mainactivity or inside every single fragment ?
For instance : User selects a device in the scan fragment, this fragment communicates the choise directly to the service or communicates the choise to the mainactivity which forwards the information to the service ?
Thanks for your time :)
Do not use setContentView() to change between states of the application, it can cause inconsistency problems on onBackPressed() - use fragments instead. But you will run into a problem on back press, so you will need to see if there are fragments left in your activity on back press, for which you will need to see https://stackoverflow.com/a/24527530/2413303 ContainerFragment in this question.
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
if (saveInstanceState == null)
{
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_container_container, new ExampleFragment())
.addToBackStack(null)
.commit();
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
Also, use ButterKnife library, I didn't in this example I linked, even though I should have. It makes the code much less verbose and do the same thing.
public class FancyFragment extends Fragment {
#InjectView(R.id.button1) Button button1;
#InjectView(R.id.button2) Button button2;
#OnClick(R.id.btnSubmit)
public void submit(View view) {
// TODO submit data to server...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
ButterKnife.inject(this, view);
// TODO Use "injected" views...
return view;
}
}
And for communication, you can use Otto library as per https://stackoverflow.com/a/28480952/2413303
public class UpdateListEvent {
}
public class MainActivity extends ActionBarActivity {
...
public void updatelist() {
SingletonBus.INSTANCE.getBus().post(new UpdateListEvent());
}
}
public class FragmentA extends Fragment {
#Override
public void onResume() {
super.onResume();
SingletonBus.INSTANCE.getBus().register(this);
}
#Override
public void onPause() {
SingletonBus.INSTANCE.getBus().unregister(this);
super.onPause();
}
#Subscribe
public void onUpdateListEvent(UpdateListEvent e) {
//do things
}
}
public enum SingletonBus {
INSTANCE;
private Bus bus;
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public Bus getBus() {
return bus;
}
}
I have another doubt regarding the main question.
Now I develop this app in this way :
Main Activity
Scan DEvice fragment
Unlock Device fragment
Loading fragment
Started Under Service
In the precedent version I thought to develop three different activity and to use binder and messange to communicate in two direction way to/from the service
Now,instead, there are three different fragments inside the main activity, for you is better implement the comunication to the service inside mainactivity or inside every single fragment ?
For instance :
User selects a device in the scan fragment, this fragment communicates the choise directly to the service or communicates the choise to the mainactivity which forwards the information to the service ?
I have a Fragment Activity with a FragmentTabHost. I add the fragments to the tab using the following code:
mTabHost.addTab(mTabHost.newTabSpec(tab1Name).setIndicator(tabIndicator1),
EventSettingsStep1Fragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec(tab2Name).setIndicator(tabIndicator2),
EventSettingsStep2Fragment.class, null);
When I switch to different tabs, I'd like to retain all the values (view state, etc) so that I have the same data when I switch back to the tab.
I overrode the onSaveInstanceState method & in there, I added values that I want retained to the bundle.
I ran through the methods being called and I have the following:
Switching from Tab1 to Tab2: Tab1:onPause then Tab2:onCreateView, Tab2:onResume
Switching from Tab2 to Tab1: Tab2:onPause then Tab1:onCreateView, Tab1:onResume
onSaveInstanceState is not being called.
Here is the code for one of my fragments:
public class EventSettingsStep1Fragment extends Fragment implements View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
if (savedInstanceState != null) {
Log.d(TAG, "restoring onSavedInstanceState");
Gson gson = new Gson();
event = gson.fromJson(savedInstanceState.getString("event"), Event.class);
}
if (event != null) {
//set views
}
return v;
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
#Override
public void onSaveInstanceState(Bundle outState) {
Log.d(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
Gson gson = new Gson();
outState.putString("event", gson.toJson(event));
}
}
Why is onSaveInstanceState not being called? Is it only triggered through the FragmentActivity?
onSaveInstanceState is not being called because the framework simply reuses the already-existing instance of the fragment. onSaveInstanceState only gets called when the instance is about to be destroyed and then recreated. This happens for example when you rotate the display and force the hosting activity to be recreated.
onSaveInstanceState is also not called when you push a fragment on the backstack of a FragmentManager. You will have to restore the state from the already existing instance, which can be very annoying. See SO questions How can I maintain fragment state when added to the back stack? and Once for all, how to correctly save instance state of Fragments in back stack? for example.
Basically you will have to do what the answers to these questions suggest: continue using the values of your instance variables and do not rely on a saved instance state.