MapView в фрагменте (соты)
теперь, когда окончательный SDK вышел с google apis - какой лучший способ создать фрагмент с помощью MapView? MapView нуждается в MapActivity для правильной работы.
Имея действие, управляющее фрагментами, наследуется от MapActivity (плохое решение, потому что оно противоречит идее, что фрагменты самодостаточны), и использование обычного макета на основе xml не работает. Я получаю исключение NullPointerException в MapActivity.setupMapView():
E/AndroidRuntime( 597): Caused by: java.lang.NullPointerException
E/AndroidRuntime( 597): at com.google.android.maps.MapActivity.setupMapView(MapActivity.java:400)
E/AndroidRuntime( 597): at com.google.android.maps.MapView.(MapView.java:289)
E/AndroidRuntime( 597): at com.google.android.maps.MapView.(MapView.java:264)
E/AndroidRuntime( 597): at com.google.android.maps.MapView.(MapView.java:247)
Моя вторая идея заключалась в том, чтобы программно создать MapView и передать связанную активность (через getActivity()) в качестве Контекста к конструктору MapView. Не работает:
E/AndroidRuntime( 834): Caused by: java.lang.IllegalArgumentException: MapViews can only be created inside instances of MapActivity.
E/AndroidRuntime( 834): at com.google.android.maps.MapView.(MapView.java:291)
E/AndroidRuntime( 834): at com.google.android.maps.MapView.(MapView.java:235)
E/AndroidRuntime( 834): at de.foo.FinderMapFragment.onCreateView(FinderMapFragment.java:225)
E/AndroidRuntime( 834): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:708)
E/AndroidRuntime( 834): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:900)
E/AndroidRuntime( 834): at android.app.FragmentManagerImpl.addFragment(FragmentManager.java:978)
E/AndroidRuntime( 834): at android.app.Activity.onCreateView(Activity.java:4090)
E/AndroidRuntime( 834): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:664)
Действительно, должно быть что-то вроде MapFragment, которое заботится о фоновых потоках, которые нужны MapView, я думаю... Итак, какова нынешняя лучшая практика для этого?
Спасибо и привет от Германии,
Valentin
Ответы
Ответ 1
Начиная с 03.12.2012 Google выпустил API Google Maps Android v2. Теперь вы можете забыть об этих проблемах.
https://developers.google.com/maps/documentation/android/
Пример использования нового API - https://developers.google.com/maps/documentation/android/start#add_a_map
Этот API будет работать как минимум для Android API 8, поэтому используйте его;).
Итак, теперь вы можете просто использовать класс фрагментов com.google.android.gms.maps.MapFragment. Он отобразит карту в вашей деятельности. Пример компоновки по ссылке выше:
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"/>
Ответ 2
Мне удалось решить это, используя TabHost в фрагменте.
Вот идея (кратко):
-
MainFragmentActivity
extends FragmentActivity
(из библиотеки поддержки) и имеет MapFragment
.
-
MyMapActivity
extends MapActivity
и содержать MapView
.
-
LocalActivityManagerFragment
hosts LocalActivityManager
-
MapFragment
extends LocalActivityManagerFragment
.
-
И LocalActivityManager
содержит в нем MyMapActivity
активность.
Пример реализации: https://github.com/inazaruk/map-fragment.
![enter image description here]()
Ответ 3
Как обсуждалось в Группах Google, Питер Дойл создал специальную библиотеку совместимости, поддерживающую Карты Google. android-support-v4-googlemaps
Однако есть и недостаток:
В настоящее время одним недостатком является то, что ВСЕ классы, расширяющие FragmentActivity, являются MapActivitys. Его можно сделать отдельным классом (т.е. FragmentMapActivity), но он требует некоторого рефакторинга кода FragmentActivity.
Ответ 4
Просто, чтобы прояснить ответ. Я пробовал подход, предложенный иназаруком и Кристофом. На самом деле вы можете запускать любую деятельность в фрагменте, а не только на картах Google. Вот код, который реализует активность карты google как фрагмент благодаря иназаруку и ChristophK.
import com.actionbarsherlock.app.SherlockFragment;
import android.view.Window;
import android.app.LocalActivityManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MapFragment extends SherlockFragment {
private static final String KEY_STATE_BUNDLE = "localActivityManagerState";
private LocalActivityManager mLocalActivityManager;
protected LocalActivityManager getLocalActivityManager() {
return mLocalActivityManager;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle state = null;
if (savedInstanceState != null) {
state = savedInstanceState.getBundle(KEY_STATE_BUNDLE);
}
mLocalActivityManager = new LocalActivityManager(getActivity(), true);
mLocalActivityManager.dispatchCreate(state);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//This is where you specify you activity class
Intent i = new Intent(getActivity(), GMapActivity.class);
Window w = mLocalActivityManager.startActivity("tag", i);
View currentView=w.getDecorView();
currentView.setVisibility(View.VISIBLE);
currentView.setFocusableInTouchMode(true);
((ViewGroup) currentView).setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
return currentView;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBundle(KEY_STATE_BUNDLE,
mLocalActivityManager.saveInstanceState());
}
@Override
public void onResume() {
super.onResume();
mLocalActivityManager.dispatchResume();
}
@Override
public void onPause() {
super.onPause();
mLocalActivityManager.dispatchPause(getActivity().isFinishing());
}
@Override
public void onStop() {
super.onStop();
mLocalActivityManager.dispatchStop();
}
@Override
public void onDestroy() {
super.onDestroy();
mLocalActivityManager.dispatchDestroy(getActivity().isFinishing());
}
}
Ответ 5
Отличные новости от Google по этому поводу. Сегодня они выпускают новый API Карт Google, с картами внутри помещений и MapFragment.
With this new API, adding a map to your Activity is as simple as:
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
Ответ 6
API Карт Google не является частью AOSP. Пока Гуглер не отвечает, едва ли можно сказать, будет ли MapFragment в будущем.
Возможной ограниченной альтернативой является использование WebViewFragment
и злоупотребление ею для загрузки пользовательского URL maps.google.com
.
Ответ 7
Слишком плохо, что Google еще не ответил. FWIW, если вам действительно нужно это сделать, я не нашел другого способа, чем:
Наследуйте действия по управлению вкладками из MapActivity, создайте MapView там программно, используйте mapfragment.xml, содержащий ViewGroup, и добавьте MapView в ViewGroup, используя
((ViewGroup) getFragmentManager().findFragmentById(R.id.finder_map_fragment).getView()).addView(mapView);;
Ясно, что это сильно противоречит идее о том, что фрагменты являются самодостаточными, но...
Ответ 8
Здесь версия MonoDroid (Моно для Android) очень упрощенного MapFragment:
public class MapFragment : Fragment
{
// FOLLOW http://stackoverflow.com/questions/5109336/mapview-in-a-fragment-honeycomb
private static String KEY_STATE_BUNDLE = "localActivityManagerState";
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Bundle state = null;
if (savedInstanceState != null) {
state = savedInstanceState.GetBundle(KEY_STATE_BUNDLE);
}
mLocalActivityManager = new LocalActivityManager(Activity, true);
mLocalActivityManager.DispatchCreate(state);
}
public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//This is where you specify you activity class
Intent i = new Intent(Activity, typeof(SteamLocationMapActivity));
Window w = mLocalActivityManager.StartActivity("tag", i);
View currentView=w.DecorView;
currentView.Visibility = ViewStates.Visible;
currentView.FocusableInTouchMode = true;
((ViewGroup) currentView).DescendantFocusability = DescendantFocusability.AfterDescendants;
return currentView;
}
private LocalActivityManager mLocalActivityManager;
protected LocalActivityManager GetLocalActivityManager() {
return mLocalActivityManager;
}
public override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutBundle(KEY_STATE_BUNDLE,mLocalActivityManager.SaveInstanceState());
}
public override void OnResume()
{
base.OnResume();
mLocalActivityManager.DispatchResume();
}
public override void OnPause()
{
base.OnPause();
mLocalActivityManager.DispatchPause(Activity.IsFinishing);
}
public override void OnStop()
{
base.OnStop();
mLocalActivityManager.DispatchStop();
}
}
Ответ 9
Это решает мою проблему при добавлении MapView во Фрагменты.
https://github.com/petedoyle/android-support-v4-googlemaps
Ответ 10
В новой версии ABS 4.0 нет поддержки для MapFragmentActivity, вот хорошее решение для отображения карты в фрагменте!
https://xrigau.wordpress.com/2012/03/22/howto-actionbarsherlock-mapfragment-listfragment/#comment-21
Ответ 11
Могу ли я получить решение:
- создать класс TempFragmentActivity расширяет MapActivity
- есть объект MapView внутри TempFragmentActivity (например, normal define в xml)
- удалить эту родительскую форму MapView parent (LinearLayout) (исключение void позже)
- сохранить этот объект MapView где-нибудь (ex: статический член TempFragmentActivity)
- в вашем фрагменте добавьте этот объект MapView с помощью кода (не определяйте в xml) в некоторый LinearLayout
Ответ 12
Я написал небольшую библиотеку, замалчивая решения LocalActivityManager на проблему MapFragment (также включает пример приложения, показывающий различные ситуации использования):
https://github.com/coreform/android-tandemactivities