Android OSM in App einbinden

LucasZL

Mitglied
Hi,
ich versuche OpenStreetMap in eine Fragment Klasse einzubinden:

Java:
import org.osmdroid.views.MapController;
import org.osmdroid.views.MapView;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class fragmentC extends Fragment {
	
	private MapView mapView;
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		
		this.mapView = (MapView) getActivity().findViewById(R.id.openmapview);
		
		mapView.setBuiltInZoomControls(true);
		mapView.setMultiTouchControls(true);
		MapController controller = mapView.getController();
		controller.setZoom(4);
		
		return inflater.inflate(R.layout.fragmentc, container, false);
	
	}
	
	

}

Nehme ich alles mit Map aus der Activity öffnet sich zwar die Map nur so kann ich ja nichts darstellen/personalisieren.

XML:[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:eek:rientation="vertical"
android:background="@drawable/bckgr">

<org.osmdroid.views.MapView
android:id="@+id/openmapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

</LinearLayout>[/XML]

Exception:
11-05 13:58:49.488: E/AndroidRuntime(834): FATAL EXCEPTION: main
11-05 13:58:49.488: E/AndroidRuntime(834): java.lang.NullPointerException
11-05 13:58:49.488: E/AndroidRuntime(834): at com.goh.tablet.fragmentC.onCreateView(fragmentC.java:23)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.Fragment.performCreateView(Fragment.java:1695)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:885)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1057)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.BackStackRecord.run(BackStackRecord.java:682)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.FragmentManagerImpl$1.run(FragmentManager.java:441)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.os.Handler.handleCallback(Handler.java:725)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.os.Handler.dispatchMessage(Handler.java:92)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.os.Looper.loop(Looper.java:137)
11-05 13:58:49.488: E/AndroidRuntime(834): at android.app.ActivityThread.main(ActivityThread.java:5041)
11-05 13:58:49.488: E/AndroidRuntime(834): at java.lang.reflect.Method.invokeNative(Native Method)
11-05 13:58:49.488: E/AndroidRuntime(834): at java.lang.reflect.Method.invoke(Method.java:511)
11-05 13:58:49.488: E/AndroidRuntime(834): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
11-05 13:58:49.488: E/AndroidRuntime(834): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
11-05 13:58:49.488: E/AndroidRuntime(834): at dalvik.system.NativeStart.main(Native Method)
 

dzim

Top Contributor
Hilft es dir, wenn ich dir sage, dass es mit relativ wenig Aufwand möglich ist, einen Tile-Provider für die Google Maps API v2 zu schreiben (soweit ich gelesen habe, ist OSM wohl etwas "frickelig" zu integrieren). Nachteil der Variante wäre allerdings, dass du das Laden der Tiles sowie Caching selbst machen musst, auch ist die Zoomstufe dann nicht so gut (muss man bedenken und ein entsprechendes "no-such-tile"-png oder so liefern) und auch die Qualität ist vielleicht nicht so gut, wie die von OSM selbst...

Allerdings war der Aufwand bei mir mit ca. 8h (inkl. aller notwendiger Recherchen) jetzt auch jetzt nicht soooo schlimm - finde ich.

Wenn du interessiert bist, poste ich mal etwas Code; wenn nicht, dann eben nicht :-D

#edit: und mit Code-Schnipseln wahrscheinlich sogar noch geringer ;-)
 
Zuletzt bearbeitet:

LucasZL

Mitglied
Danke dann werd ich wohl auf Google Maps zurückgreifen müssen. Hab ich aber auch schonmal gemacht, wenn auch nur extern also durch öffnen der Maps App. Wenn du Maps richtig in deine App implementiert hast würd ich mich sehr über einen Code-Post freuen :toll:
 

dzim

Top Contributor
Wie du Google Maps in deine App integrierst, musst du im Internet suchen - da gibt es schon viele gute Beschreibungen. Du musst hierzu allerdings bei Google einen Key für deine App erstellen - Da kommst du nicht drum herumWenn du bei Google nach google maps api v2 android suchst, solltest du fündig werden - wie fast immer ist auch Tutorial von Lars Vogel mit am Start.

Zusammenfassend wären deine ersten Schritte:
  1. Google Play Services im SDK installieren
  2. diese als lib-Projekt in dein Eclipse importieren
    • via File > Import > Android > Existing Android Code Into Workspace
    • dann wähle <Android-SDK-Verzeichnis>/extras/google/google_play_services/libproject
    • importiere google-play-services_lib (Hacken vorne ran und Ok)
  3. diese lib deinem Projekt hinzufügen
    • Properties deines Projekts öffnen, Android
    • unten in der Gruppe Library dann Add... drücken und das lib-Projekt auswählen
  4. Manifest deines Projekts öffnen und nun Permissions u.s.w. hinzufügen
    Code:
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="ch.cnlab.speedtest.permission.MAPS_RECEIVE" />
    
    <permission
        android:name="ch.cnlab.speedtest.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />
    
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />
    
    <application
        [... deine Einstellungen zur App]>
    
        <uses-library android:name="com.google.android.maps" />
    
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="@string/keystore.map.debug" /> <!-- dein Key kann direkt hier drin stehen, ich hab ihn aber in der allg. strings.xml -->
    </application>
    • WRITE_EXTERNAL_STORAGE wegen dem Caching der OSM-Tiles
  5. aktuellste Support Library zum Projekt hinzufügen
    • rechter Mausklick auf's Projekt, Android Tools > Add Support Library... ...

Ich gehe jetzt davon aus, dass du dir bereits einen Key für deine App bei Google angelegt hast (der Package Name ist hier für Google entscheidend).
Jetzt ist der grösste Aufwand schon getan. :)

Lege dir jetzt ein layout für deine Map an - ich habe das in einem Layout gemacht, dass ich dann als Fragment in einem Tab-Container eingebunden habe:
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fragment_map"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- map:cameraTargetLat und -Lng beschreiben die initialen Koordinaten - hier die Schweiz -->
    <!-- -->
    <fragment
        android:id="@+id/fragment_mapview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/relativeLayout"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="5dp"
        class="com.google.android.gms.maps.SupportMapFragment"
        android:background="@android:color/darker_gray"
        map:cameraTargetLat="46.954012"
        map:cameraTargetLng="8.415527"
        map:cameraZoom="8"
        map:mapType="normal"
        map:uiCompass="true"
        map:uiTiltGestures="true"
        tools:ignore="MissingPrefix" />

</RelativeLayout>

Im Fragment geht es dann weiter. Ich musste hier etwas tricksen, da ich in dem Tab-Container ein paar Probleme mit der Map hatte. Verwende folgenden Code, solltest du auch Probleme mit der Map haben, denn diese herangehensweise ist nicht unbedingt die sauberste!

Java:
public class MapFragment extends Fragment {
	
	protected View mView;
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		
		// hack!!!
		if ((mView != null) && (mView.getParent() != null)) {
			((ViewGroup) mView.getParent()).removeView(mView);
		}
		if (mView == null) {
			mView = inflater.inflate(R.layout.fragment_map, container, false);
		}

		// das ist dann wieder ok :-)
		setUpMapIfNeeded();
	}

	protected void setUpMapIfNeeded() {
		
		FragmentManager fm = getFragmentManager();
		Fragment frag = fm.findFragmentById(R.id.fragment_mapview);
		
		if (frag == null) {
			if ((mView != null) && (mView.getParent() != null)) {
				((ViewGroup) mView.getParent()).removeView(mView);
				mView = null;
			}
			return;
		} else {
			if (mMap == null) {
				mMap = ((com.google.android.gms.maps.SupportMapFragment) frag).getMap();
			}
		}
		
		if (mMap != null) {
			
			mMap.setMyLocationEnabled(true);
			
			// die listener musst du jetzt bei Bedarf selbst machen
			// mMap.setOnCameraChangeListener(mOnCameraChangeListener);
			// mMap.setOnMapClickListener(mOnMapClickListener);
			// mMap.setOnMarkerClickListener(mOnMarkerClickListener);
			// mMap.setInfoWindowAdapter(new MyInfoWindowAdapter(getActivity()));
			// mMap.setOnInfoWindowClickListener(mOnInfoWindowClickListener);
			
			// ich erstelle meine Marker für die Map in einem AsyncTask,damit das UI nicht blockiert
			// if ((mInitTask != null) && !mInitTask.isCancelled()) {
			//	mInitTask.cancel(true);
			// }
			// mInitTask = new MarkerBuilderTask();
			// mInitTask.execute();
		}
	}

für dein Map-Set-Up empfehle ich dir, deine letzten Map-Optionen vielleicht im savedInstanceState zu speichern, oder alternativ eine Präferenz dafür anzulegen (falls du das jetzt noch nicht kennst: Google! :p ).

Jetzt geht es ans eingemachte: Um den Typ der Map auszutauschen, musst du dir ein Menü, einen Dialog, oder was auch immer, bauen, um via mMap.setMapType(newType); hier umzuschalten.
Ich gehe da grob etwa folgendermaßen vor:
Java:
		// der Code hier ist mehr oder weniger Teil des Set-Up!!!
		if (rgMapOptionView != null) {
			int type = PreferenceHelper.getMapOptionType(getActivity());
			if (mCustomTileOverlay != null) {
				mCustomTileOverlayOptions = null;
				mCustomTileOverlay.remove();
				mCustomTileOverlay = null;
			}
			if (mMap != null) {
				switch (type) {

				// Google-Types
				case GoogleMap.MAP_TYPE_NORMAL:
					mMap.setMapType(type);
					rgMapOptionView.check(R.id.map_option_view_def);
					break;
				case GoogleMap.MAP_TYPE_HYBRID:
					mMap.setMapType(type);
					rgMapOptionView.check(R.id.map_option_view_sat);
					break;
				case GoogleMap.MAP_TYPE_TERRAIN:
					mMap.setMapType(type);
					rgMapOptionView.check(R.id.map_option_view_top);
					break;

				// Custom - alsoo von mir
				case CustomMapType.MAP_TYPE_OSM:
					// hier kann auch ein weniger bescheuertes Konstrukt verwendent werden! :-D
					TileProvider osmTileProvider = CustomMapType.CustomTileProviderEnum.OSM.createTileProvider(getActivity());
					mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
					mCustomTileOverlayOptions = new TileOverlayOptions().tileProvider(osmTileProvider).zIndex(0);
					mCustomTileOverlay = mMap.addTileOverlay(mCustomTileOverlayOptions);
					mCustomTileOverlay.clearTileCache();
					mMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(46.954012, 8.415527)));
					rgMapOptionView.check(R.id.map_option_view_osm);
					break;

				// Fallback
				default:
					type = GoogleMap.MAP_TYPE_NORMAL;
					mMap.setMapType(type);
					rgMapOptionView.check(R.id.map_option_view_def);
					break;
				}
			}
			rgMapOptionView.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
				@Override
				public void onCheckedChanged(RadioGroup group, int checkedId) {
					if (mMap == null)
						return;
					if (mCustomTileOverlay != null) {
						mCustomTileOverlayOptions = null;
						mCustomTileOverlay.remove();
						mCustomTileOverlay = null;
					}
					switch (checkedId) {
					case R.id.map_option_view_def:
						mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
						PreferenceHelper.setMapOptionType(getActivity(), GoogleMap.MAP_TYPE_NORMAL);
						break;
					case R.id.map_option_view_sat:
						mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
						PreferenceHelper.setMapOptionType(getActivity(), GoogleMap.MAP_TYPE_HYBRID);
						break;
					case R.id.map_option_view_top:
						mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
						PreferenceHelper.setMapOptionType(getActivity(), GoogleMap.MAP_TYPE_TERRAIN);
						break;
					case R.id.map_option_view_osm:
						TileProvider osmTileProvider = CustomMapType.CustomTileProviderEnum.OSM.createTileProvider(getActivity());
						if (osmTileProvider != null) {
							mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
							mCustomTileOverlayOptions = new TileOverlayOptions().tileProvider(osmTileProvider).zIndex(0);
							mCustomTileOverlay = mMap.addTileOverlay(mCustomTileOverlayOptions);
							PreferenceHelper.setMapOptionType(getActivity(), CustomMapType.MAP_TYPE_OSM);
							break;
						}
						// otherwise we use the fall-through and create the default map
					default:
						mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
						PreferenceHelper.setMapOptionType(getActivity(), GoogleMap.MAP_TYPE_NORMAL);
						break;
					}
				}
			});
		}

Um es halbwegs elegant ( -.- ) zu lösen, habe ich hier übrigens ein Frame-Layout verwendet, das über der Map liegt und mittels Toggle-Button angezeigt, oder ausgeblendet wird.

So. Nun der TileProvider, der die eigentliche Arbeit macht. Im Code war bereits CustomMapType zu sehen: Ein häßliches Ding!

Java:
public class CustomMapType {
	
	public static final int MAP_TYPE_OSM = 101;
	
	public enum CustomTileProviderEnum {
		
		OSM(OSMCachedTileProvider.class, 18);
		
		private final Class<? extends TileProvider> customTileProviderClass;
		private final int maxZoomLevel;
		
		private CustomTileProviderEnum(final Class<? extends TileProvider> customTileProviderClass, final int maxZoomLevel) {
			this.customTileProviderClass = customTileProviderClass;
			this.maxZoomLevel = maxZoomLevel;
		}
		
		public int getMaxZoomLevel() {
			return maxZoomLevel;
		}
		
		public TileProvider createTileProvider(final Context context) {
			try {
				Constructor<? extends TileProvider> constructor = customTileProviderClass.getConstructor(Context.class);
				TileProvider tileProvider = constructor.newInstance(context);
				return tileProvider;
			} catch (NoSuchMethodException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (IllegalArgumentException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (InstantiationException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (IllegalAccessException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (InvocationTargetException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			}
			return null;
		}
		
		public TileProvider createTileProvider(final Context context, Integer width, Integer height) {
			try {
				Constructor<? extends TileProvider> constructor = customTileProviderClass.getConstructor(Context.class, Integer.class, Integer.class);
				TileProvider tileProvider = constructor.newInstance(context, width, height);
				return tileProvider;
			} catch (NoSuchMethodException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (IllegalArgumentException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (InstantiationException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (IllegalAccessException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			} catch (InvocationTargetException e) {
				Log.e(CustomTileProviderEnum.class.getSimpleName() + "#createTileProvider", e.getMessage(), e);
			}
			return null;
		}
	}
}

Caching macht also der OSMCachedTileProvider.

Java:
public class OSMCachedTileProvider implements TileProvider {
	
	private static final int TILE_WIDTH = 256;
	private static final int TILE_HEIGHT = 256;
	private static final int BUFFER_SIZE = 16 * 1024;
	
	private final Context mContext;
	
	private final int mTileWidth;
	private final int mTileHeight;
	
	private final OSMTileProvider mOnlineProvider;
	
	private boolean useCache = true;
	
	public OSMCachedTileProvider(final Context context, Integer width, Integer height) {
		this.mContext = context;
		this.mTileWidth = width;
		this.mTileHeight = height;
		this.mOnlineProvider = new OSMTileProvider(mContext, mTileWidth, mTileHeight);
	}
	
	public OSMCachedTileProvider(final Context context) {
		this(context, TILE_WIDTH, TILE_HEIGHT);
	}
	
	public OSMCachedTileProvider useCache(boolean useCache) {
		this.useCache = useCache;
		return this;
	}
	
	/* ----------------------------------------------------------------------------------------- */
	/* TileProvider implementation */
	/* ----------------------------------------------------------------------------------------- */
	
	@Override
	public Tile getTile(int x, int y, int zoom) {
		
		Tile tile = null;
		
		if (useCache) {
			// read from cache
			tile = getCachedTile(x, y, zoom);
			if (tile != null)
				return tile;
		}
		
		// otherwise read from online provider
		tile = mOnlineProvider.getTile(x, y, zoom);
		
		// check, if the tile is still not initialized
		if (tile == null || NO_TILE.equals(tile)) {
			tile = getAssetNoTile();
		} else {
			if (useCache) {
				cacheTileData(x, y, zoom, tile);
			}
		}
		return tile;
	}
	
	/* ----------------------------------------------------------------------------------------- */
	/* private helper methods for the implementation */
	/* ----------------------------------------------------------------------------------------- */
	
	private void cacheTileData(int x, int y, int zoom, Tile tile) {
		
		File internalCacheDir = SpeedTestDBAccess.getInstance(mContext).getInternalCacheDir();
		if (internalCacheDir == null)
			return;
		File tileFile = new File(internalCacheDir, mContext.getString(R.string.mapFragment_tileProvider_cache_format, CustomMapType.MAP_TYPE_OSM, x, y,
				zoom, tile.width, tile.height));
		File osmTileDir = tileFile.getParentFile();
		
		if (!osmTileDir.isDirectory())
			if (!osmTileDir.mkdirs())
				return;
		
		// if there is a file already, remove it
		if (tileFile.isFile())
			if (!tileFile.delete())
				return;
		
		OutputStream out = null;
		try {
			out = new FileOutputStream(tileFile);
			out.write(tile.data);
			out.flush();
		} catch (IOException e) {
			Log.e(this.getClass().getSimpleName() + "#storeTileData", "Can't store tile data to file " + tileFile.getName() + ": " + e.getMessage(), e);
		} finally {
			if (out != null)
				try {
					out.close();
				} catch (Exception ignored) {
				}
		}
	}
	
	private Tile getCachedTile(int x, int y, int zoom) {
		
		File internalCacheDir = SpeedTestDBAccess.getInstance(mContext).getInternalCacheDir();
		if (internalCacheDir == null)
			return null;
		File tileFile = new File(internalCacheDir, mContext.getString(R.string.mapFragment_tileProvider_cache_format, CustomMapType.MAP_TYPE_OSM, x, y,
				zoom, TILE_WIDTH, TILE_HEIGHT));
		File osmTileDir = tileFile.getParentFile();
		
		if (!osmTileDir.isDirectory())
			return null;
		
		if (!tileFile.isFile()) {
			return null;
		} else {
			long now = System.currentTimeMillis();
			long then = tileFile.lastModified();
			long diff = now - then;
			Calendar c = Calendar.getInstance();
			c.setTimeInMillis(diff);
			// if the file is older then a week, tell the #getTile() method, that it need to be realoaded
			if (c.get(Calendar.DAY_OF_MONTH) > 7)
				return null;
		}
		
		InputStream in = null;
		try {
			in = new FileInputStream(tileFile);
			return new Tile(TILE_WIDTH, TILE_HEIGHT, readTileDataFromInputStream(in));
		} catch (IOException e) {
			Log.e(this.getClass().getSimpleName() + "#getCachedTile", "Can't read tile data: " + e.getMessage(), e);
			return null;
		}
	}
	
	private Tile getAssetNoTile() {
		
		InputStream in = null;
		try {
			in = mContext.getAssets().open(mContext.getString(R.string.mapFragment_tileProvider_NO_TILE));
			return new Tile(TILE_WIDTH, TILE_HEIGHT, readTileDataFromInputStream(in));
		} catch (IOException e) {
			Log.e(this.getClass().getSimpleName() + "#getAssetNoTile", "Can't read tile data: " + e.getMessage(), e);
			return null;
		}
	}
	
	private byte[] readTileDataFromInputStream(InputStream in) {
		if (in == null)
			return null;
		ByteArrayOutputStream buffer = null;
		try {
			buffer = new ByteArrayOutputStream();
			int nRead;
			byte[] data = new byte[BUFFER_SIZE];
			while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) {
				buffer.write(data, 0, nRead);
			}
			buffer.flush();
			return buffer.toByteArray();
		} catch (IOException e) {
			Log.e(this.getClass().getSimpleName() + "#readTileDataFromInputStream", "Can't read tile data: " + e.getMessage(), e);
			return null;
		} catch (OutOfMemoryError e) {
			Log.e(this.getClass().getSimpleName() + "#readTileDataFromInputStream", "Can't read tile data: " + e.getMessage(), e);
			return null;
		} finally {
			if (in != null)
				try {
					in.close();
				} catch (Exception ignored) {
				}
			if (buffer != null)
				try {
					buffer.close();
				} catch (Exception ignored) {
				}
		}
	}
}

Der "Trick", die eigentlichen Daten von OpenStreetMap heranzuziehen ist im OSMTileProvider zu finden:

Java:
public class OSMTileProvider extends UrlTileProvider {
	
	private static final int TILE_WIDTH = 256;
	private static final int TILE_HEIGHT = 256;
	
	private final Context mContext;
	private final String mBaseUrlFormat;
	
	public OSMTileProvider(final Context context, Integer width, Integer height) {
		super(width, height);
		this.mContext = context;
		this.mBaseUrlFormat = mContext.getString(R.string.mapFragment_tileProvider_osm);
	}
	
	public OSMTileProvider(final Context context) {
		this(context, TILE_WIDTH, TILE_HEIGHT);
	}
	
	@Override
	public URL getTileUrl(int x, int y, int zoom) {
		try {
			return new URL(String.format(mBaseUrlFormat, zoom, x, y));
		} catch (MalformedURLException e) {
			Log.e(OSMTileProvider.class.getSimpleName() + "#getTileUrl", e.getMessage());
			return null;
		}
	}
}

hier habe ich der Vollständigkeit halber noch die Zeicheketten aus der allg. strings.xml:
Code:
<string name="mapFragment.tileProvider.NO_TILE">img/map/no_tile_icon.png</string>
    <string name="mapFragment.tileProvider.cache.format" formatted="false">mapCache/%d/x%d_y%d_zoom%d_%dx%d.png</string>
    <string name="mapFragment.tileProvider.osm" formatted="false">http://tile.openstreetmap.org/%d/%d/%d.png</string>

OK, das ist jetzt ein ganzer Batzen! Ich empfehle dir jetzt, erst einmal die ersten Schritte bis zur Integration in deine App durchzuführen und den OSM-Teil erst einmal auszusparen. Ich schätze einen Tag Arbeit wirst du da bereits haben. Aber glaube mir: danach geht es eigentlich sehr schnell. Ich habe schnell ohne großen Aufwand Marker, Polylines (Pfade) und Kreise auf die Map gezimmert, ohne dass es mich dann noch unendlich viel Arbeit gekostet hätte. Das wichtigste ist dann nur es in AsyncTasks auszulagern und dass diese natürlich auch zügig sind - hierzu habe ich viele der notwendigen DB-Operationen (da ich mitunter Joins etc. benötige) zu Plain-SQL umgeschrieben. Aber das war nur in meinen Tests notwendig, in denen ich weit mehr Daten in der App habe, als Otto-Normal-Verbraucher es wohl je haben wird...


Viel Erfolg beim Umsetzen!
Daniel


PS: Wenn du das Ganze mal in Aktion sehen willst, damit du eine Idee hast, was hier passiert, suche im Play Store einmal nach cnlab SpeedTest. Die App ist sicher nicht perfekt, aber alle hier gezeigten Schnipsel sind aus Tutorials, Blogs und Stackoverflow-Beiträgen erstellt, also auch kein Geheimnis - ich war, bevor ich die komplette UI dieser App (neu-)geschrieben hab, übrigens völlig unwissend, wie man Android programmiert, es ist also mit etwas Fleiß machbar!

PPS: Wenn ich von der App rede, meine ich Version 2.3.2, die leider nur auf Geräten mit Android 4+ zur Verfügung steht. Ältere Geräte erhalten die alte Version 1.4.2, die viele Features, wie z.B. die OSM-Integration, nicht besitzt!
 

Ähnliche Java Themen

Neue Themen


Oben