I have an activity that hosts two fragments. One fragment provides some info to the user in various fields. The other fragment should show a Map.
For the fragment hosting the map I need to be able to add custom logic for displaying markers and several other things that I would like to reuse in other places in the app. What is the best way to create a map without defining the fragment in xml like:
<fragment
class="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Thanks,
Nathan
Edit:
I am now using nested fragments, not sure if that is the best way to handle this? My mapfragment is now:
<?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
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
</LinearLayout>
and the Fragments code is:
public class ViewCurrentMapFragment extends Fragment implements View.OnClickListener {
public static final String TAG = "MAP_FRAGMENT";
private GoogleMap map;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_view_current_map, container, false);
return rootView;
}
private void getCurrentMarkers() {
// Getting reference to the SupportMapFragment of activity_main.xml
SupportMapFragment smf = ((SupportMapFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.map));
map = smf.getMap();
}
#Override
public void onClick(View v) {
}
#Override
public void onResume() {
super.onResume();
getCurrentMarkers();
}
}
The problem I am having now is that smf.getMap() is returning null. Any ideas?
Edit2:
Got it to work by changing:
com.google.android.gms.maps.MapFragment
to
com.google.android.gms.maps.SupportMapFragment
Maybe:
<fragment
class="com.mycompany.MyCustomFragmentExtendingMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
or nest fragments like here: http://code.google.com/p/gmaps-api-issues/issues/detail?id=5064#c1
Related
One activity (MainActivity) calls a fragment(MainPage). The fragment is designed to open a local asset webview file, Map.html. It works as intended, with the exception that the map cannot be moved by fingertip. This function is available when the Map.html is opened a broswer. However, the zoom in zoom out tap button in the top left does work.
Is something overlaying the webview such that it can be seen but not swiped?
The initial AppCompatActivity (to support ActionBar) is given by MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
//list of private declarations
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// a lot of other stuff relating to navigation drawer
// calls MainPage Fragment
android.support.v4.app.Fragment fragment = new MainPage();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.addToBackStack("tag")
.commit();
}
}
where the xml for this is given as:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_centerVertical="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/content_frame">
</WebView>
<ListView
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#111">
</ListView>
</android.support.v4.widget.DrawerLayout>
</LinearLayout>
Eventually calls MainPage, given as:
public class MainPage extends android.support.v4.app.Fragment implements GeolocationPermissions.Callback {
public MainPage() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_main, container, false);
WebView webview = (WebView) rootView.findViewById(R.id.content_frame);
webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webview.getSettings().setGeolocationEnabled(true);
GeoClient geo = new GeoClient();
webview.setWebChromeClient(geo);
String origin = "";
geo.onGeolocationPermissionsShowPrompt(origin, this);
webview.loadUrl("file:///android_asset/Map.html");
return rootView;
}
You may need to set an onclicklistener. For Google Maps, this is how I did it. Its probably similar.
Add the implements, #override function, and set the onclick listener. I'm not sure if this will work since you are using HTML at some point.
public class MainActivity extends FragmentActivity implements View.OnClickListener,
GoogleMap.OnMapClickListener {
Then when you initiate it set the onmap click listener.
gMap.setOnMapClickListener(this);// add the listener for click on gmap object
Finally, add this #override:
#Override
public void onMapClick(LatLng point) {
//What code if any happens when user clicks on map
}
I need a search toolbar on Google Map.
I display the map with the use of MapActivity.
The way I did is via the create project wizard, when you a create a new project, you can choose from basic activity, empty activity, map activity.
I selected MapActivity.
In XML file I had only this:
<resources>
<string name="google_maps_key" templateMergeStrategy="preserve"
translatable="false">AIzaSyCx3iR5fkeWr2LUf6JBHwGLEEkeghL3snk
</string>
</resources>
So what is the way to insert that search toolbar on the map?
Then how to add that search tool bar. If I add a layout then an error it is shown.
The way I would do it is the following:
I would have an activity which has a fragment that contain the map inside.
Inside the activity I would have a method that display the fragment with the map:
public void showMap() {
MapLocationFragment locationFragment = MapLocationFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, locationFragment)
.addToBackStack(null)
.commit();
}
activity xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/app_bar_include"
layout="#layout/app_bar_layout_device" />
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize" />
</RelativeLayout>
Now, inside the MapLocationFragment you need to have something like that:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.maps_layout, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initializeMapIfNeeded();
}
private void initializeMapIfNeeded() {
if (googleMap == null) {
loadMapAsync();
}
}
private void loadMapAsync() {
((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.location_map)).getMapAsync(getOnMapReadyCallback());
}
public OnMapReadyCallback getOnMapReadyCallback() {
return new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap map) {
googleMap = map;
if (googleMap == null) {
return;
}
googleMap.setIndoorEnabled(true);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
googleMap.setOnMyLocationButtonClickListener(getMapMpOnMyLocationClickListener());
googleMap.setLocationSource(getNewLocationSource());
googleMap.setOnCameraChangeListener(getCameraChangeListener());
//do other stuff too
}
};
}
xml:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<fragment
android:id="#+id/location_map"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:src="#android:drawable/ic_menu_set_as"
app:layout_anchorGravity="bottom|right|end"
android:layout_gravity="bottom|end"/>
</android.support.design.widget.CoordinatorLayout>
For more details look at this project:
Sunshine app
You can try it.
public class MapsActivity extends FragementActivity implements OnMapReadyCallback,LocationListener in here, Put the AppCompatActivity instead of the FragementActivity.
I having trouble implementing Google Map in Fragment.
This is my the part of my fragment class:
public class FragmentStoreFinderMap extends Fragment implements OnMapReadyCallback {
// Google Map
private GoogleMap googleMap;
private int i;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
googleMap = ((SupportMapFragment)getActivity().getSupportFragmentManager().findFragmentById(
R.id.map)).getMapAsync(this);
I am getting an error in getMapAsync(this). I tells Incompatible type.
it says:
required: com.google.android.gms.map.GoogleMap
found: void
BTW here is my xml:
<?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.google.android.gms.maps.SupportMapFragment"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
In your XML layout you should do like this
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.gms.maps.MapView
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
inside your fragment implement this
private MapView mapView;
private GoogleMap googleMap;
override onCreateView if there is no onCreateView just like the code below
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.your_layout, container, false);
}
override onViewCreated() inside that
onViewCreated() put this
mapView = (MapView) view.findViewById(R.id.map);
mapView.onCreate(savedInstanceState);
mapView.onResume();
mapView.getMapAsync(this);//when you already implement OnMapReadyCallback in your fragment
when you already implement OnMapReadyCallback in your fragment put this/code like this
#Override
public void onMapReady(GoogleMap map) {
googleMap = map;
}
hope this helps you.
After a few overheats in my head, thanks to this post and https://stackoverflow.com/a/34732804/3925554, I would say that currently there are two ways of adding a MapFragment to your app:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
class="com.google.android.gms.maps.SupportMapFragment"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
By this way, you have to use a standard fragment and implement this inside. Actually you are inserting a fragment into another:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SupportMapFragment supportMapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
supportMapFragment.getMapAsync(this);
}
The other way would be extending the real SupportMapFragment and implement it in this manner without the need of an xml:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getMapAsync(this);
}
And they work without having to set a layout in onCreateView neither call mapView.onCreate(savedInstanceState); and mapView.onResume(); manually inside onViewCreated(), what is not recommended anyway.
Hope it helps!
SupportMapFragment mMapFragment = SupportMapFragment.newInstance();
FragmentTransaction fragmentTransaction =
getChildFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.flMap, mMapFragment);
fragmentTransaction.commit();
mMapFragment.getMapAsync(this);
Your code is returning this error because getMapAsync() does not return anything. If you look at the documentation for this function:
public void getMapAsync (OnMapReadyCallback callback)
Sets a callback object which will be triggered when the GoogleMap
instance is ready to be used.
Note that:
This method must be called from the main thread. The callback will be
executed in the main thread. In the case where Google Play services is
not installed on the user's device, the callback will not be triggered
until the user installs it. In the rare case where the GoogleMap is
destroyed immediately after creation, the callback is not triggered.
The GoogleMap object provided by the callback is non-null.
OnMapReadyCallback is an interface that needs implemented and passed to through this function. Nothing is currently assigned to your googleMap variable you should instead set its value in this block of code which implements OnMapReadyCallback
#Override
public void onMapReady(GoogleMap googleMap) {
this.googleMap = googleMap;
}
This code should be in your Fragment somewhere. Since your Fragment already implements this interface you do pass it in as this.
So, I'm following the steps in the Android API to add login to my app. My app is already fairly developed, and of course I'm no to android, so I'm having issues at this point.
the step reads:
Then set up the button in your UI by adding it to a fragment and update your activity to use your fragment.
I've searched for how to add a fragment and anything about that, watched youtube videos, but can't find out exactly what this means. Can anyone dumb this down for me?
https://developers.facebook.com/docs/facebook-login/android
First, you need a hosting activity, extending AppCompatActivity that has a FrameLayout in its related layout file. Then you inflate the layout in the onCreate method and add the needed Fragment:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.fragment_placeholder) != null) {
if (savedInstanceState != null) {
return;
}
// we are extending AppCompatActivity, so we need supportFragmentManager
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_placeholder, new ContentFragment()).commit();
}
}
}
The according layout file activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>`
Now you need to define ContentFragment:
public class RecordFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.fragment_content, container, false);
}
}
The last step is the layout file for our fragment, fragment_content, containing a Button:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
For more information why we use AppCompatActivity, read this answer.
I'm trying to put 2 fragments inside a fragment. I found some code on internet but, as far as I could go, I don't succeed to put 2 fragments in 1 fragment.
I have seen tips dealing with FragmentManager and especially the method getChildFragmentManager() but I don't know how it works with 2 fragments.
For the story, I'm using an activity with ActionBar which creates 3 fragments. In one of them, I need to handle a graph and a kind of menu to change the graph scale. In this way, I need 2 fragments in one fragment.
Here is the code :
The fragment which handles the others:
public class GraphDisplayFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myFragmentView = inflater.inflate(R.layout.graph_fragment, container, false);
return myFragmentView;
}
}
The code to draw the graph:
public class GraphFragment extends Fragment {
private static final int SERIES_NR = 1;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
GraphicalView myFragmentView = ChartFactory.getTimeChartView(this.getActivity(), getDateDemoDataset(), getDemoRenderer(),null);
return myFragmentView;
}
//some functions to set graph propreties
}
The XML files :
graph_fragment.xml
<?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
android:id="#+id/graph_fragment"
android:name="com.test.GraphFragment"
android:layout_width="match_parent"
android:layout_height="259dp" >
</fragment>
<fragment
android:name="com.test.GraphDetailFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/graph_detail_fragment">
</fragment>
</LinearLayout>
graph_detail.xml with a test implementation
<?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" >
<TextView
android:id="#+id/textView1"
android:layout_width="211dp"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
The weird thing is, it works at the begining when I switch between fragments in the ActionBar but after 3-4 moves, I get this error :
android.view.InflateException: Binary XML file line #7: Error inflating class fragment
If anyone has the solution, it would be awesome!
So first off change your xml for graph_fragment.xml to this
<?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" >
<FrameLayout
android:id="#+id/graph_fragment_holder"
android:layout_width="match_parent"
android:layout_height="259dp" >
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/graph_detail_fragment_holder">
</FrameLayout>
</LinearLayout>
Then in code to inflate them from the fragment use something like this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.graph_fragment,
container, false);
FirstChildFragment frag1 = (FirstChildFragment) getChildFragmentManager()
.findFragmentById(R.id.first_frag_layout);
SecondChildFragment frag1 = (SecondChildFragment) getChildFragmentManager()
.findFragmentById(R.id.second_frag_layout);
if (null == frag1) {
FirstChildFragment = new frag1();
FragmentTransaction transaction = getChildFragmentManager()
.beginTransaction();
transaction.add(R.id.graph_fragment_holder, frag1)
.addToBackStack(null).commit();
}
if (null == frag2) {
SecondChildFragment = new frag2();
FragmentTransaction transaction = getChildFragmentManager()
.beginTransaction();
transaction.add(R.id.graph_detail_fragment_holder, frag2)
.addToBackStack(null).commit();
}
return rootView;
}
MyFragment myFragment = (MyFragment)getChildFragmentManager().findFragmentById(R.id.my_fragment);
You can try to use the method *findFragmentById(fragment_id)* from SupportFragmentManager to get the instance of fragment from your .xml file and insert your fragment there.
Something like:
Fragment myFragment = getSupportFragmentManager().findFragmentById(R.id.myFragmentId);
I hope to help you.
Best regards.
Adriano