geocoder
Android Geocoder class is used for reverse geocoding i.e. retrieving the address from the location on the Google Map.
If you aren’t aware of how to use Google Maps in your Android Application, refer android google maps tutorial before moving ahead.
Android Geocoder类用于反向地理编码,即从Google Map上的位置检索地址。
如果您不知道如何在Android应用程序中使用Google Maps,请先阅读android Google Maps教程,然后再继续。
Android Geocoder
class is used for Geocoding as well as Reverse Geocoding. Geocoding refers to transforming street address or any address into latitude and longitude. Reverse Geocoding refers to transforming latitude and longitude into its corresponding street address.
Android Geocoder
类用于地理编码以及反向地理编码。 地理编码是指将街道地址或任何地址转换为纬度和经度。 反向地理编码是指将纬度和经度转换为其对应的街道地址。
Address
class helps in fetching the street address, locality, sub-locality, city, country, landmark etc. features of the location.
Address
类别有助于获取街道地址,位置,子位置,城市,国家/地区地标等特征。
Using the above two classes we’ll be fetching the current marker address on the Google Maps in our application. Let’s start with the android reverse geocoding example project.
使用以上两个类,我们将在应用程序中的Google Maps上获取当前标记地址。 让我们从android反向地理编码示例项目开始。
We’ll need the google maps and google places api. So let’s add them in the build.gradle
file as shown below.
我们需要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.journaldev.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'
}
Get the relevant API key from the Google Developer Console and add it inside the meta-data in the AndroidManifest.xml
file. If you aren’t aware of integrating Google APIs in your application, refer this tutorial before moving ahead.
从Google Developer Console获取相关的API密钥,并将其添加到AndroidManifest.xml
文件的元数据中。 如果您不知道将Google API集成到应用程序中,请先阅读本教程,然后再继续。
The code for the activity_main.xml
layout is given below.
下面给出了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>
Our layout contains a CardView, which holds a TextView that’ll eventually display the current address.
The marker is placed in an ImageView at the center of the screen.
我们的布局包含一个CardView ,其中包含一个TextView,最终将显示当前地址。
标记放置在屏幕中央的ImageView中。
The code for the MainActivity.java
class is given below.
下面给出MainActivity.java
类的代码。
package com.journaldev.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();
}
}
Let’s break down the above code and see what it does.
让我们分解上面的代码,看看它能做什么。
setEllipize()
and set setMarqueeRepeatLimit()
on the TextView programmatically since setting the same attributes in xml doesn’t scroll the text on all devices anymore. The above methods make the content of TextView scroll horizontally IF the content length is greater than the TextView width. 我们已通过编程方式在TextView上设置了属性setEllipize()
和setMarqueeRepeatLimit()
,因为在xml中设置相同的属性不会再在所有设备上滚动文本。 如果内容长度大于TextView的宽度,则上述方法可使TextView的内容水平滚动。 setMarqueeRepeatLimit()
makes it scroll forever. 在setMarqueeRepeatLimit()
传递-1将使其永远滚动。 getMapAsync()
callback is assigned to the fragment. This callback gets triggered when the Google Play Services exists. 使用SupportMapFragment显示Google Map。 getMapAsync()
回调分配给该片段。 存在Google Play服务时会触发此回调。 onMapReady()
method is the one that displays the Google Map and returns a non-null instance of the GoogleMap
class. We assign it to our instance variable map
.map.getUiSettings().setZoomControlsEnabled(true)
is used to set the zoom controls on the screen. onMapReady()
方法是显示Google Map并返回GoogleMap
类的非null实例的方法。 我们将其分配给实例变量map
。 map.getUiSettings().setZoomControlsEnabled(true)
用于在屏幕上设置缩放控件。 map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16))
moves the map to the location specified in the latLng
(Some location in India!). The camera zooms to the location in the center of the screen where the marker is placed. map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16))
将地图移动到latLng
指定的位置(印度的某些位置!)。 相机将缩放到屏幕中心放置标记的位置。 setOnCameraIdleListener
would listen for the movements/dragging on the map. When the movement ends the method onCameraIdle()
gets triggered. This is where we retrieve the latitude and longitude of the center of map(because that is where the marker resides) and pass it into the method getAddressFromLocation()
which will eventually do the reverse geocoding. setOnCameraIdleListener
将侦听地图上的移动/拖动。 运动结束后,将触发onCameraIdle()
方法。 在这里,我们检索地图中心的纬度和经度(因为这是标记所在的位置),并将其传递到方法getAddressFromLocation()
,该方法最终将进行反向地理编码。 getFromLocation(double latitude, double longitude, int maxResults)
returns a List of Addresses for the current location. 方法getFromLocation(double latitude, double longitude, int maxResults)
返回当前位置的地址列表。 Address
object, the method getAddressLine(int index)
returns a line of the address numbered by the given index or null if no address exists. 在Address
对象中,方法getAddressLine(int index)
返回由给定索引编号的地址行;如果不存在地址,则返回null。 getMaxAddressLineIndex()
returns the largest index currently in use to specify an address line. getMaxAddressLineIndex()
返回当前用于指定地址行的最大索引。 getThoroughfare()
: This is the street, which contains the delivery point. If it doesn’t exist null would be returned.getSubThoroughfare()
: When a thoroughfare name exists more than once within a town, subThoroughfare name is used for additional information.getLocality()
and getSublocality()
: Returns the locality of the address, for example “Mountain View” and sub locality respectively if available or null.getFeatureName()
: Returns the nearest landmark name if available or null.getCountryName()
getCountryCode()
: Returns the country name and country code respectively.getThoroughfare()
:这是一条街道,其中包含交付点。 如果不存在,则返回null。 getSubThoroughfare()
:当一个城镇的通途名称不止一次存在时,subThoroughfare名称将用于其他信息。 getLocality()
和getSublocality()
:返回地址的局部性,例如,如果可用,则分别返回“ Mountain View”和子局部性,或者返回null。 getFeatureName()
:返回最近的地标名称(如果有)或为null。 getCountryName()
getCountryCode()
:分别返回国家名称和国家代码。 CardView
would launch the PlaceAutoComplete service from the Google Places API. 点击CardView
将从Google Places API启动PlaceAutoComplete服务。 PlaceAutocomplete.IntentBuilder
to create Intent and pass the Mode as full screen. 我们使用PlaceAutocomplete.IntentBuilder
创建Intent并将Mode作为全屏传递。 onActivityResult()
which is eventually displayed in the TextView. The latitude and longitude of the Place
are returned as place.getLatLng()
which is eventually zoomed into the center of the screen. 所选位置将在onActivityResult()
返回,该结果最终将显示在TextView中。 将Place
的纬度和经度作为place.getLatLng()
返回,最终将其缩放到屏幕的中心。 The output of the android reverse geocoding application in action is given below.
实际操作中的android反向地理编码应用程序的输出如下。
This brings an end to android Geocoder example tutorial. You can download the final Android Reverse Geocoding Project from the link below and make sure to add your own API in the meta-data tag inside AndroidManifest.xml
file.
这结束了android Geocoder示例教程。 您可以从下面的链接下载最终的Android反向地理编码项目 ,并确保在AndroidManifest.xml
文件内的meta-data标签中添加自己的API。
References : Geocoder, Address, Place Autocomplete.
翻译自: https://www.journaldev.com/15676/android-geocoder-reverse-geocoding
geocoder