MapFragment во фрагменте, альтернативы?
Мне нужна твоя помощь... Я работаю над этим до 3 дней. Мое приложение работает с фрагментами. Один из этих фрагментов должен отображать карту из API Google Maps V2 для Android.
В настоящее время я использую MapFragment, но неудивительно, что фрагмент во фрагменте не очень хорошая идея, но он работает, карта отображается, я могу редактировать ее, но когда я переключаю основной фрагмент и возвращаюсь к нему.
Вызывается: java.lang.IllegalArgumentException: строка двоичного XML-файла #59: дублирующийся идентификатор 0x7f070041, нулевой тег или родительский идентификатор 0x7f070040 с другим фрагментом для com.google.android.gms.maps.MapFragment
на android.app.Activity.onCreateView(Activity.java:4252)
на android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:673)
Это причина, когда я перехожу к другому фрагменту и возвращаюсь к тому, который содержит карту. Я ищу до 3 дней, чтобы исправить это, но без особых результатов.
Чтобы возобновить для вас, у меня есть Activity, которая вызывает фрагмент, который содержит MapFragment в файле макета. Если вам нужно больше, просто спросите:)
Спасибо
Изменить: вот код для изменения фрагмента в основной деятельности
private void swtichFragment(Fragment fragment, Bundle bundle)
{
fragment.setBundle(this, bundle);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.rightFragmentPlaceHolder, fragment);
fragmentTransaction.commit();
mRightFragment = fragment;
}
5 ответов
Используйте MapView вместо MapFragment в макете вашего фрагмента. Не забудьте вызвать методы жизненного цикла MapView:
- OnCreate (Bundle)
- onResume ()
- OnPause()
- OnDestroy ()
- onSaveInstanceState (Bundle)
- onLowMemory ()
как описано здесь.
Btw. Вы не должны использовать MapFragment, только SupportMapFragment и библиотеку поддержки.
Редактировать:
Если вы переключитесь на поддержку библиотеки, вы можете использовать код из комментария № 1 здесь: http://code.google.com/p/gmaps-api-issues/issues/detail?id=5064
Используйте SupportMapFragment, чтобы преодолеть эту ошибку:
Во фрагментном макете
<fragment
android:id="@+id/googleMap"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
В вашем фрагменте onCreateView
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.googleMap);
if (mapFragment != null) {
mapFragment.getMapAsync(this);
}
Так как ваш макет находится в Fragment's
макет, поэтому SupportMapFragment
это дочерний макет вашего фрагмента. Следовательно, используйте getChildFragmentManager()
который Fragment's FragmentManager
Чтобы отобразить MapFragment внутри фрагмента (NestedFragment): На данный момент, я полагаю, у вас есть
- добавлено необходимое разрешение на манифест
- добавлен сервис google play в качестве проекта lib
- Ключ API в файле манифеста. 4.
where.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.03"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/mapwhere" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
учебный класс:
public class WhereFragment extends SupportMapFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.where, null, false);
initilizeMap();
return root;
}
private void initilizeMap()
{
mSupportMapFragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.mapwhere);
if (mSupportMapFragment == null) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
mSupportMapFragment = SupportMapFragment.newInstance();
fragmentTransaction.replace(R.id.mapwhere, mSupportMapFragment).commit();
}
if (mSupportMapFragment != null)
{
googleMap = mSupportMapFragment.getMap();
if (googleMap != null)
googleMap.setOnMapClickListener(new OnMapClickListener()
{
@Override
public void onMapClick(LatLng point)
{
//TODO: your onclick stuffs
}
});
}
}
}
Документация:
Вложенные фрагменты Теперь вы можете вставлять фрагменты внутри фрагментов. Это полезно для множества ситуаций, в которых вы хотите поместить динамические и повторно используемые компоненты пользовательского интерфейса в компонент пользовательского интерфейса, который сам по себе является динамическим и может использоваться повторно. Например, если вы используете ViewPager для создания фрагментов, которые смахивают влево и вправо и занимают большую часть экранного пространства, теперь вы можете вставлять фрагменты в каждую страницу фрагмента.
Чтобы вложить фрагмент, просто вызовите getChildFragmentManager() для фрагмента, в который вы хотите добавить фрагмент. Это возвращает FragmentManager, который вы можете использовать, как обычно, из действия верхнего уровня для создания транзакций фрагмента. Например, вот некоторый код, который добавляет фрагмент из существующего класса Fragment:
Fragment videoFragment = new VideoPlayerFragment (); FragmentTransactionaction = getChildFragmentManager(). BeginTransaction(); action.add(R.id.video_fragment, videoFragment).commit(); Из вложенного фрагмента вы можете получить ссылку на родительский фрагмент, вызвав getParentFragment().
Библиотека поддержки Android также теперь поддерживает вложенные фрагменты, так что вы можете реализовать проекты с вложенными фрагментами на Android 1.6 и выше.
Примечание. Вы не можете раздувать макет на фрагмент, если этот макет включает в себя. Вложенные фрагменты поддерживаются только при динамическом добавлении к фрагменту.
источник: http://developer.android.com/about/versions/android-4.2.html
Это также исправит для:
11-06 11:36:01.509: E/AndroidRuntime(6309): FATAL EXCEPTION: main
11-06 11:36:01.509: E/AndroidRuntime(6309): android.view.InflateException: Binary XML file line #9: Error inflating class fragment
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:710)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.rInflate(LayoutInflater.java:752)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.rInflate(LayoutInflater.java:760)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.rInflate(LayoutInflater.java:760)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.rInflate(LayoutInflater.java:760)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.rInflate(LayoutInflater.java:760)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.inflate(LayoutInflater.java:495)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
11-06 11:36:01.509: E/AndroidRuntime(6309): at com.abc.android.ui.WhereFragment.onCreateView(WhereFragment.java:60)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.support.v4.app.Fragment.performCreateView(Fragment.java:1500)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
11-06 11:36:01.509: E/AndroidRuntime(6309): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
11-06 11:36:01.509: E/AndroidRuntime(6309): at a ...
В вашем классе
SupportMapFragment mSupportMapFragment;
private GoogleMap googleMap;
int ZOOM_LEVEL=15;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View mTrackView = inflater
.inflate(R.layout.mylayout, container, false);
mSupportMapFragment = SupportMapFragment.newInstance();
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.mapwhere, mSupportMapFragment);
fragmentTransaction.commit();
return mTrackView;
}
@Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
if(mSupportMapFragment!=null){
googleMap = mSupportMapFragment.getMap();
if(googleMap!=null){
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
googleMap.getUiSettings().setMyLocationButtonEnabled(false);
googleMap.setMyLocationEnabled(false);
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(
new LatLng(12.12122,
17.22323), ZOOM_LEVEL);
googleMap.animateCamera(cameraUpdate);
}
}
}
mylayout.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.03"
android:id="@+id/mapwhere" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
В вашем файле макета
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
В вашем фрагменте onCreate()
, ссылка на фрагмент карты в вашем файле макета фрагмента, используя childFragmentManager
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment: SupportMapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
// Set callback on the fragment
mapFragment.getMapAsync(this)
После многих ошибок я наконец сделал это, вот мой класс фрагмента MapView:-
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.serveroverload.yago.R;
/**
* @author 663918
*
*/
public class HomeFragment extends Fragment implements LocationListener {
// Class to do operations on the Map
GoogleMap googleMap;
private LocationManager locationManager;
public static Fragment newInstance() {
return new HomeFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home_fragment, container, false);
Bundle bdl = getArguments();
// setuping locatiomanager to perfrom location related operations
locationManager = (LocationManager) getActivity().getSystemService(
Context.LOCATION_SERVICE);
// Requesting locationmanager for location updates
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1, 1, this);
// To get map from MapFragment from layout
googleMap = ((MapFragment) getActivity().getFragmentManager()
.findFragmentById(R.id.map)).getMap();
// To change the map type to Satellite
// googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
// To show our current location in the map with dot
// googleMap.setMyLocationEnabled(true);
// To listen action whenever we click on the map
googleMap.setOnMapClickListener(new OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
/*
* LatLng:Class will give us selected position lattigude and
* longitude values
*/
Toast.makeText(getActivity(), latLng.toString(),
Toast.LENGTH_LONG).show();
}
});
changeMapMode(3);
// googleMap.setSatellite(true);
googleMap.setTrafficEnabled(true);
googleMap.setBuildingsEnabled(true);
googleMap.setMyLocationEnabled(true);
return v;
}
private void doZoom() {
if (googleMap != null) {
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(18.520430, 73.856744), 17));
}
}
private void changeMapMode(int mapMode) {
if (googleMap != null) {
switch (mapMode) {
case 0:
googleMap.setMapType(GoogleMap.MAP_TYPE_NONE);
break;
case 1:
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
break;
case 2:
googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
break;
case 3:
googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
break;
case 4:
googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
break;
default:
break;
}
}
}
private void createMarker(double latitude, double longitude) {
// double latitude = 17.385044;
// double longitude = 78.486671;
// lets place some 10 random markers
for (int i = 0; i < 10; i++) {
// random latitude and logitude
double[] randomLocation = createRandLocation(latitude, longitude);
// Adding a marker
MarkerOptions marker = new MarkerOptions().position(
new LatLng(randomLocation[0], randomLocation[1])).title(
"Hello Maps " + i);
Log.e("Random", "> " + randomLocation[0] + ", " + randomLocation[1]);
// changing marker color
if (i == 0)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
if (i == 1)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
if (i == 2)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_CYAN));
if (i == 3)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
if (i == 4)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
if (i == 5)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
if (i == 6)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_RED));
if (i == 7)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_ROSE));
if (i == 8)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_VIOLET));
if (i == 9)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW));
googleMap.addMarker(marker);
// Move the camera to last position with a zoom level
if (i == 9) {
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(randomLocation[0], randomLocation[1]))
.zoom(15).build();
googleMap.animateCamera(CameraUpdateFactory
.newCameraPosition(cameraPosition));
}
}
}
/*
* creating random postion around a location for testing purpose only
*/
private double[] createRandLocation(double latitude, double longitude) {
return new double[] { latitude + ((Math.random() - 0.5) / 500),
longitude + ((Math.random() - 0.5) / 500),
150 + ((Math.random() - 0.5) * 10) };
}
@Override
public void onLocationChanged(Location location) {
if (null != googleMap) {
// To get lattitude value from location object
double latti = location.getLatitude();
// To get longitude value from location object
double longi = location.getLongitude();
// To hold lattitude and longitude values
LatLng position = new LatLng(latti, longi);
createMarker(latti, longi);
// Creating object to pass our current location to the map
MarkerOptions markerOptions = new MarkerOptions();
// To store current location in the markeroptions object
markerOptions.position(position);
// Zooming to our current location with zoom level 17.0f
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(position,
17f));
// adding markeroptions class object to the map to show our current
// location in the map with help of default marker
googleMap.addMarker(markerOptions);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
locationManager.removeUpdates(this);
android.app.Fragment fragment = getActivity().getFragmentManager()
.findFragmentById(R.id.map);
if (null != fragment) {
android.app.FragmentTransaction ft = getActivity()
.getFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commit();
}
}
}
Мой XML-файл выглядит так:
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
И результат выглядит так:
Самое важное, на что следует обратить внимание, - это то, что приложение "Не смешивать". Фрагмент с приложением v4.Fragments, в противном случае произойдет сбой.
Как вы можете видеть, я использовал app.Fragment, чтобы прикрепить и удалить мой фрагмент MapView.
Надеюсь, это кому-нибудь поможет
Вы можете попробовать добавить MapFragment внутри FragmentContainerView
из androidx
Fragment
пакет:
- FragmentContainerView
- Из:
implementation "androidx.fragment:fragment:$1.2.4"
- Проверить: https://developer.android.com/jetpack/androidx/releases/fragment
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/location_map"
android:layout_above="@id/atmLocation_recyclerView"
/>
View root= inflater.inflate(R.layout.fragment_a_t_m_locations, container, false);
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.location_map);
mapFragment.getMapAsync(googleMap -> {
mMap=googleMap;
if(mMap!=null){
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
}
});