Listed below is my basic code for controlling the maps. I do some really advanced stuff later. Everything seems to work perfect, until onResume().
Here is the layout, you navigate through the app in 1 single activity, with multiple fragments. This mapFragment is contained inside of a fragment. This works fine. However when I add another fragment and push this one on the back stack, when i come back to it later, the map is unresponsive.
I tried fixing this by moving my call to setupMaps(); into the onResume(), however this caused gMaps to be null when I get it from gMaps = mapFragment.getMap(); in the setViews().
How should I handle this?
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
root = inflater.inflate(R.layout.fragment_maps, container, false);
setupMaps();
return root;
}
private void setupMaps()
{
gMaps = null;
fm = getActivity().getSupportFragmentManager();
mapFragment = SupportMapFragment.newInstance();
android.support.v4.app.FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.flMapContainer, mapFragment).commit();
}
#Override
public void onResume()
{
super.onResume();
mapFragment.onResume();
setViews();
}
private void setViews()
{
gMaps = mapFragment.getMap();
getData(); // initializes overlays, markers, polygons etc.
}
#Override
public void onPause()
{
mapFragment.onPause();
super.onPause();
}
Do you see anything in your logcat? I've had some issues like this before, and I believe it was related to the old map fragment's View not being removed from its parent ViewGroup before creating a new instance of it. This resulted in errors regarding a duplicate fragment.
Try removing all views from your flMapContainer before you create the new instance of the SupportMapFragment.
To implement Scott Stanchfield's solution:
call cleanFrame() when add/replace another fragment.
public void cleanFrame(){
FrameLayout FL = (FrameLayout) thisview.findViewById(R.id.myfragmentcontainer);
FL.removeAllViewsInLayout();
}
I have disabled hardware acceleration inside manifest.xml and after it everything started to work successfully:
<application android:hardwareAccelerated="false">
...
</application>
Related
I've looked through the answers on stackoverflow for this newbie android question, but I cannot understand what the optimal solution is.
I am trying to create a simple activity (with a fragment) that has a couple of text fields and a button. When the button is pressed, I would like the button to do something!
So I have my mainActivity class, along with its code fragment. I am trying to correctly set the "onClickListener" to my button object.
When I do this in the "onCreate" method in the activity itself, it throws a nullpointerexception - I'm guessing this is because the button itself is not yet created. However putting this code in the OnStart method would cause it to be called over and over again and seems out of place.
So I moved it to the "Placeholder fragment" - where it is no longer being reported as null, but I cannot use an intent to navigate to another page (because the other pages are not static AND I cannot reference the current activity using "this").
I'm pretty stuck and it seems like something simple.
Scenario #1: trying to initiate it in the OnCreate method (:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_made_my_day);
////////////////////// PROBLEM IS HERE //////////////////////////////////
Button butLogin = (Button) findViewById(R.id.but_login);
butLogin.setOnClickListener(butLogin_OnClickListener);
///////////
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
Version 2 -- Fragment code, also doesn't work:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_made_my_day, container, false);
Button butLogin = (Button) rootView.findViewById(R.id.but_login);
butLogin.setOnClickListener(butLogin_OnClickListener);
return rootView;
}
First, you can reference the Activity from a Fragment (after it's been attached) by calling getActivity(). That should be enough to launch the intent. For example:
Intent intent = new Intent(getActivity(), ActivityToCall.class);
getActivity().startActivity(intent);
Second, is the but_login Button actually in activity_made_my_day.xml or in fragment_made_my_day.xml? Depending on which, that's the place where you should use findViewById() (activity or fragment).
I have a SupportMapFragment added programmatically with getChildFragmentManager() in my onCreate() method.
When I reopen the app after the activity has been closed, the app seems to be rendering the old child SupportMapFragment without the markers. The old child fragment isn't interactable either.
How do I fix this lifecycle issue with SupportMapFragment? Do I need to call a specific detach method or something to that effect?
The issue was to do with the way I was handling my child fragments.
Every time the parent fragment would call onCreate the children fragments would get recreated.
I did the following to handle my child fragments, but there may be a better way:
private static final String TAG_FRAGMENT_MAP = "TagFragmentMap";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
if (savedInstanceState == null) {
// create the fragments for the first time
ft.add(R.id.view_flip, new SupportMapFragment(), TAG_FRAGMENT_MAP);
ft.commit();
}
}
// ...
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
mMapFragment = (SupportMapFragment)findFragmentByTag(TAG_FRAGMENT_MAP);
}
I am trying to use Android fragments in a very simple way, similar to the tutorial on the Android developer website.
I have an Activity (MediaInfoActivity) with the following code:
public class MediaInfoActivity extends FragmentActivity {
private final String TAG = "MediaInfoActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
setContentView(R.layout.media_info_activity_layout);
}
}
Here is the code for the media_info_activity_layout.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment class="com.hawkforce.test.MediaInfoFragment"
android:id="#+id/mediaInfoFragment"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
<FrameLayout android:id="#+id/mediaPlayerBarPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<fragment class="com.hawkforce.test.MediaPlayerBarFragment"
android:id="#+id/mediaPlayerBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
And finally here is the code for MediaInfoFragment:
public class MediaInfoFragment extends Fragment {
private final static String TAG = "MediaInfoFragment";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i(TAG, "onCreateView()");
if (container == null) {
Log.i(TAG, "onCreateView(): container = null");
}
return inflater.inflate(R.layout.media_info_fragment_layout, container, false);
}
}
Here is my problem : the container passed in the onCreateView() method of the MediaInfoFragment is null. As I understood, this should only be the case for non-UI Fragments. However, my Fragment has a UI, which is displayed OK on the screen when I launch MediaInfoActivity. It causes problems because no style declared in the xml layout file of the fragment is applied.
Here is my Log:
I/MediaInfoActivity: onCreate()
I/MediaInfoFragment: onCreate()
I/MediaInfoFragment: onCreateView()
I/MediaInfoFragment: onCreateView(): container = null
Am I missing anything obvious here ?
You just have to create a inflater like bellow in your fragment.
View rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.activity_my_cart, null);
} else {
((ViewGroup) container.getParent()).removeView(rootView);
}
return rootView;
}
I hope it will work as per your question.
I am not sure since I don't have the code of the SDK in front of me but I think that the life-cycle of calling Fragment "onCreateView" function is different between the two cases:
1. Static settings of fragment in layout
2. Loading pragmatically with FragmentManager.
In the first case the debugger get into Fragment.onCreateView() method immediately upon adding the content view to the parent activity as part of onCreate() code:
When calling: setContentView(R.layout.some_layoue);
You will see the debugger get into Fragment.onCreateView() before going to next line
In the second case the Fragment.onCreateView() is being invoked only after the onCreate() of the activity is finished.
This looks like design bug for me but possibly as design feature.
Anyway the container is null when adding fragment statically because the related object was not yet created.
In fact the difference between the two situations is much deeper. In the case of static fragments toggling between fragments will not create the view hierarchy correctly.
For example if you will add button-A to fragment A and button-B to Fragment-B and toggle the fragments with a code looks like this (highlighting only the relevant code):
public void turnOnFragment() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
manager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.attach(this)
.commit();
}
}
public void turnOffFragment() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
manager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.detach(this)
.commit();
}
}
You will see that in the case of static fragments the buttons from both fragments are presented although turning on and off. If however fragments are added programatically the toggle works fine and view hierarchy is cleaned and show only button from relevant fragment.
This is based of my experience with version 4.4.2
I am having a very odd behaviour here. From my MainActivity class, I invoke my DialogFragment:
EndRoundDialogFragment df = new EndRoundDialogFragment(myVO);
df.show(fragmentManager, "end_round_dialog_fragment");
The constructor for this DialogFragment is simple:
public EndRoundDialogFragment(UserVO vo) {
this.userVO = vo;
}
This Fragment has a Google Maps fragment inside it. So what I basically do is:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.end_round_dialog, container, false);
mMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map_result)).getMap();
mMap.setIndoorEnabled(false);
mMap.setMyLocationEnabled(false);
mMap.setTrafficEnabled(false);
buttonClose.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mListener.onEndDialogClosed();
userVO = null;
dismissAllowingStateLoss();
}
});
//Rest of the code is manipulating Markers in this map.
So, when user clicks on Close button, I dismiss the dialog and am OKwith State loss. I am even setting the VO to null.
As you can see, my Activity implements the Dialog's Listener onEndDialogClosed(). This method implementation is:
#Override
public void onEndDialogClosed() {
Fragment prev = fragmentManager.findFragmentByTag("end_round_dialog_fragment");
if(prev != null)
fragmentManager.beginTransaction().remove(prev).commit();
new Get5PointsTask().execute();
}
So I am also removing the whole fragment when it is closed.
I also added the following to this DialogFragments, to make sure that the Google Maps fragment was indeed being removed from the code:
#Override
public void onDestroyView() {
super.onDestroyView();
Fragment f = (MapFragment) getFragmentManager().findFragmentById(R.id.map_result);
if (f != null) {
getFragmentManager().beginTransaction().remove(f).commit();
}
}
While debugging, I verified that the variable f is not null and the remove() code is indeed called.
When I instantiate the DialogFragment again, with a new VO data, Google Maps is shown with new and old data. It's just not resetting the de memory...
Any ideas?
EDIT 1:
And I found what the problem was. Was simply not erasing my VO in the Activity's onEndDialogClosed() method. Stupid me.
Thanks to all and hope this code helps other people.
So, I fixed the issue.
I was simply not re-instantiating the VO that held the user data in the Activity... so when opening the DialogFragment, it held the new and the old that. Simple like that, stupid like that.
Thanks!
My app is using a ListFragment on left side that the user can use to select what fragment to use on the right hand side.
In sort it seems impossible to show the MapView more than once. The first problem is that it only allow one instance of MapView per Activity.
# Exception 1:
You are only allowed to have a single MapView in a MapActivity
Therefore, I saved my MapView and container in the Activity class:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager.enableDebugLogging(true);
setContentView(R.layout.main);
mapViewContainer = LayoutInflater.from(this).inflate(R.layout.maplayout, null);
mapView = (MapView) mapViewContainer.findViewById(R.id.map_view);
}
However, this give me the next problem:
# Exception 2:
The specified child already has a parent.
You must call removeView() on the child’s parent first.
I have tried to remove the view, using this code:
((ViewGroup)mapViewContainer).removeView(mapView);
((ViewGroup)mapView.getParent()).removeView(mapView);
Got a NullPointerExeption.
I would appreciate any good ideas, or if you could share if you have been successful in doing this?
Thanks :)
Yeah, bumped into this one, too.
Do not add your MapView in XML layout file for your fragment. Instead, just leave a place for it, say, in a LinearLayout with id="#+id/your_map_container_id"
Declare a MapView private member in YourMapContainerFragment's:
public class YourMapContainerFragment extends Fragment {
private MapView mMapView;
//...
Then, go like this in YourMapContainerFragment's onCreateView():
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ... Inflate your fragment's layout...
// ...
if (mMapView == null) {
mMapView = new MapView(getActivity(), /*String*/YOUR_MAPS_API_KEY);
} else {
((ViewGroup)mMapView.getParent()).removeView(mMapView);
}
ViewGroup mapContainer = (ViewGroup) fragmentLayout.findViewById(R.id.your_map_container_id);
mapContainer.addView(mMapView);
// ...
}
This will make the same MapView object be reused across removals/additions of your fragment to activity.