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()"的形式返回,最终将其放大到屏幕中心。

