Android Geocoder反向地理编码
Android Geocoder类用于反向地理编码,即从Google Map上的位置检索地址。
如果您不知道如何在Android应用程序中使用Google Maps,请先阅读android google maps教程,然后再继续。
Android Geocoder
Android" Geocoder"类用于地理编码以及反向地理编码。
地理编码是指将街道地址或者任何地址转换为纬度和经度。
反向地理编码是指将纬度和经度转换为其相应的街道地址。
"地址"类有助于获取该位置的街道地址,位置,子位置,城市,国家/地区地标等特征。
使用以上两个类,我们将在应用程序中的Google地图上获取当前标记地址。
让我们从android反向地理编码示例项目开始。
Android Geocoder反向地理编码项目结构
我们需要Google Maps和Google Places API。
因此,如下所示,将它们添加到" build.gradle"文件中。
apply plugin: 'com.android.application' allprojects { repositories { jcenter() maven { url "https://maven.google.com" } } } android { compileSdkVersion 26 buildToolsVersion "26.0.1" defaultConfig { applicationId "com.theitroad.reversegeocoding" minSdkVersion 16 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.0.1' compile 'com.android.support:cardview-v7:26.0.1' compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'com.google.android.gms:play-services-maps:11.0.4' compile 'com.google.android.gms:play-services-places:11.0.4' testCompile 'junit:junit:4.12' }
从Google Developer Console获取相关的API密钥,并将其添加到ʻAndroidManifest.xml`文件的元数据中。
如果您不知道如何在应用程序中集成Google API,请先阅读本教程,然后再继续。
Android反向地理编码代码
下面给出了" activity_main.xml"布局的代码。
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:card_view="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.CardView android:id="@+id/cardView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="12dp" card_view:cardCornerRadius="0dp" card_view:cardElevation="0dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center"> <TextView android:id="@+id/txtLocationAddress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:ellipsize="marquee" android:focusable="true" android:focusableInTouchMode="true" android:gravity="center" android:marqueeRepeatLimit="marquee_forever" android:maxLines="1" android:padding="16dp" android:scrollHorizontally="true" android:text="Current Marker Address" </RelativeLayout> </android.support.v7.widget.CardView> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/mapFragment" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:tag="tag_map_fragment" <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"> <ImageView android:id="@+id/centerMarker" android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" android:src="@drawable/black_marker" </FrameLayout> </FrameLayout> </RelativeLayout>
我们的布局包含一个CardView,其中包含一个TextView,最终将显示当前地址。
标记放置在屏幕中央的ImageView中。
下面给出了MainActivity.java类的代码。
package com.theitroad.reversegeocoding; import android.content.Intent; import android.location.Address; import android.location.Geocoder; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.CardView; import android.text.TextUtils; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import com.google.android.gms.common.GooglePlayServicesRepairableException; import com.google.android.gms.location.places.Place; import com.google.android.gms.location.places.ui.PlaceAutocomplete; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import java.io.IOException; import java.util.List; import java.util.Locale; public class MainActivity extends AppCompatActivity { TextView txtLocationAddress; SupportMapFragment mapFragment; GoogleMap map; LatLng center; CardView cardView; private static final int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtLocationAddress = findViewById(R.id.txtLocationAddress); txtLocationAddress.setEllipsize(TextUtils.TruncateAt.MARQUEE); txtLocationAddress.setSingleLine(true); txtLocationAddress.setMarqueeRepeatLimit(-1); txtLocationAddress.setSelected(true); cardView = findViewById(R.id.cardView); cardView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { Intent intent = new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN) .build(MainActivity.this); startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE); } catch (GooglePlayServicesRepairableException e) { printToast("Google Play Service Repair"); } catch (GooglePlayServicesNotAvailableException e) { printToast("Google Play Service Not Available"); } } }); mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapFragment); mapFragment.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { map = googleMap; map.getUiSettings().setZoomControlsEnabled(true); LatLng latLng = new LatLng(20.5937, 78.9629); map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16)); initCameraIdle(); } }); } private void initCameraIdle() { map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { @Override public void onCameraIdle() { center = map.getCameraPosition().target; getAddressFromLocation(center.latitude, center.longitude); } }); } private void getAddressFromLocation(double latitude, double longitude) { Geocoder geocoder = new Geocoder(this, Locale.ENGLISH); try { List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1); if (addresses.size() > 0) { Address fetchedAddress = addresses.get(0); StringBuilder strAddress = new StringBuilder(); for (int i = 0; i < fetchedAddress.getMaxAddressLineIndex(); i++) { strAddress.append(fetchedAddress.getAddressLine(i)).append(" "); } txtLocationAddress.setText(strAddress.toString()); } else { txtLocationAddress.setText("Searching Current Address"); } } catch (IOException e) { e.printStackTrace(); printToast("Could not get address..!"); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) { if (resultCode == RESULT_OK) { Place place = PlaceAutocomplete.getPlace(this, data); if (!place.getAddress().toString().contains(place.getName())) { txtLocationAddress.setText(place.getName() + ", " + place.getAddress()); } else { txtLocationAddress.setText(place.getAddress()); } CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(place.getLatLng(), 16); map.animateCamera(cameraUpdate); } else if (resultCode == PlaceAutocomplete.RESULT_ERROR) { printToast("Error in retrieving place info"); } } } private void printToast(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); } }
让我们分解上面的代码,看看它能做什么。
我们已通过编程方式在TextView上设置了属性setEllipize()和setMarqueeRepeatLimit(),因为在xml中设置相同的属性不会再在所有设备上滚动文本。
如果内容长度大于TextView的宽度,则上述方法可使TextView的内容水平滚动。在
setMarqueeRepeatLimit()
中传递-1使其永远滚动。使用SupportMapFragment显示Google Map。
将getMapAsync()回调分配给该片段。
存在Google Play服务时会触发此回调。onMapReady()方法是显示Google Map并返回GoogleMap类的非空实例的方法。
我们将其分配给实例变量" map"。
map.getUiSettings()。
setZoomControlsEnabled(true)用于设置屏幕上的缩放控件。
map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng,16))将地图移到latLng中指定的位置(旧金山的某些位置!)。
相机缩放到屏幕中心放置标记的位置。setOnCameraIdleListener将侦听地图上的移动/拖动。
当运动结束时,方法onCameraIdle()被触发。
其中我们检索地图中心的纬度和经度(因为该标记位于此位置),并将其传递给方法" getAddressFromLocation()",该方法最终将进行反向地理编码。方法getFromLocation(double latitude,double longitude,int maxResults)`返回当前位置的地址列表。
在"地址"对象中,方法" getAddressLine(int index)"返回由给定索引编号的地址行;如果不存在地址,则返回null。
我们将该地址附加到StringBuilder,该字符串最终显示在TextView中。
getMaxAddressLineIndex()返回当前用于指定地址行的最大索引。
地址的其他一些重要细节也可以检索。
下面列出了其中一些:getThoroughfare()
:这是一条街道,其中包含交付点。
如果不存在,则返回null。getSubThoroughfare()
:当一个城镇中存在一个以上的通途名称时,subThoroughfare名称将用于其他信息。getLocality()和getSublocality():返回地址的位置,例如," Mountain View"和子位置(如果可用)或者返回null。
getFeatureName():返回最近的地标名称(如果有)或者为null。
getCountryName()getCountryCode():分别返回国家名称和国家代码。
点击" CardView"会从Google Places API启动PlaceAutoComplete服务。
我们使用PlaceAutocomplete.IntentBuilder来创建Intent并将Mode作为全屏传递。
所选位置将返回到onActivityResult()中,该结果最终将显示在TextView中。
" Place"的纬度和经度以" place.getLatLng()"的形式返回,最终将其放大到屏幕中心。