주변 지역 검색 | Places API | Google for Developers
이제 Places API (신규)가 출시되면서 차세대 Places API를 사용할 수 있습니다. 이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 주변 지역 검색 컬렉션을 사용해 정리하기 내 환경
developers.google.com
저장 후, 키 복사하기
환경설정하기
https://codebunny99.tistory.com/179
안드로이드 스튜디오에서 구글 맵 사용하기
구글맵 사용하기https://developers.google.com/maps/documentation/places/web-service/search?hl=ko#nearby-search-and-text-search-responses 장소 검색 | Places API | Google for Developers이제 Places API (신규)가 출시되면서 차세
codebunny99.tistory.com
Manifest에서 작성해주기
<uses-permission android:name="android.permission.INTERNET"/>
라이브러리 설치
build.gradle.kts(:app) 에서, 맨 아랫줄 작성.
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("commons-io:commons-io:2.4")
config, NetworkClient 작성하기
config
package com.~.placeapp.config;
public class Config {
public static final String DOMAIN = "https://maps.googleapis.com";
public static final String SP_NAME = "places_app";
public static final String PLACE_API_KEY = "자신의 API 키 입력";
}
NetworkClient
package com.~.placeapp.api;
import android.content.Context;
import com.yujinoh.placeapp.config.Config;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class NetworkClient {
public static Retrofit retrofit;
public static Retrofit getRetrofitClient(Context context){
if(retrofit == null){
// 통신 로그 확인할때 필요한 코드
HttpLoggingInterceptor loggingInterceptor =
new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 네트워크 연결관련 코드
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.addInterceptor(loggingInterceptor)
.build();
// 네트워크로 데이터를 보내고 받는
// 레트로핏 라이브러리 관련 코드
retrofit = new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
PlaceApi
package com.~.placeapp.api;
import com.~.placeapp.model.PlaceList;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface PlaceApi {
//키워드 기반으로 장소 가져오는 API
@GET("/maps/api/place/nearbysearch/json")
Call<PlaceList> getPlaceList(@Query("language") String language,
@Query("location") String location,
@Query("radius") int radius,
@Query("key") String key,
@Query("keyword") String keyword);
}
activity_main 화면작성
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/layoutTop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="10dp"
android:layout_marginTop="20dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="20dp"
android:orientation="horizontal">
<EditText
android:id="@+id/editKeyword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:hint="검색어 입력..."
android:inputType="text"
android:textSize="20sp" />
<ImageView
android:id="@+id/imgSearch"
android:layout_width="40dp"
android:layout_height="match_parent"
app:srcCompat="@drawable/search_24dp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/layoutTop" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
layout에 place_row.xml 추가하여 리사이클러뷰에 들어갈 카드뷰 작성
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/txtName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="22sp" />
<TextView
android:id="@+id/txtVicinity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:text="TextView"
android:textSize="22sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
model 패키지 작성
Place 클래스
package com.~.placeapp.model;
import com.google.gson.JsonArray;
public class Place implements Serializable {
public String name;
public String vicinity;
public Geometry geometry;
// 이너 클래스 inner class
public class Geometry implements Serializable {
public Location location;
// 이너 클래스 inner class
public class Location implements Serializable {
public double lat;
public double lng;
}
}
}
PlaceList
package com.~.placeapp.model;
import java.util.ArrayList;
public class PlaceList {
public ArrayList<Place> results;
}
PlaceAdapter
package com.~.placeapp.adapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.~.placeapp.MapActivity;
import com.~.placeapp.R;
import com.~.placeapp.model.Place;
import java.util.ArrayList;
public class PlaceAdapter extends RecyclerView.Adapter<PlaceAdapter.ViewHolder> {
Context context;
ArrayList<Place> placeArrayList;
public PlaceAdapter(Context context, ArrayList<Place> placeList) {
this.context = context;
this.placeArrayList = placeList; // 여기서 placeArrayList에 올바르게 할당합니다.
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.place_row, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Place place = placeArrayList.get(position);
if (place.name == null) {
holder.txtName.setText("상점명 없음");
} else {
holder.txtName.setText(place.name);
}
if (place.vicinity == null) {
holder.txtVicinity.setText("주소 없음");
} else {
holder.txtVicinity.setText(place.vicinity);
}
}
@Override
public int getItemCount() {
return placeArrayList != null ? placeArrayList.size() : 0;
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView txtName;
TextView txtVicinity;
CardView cardView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
txtName = itemView.findViewById(R.id.txtName);
txtVicinity = itemView.findViewById(R.id.txtVicinity);
cardView = itemView.findViewById(R.id.cardView);
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, MapActivity.class);
int index = getAdapterPosition();
if (index != RecyclerView.NO_POSITION) {
Place place = placeArrayList.get(index);
intent.putExtra("place", place);
context.startActivity(intent);
}
}
});
}
}
}
MainActivity
package com.~.placeapp;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.~.placeapp.adapter.PlaceAdapter;
import com.~.placeapp.api.NetworkClient;
import com.~.placeapp.api.PlaceApi;
import com.~.placeapp.config.Config;
import com.~.placeapp.model.Place;
import com.~.placeapp.model.PlaceList;
import java.util.ArrayList;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class MainActivity extends AppCompatActivity {
LocationListener locationListener;
LocationManager locationManager;
EditText editKeyword;
ImageView imgSearch;
ProgressBar progressBar;
RecyclerView recyclerView;
ArrayList<Place> placeArrayList = new ArrayList<>();
PlaceAdapter adapter;
//현재 나의 위치를 나타내는 위도, 경도 멤버변수
double lat;
double lng;
String keyword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editKeyword = findViewById(R.id.editKeyword);
imgSearch = findViewById(R.id.imgSearch);
progressBar = findViewById(R.id.progressBar);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
// 현재 폰의 위치를 가져오는 코드 작성.
// 1. 로케이션 매니저를 가져온다
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// 2. 위치가 바뀔때마다 위치정보를 가져오는 코드 작성
locationListener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
lat = location.getLatitude();
lng = location.getLongitude();
Log.i("PLACES MAIN", "위도 : " + lat + ", 경도 : " + lng);
}
};
// 3. 로케이션 매니저에, 우리가 작성한 함수를 적용한다.
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION},
100);
return;
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
3000,
-1,
locationListener); //minTimeMs초마다 한번씩 또는 minDistanceM미터가 변경될 때마다 한번씩 실행 시킨다.
progressBar.setVisibility(View.GONE);
imgSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
keyword = editKeyword.getText().toString().trim();
if(keyword.isEmpty()){
return;
}
getNetworkData();
}
});
}
private void getNetworkData() {
progressBar.setVisibility(View.VISIBLE);
placeArrayList.clear();
//네트워크 호출
Retrofit retrofit = NetworkClient.getRetrofitClient(MainActivity.this);
PlaceApi api = retrofit.create(PlaceApi.class);
Call<PlaceList> call = api.getPlaceList("ko", lat+","+lng, 2000, Config.PLACE_API_KEY, keyword);
call.enqueue(new Callback<PlaceList>() {
@Override
public void onResponse(Call<PlaceList> call, Response<PlaceList> response) {
progressBar.setVisibility(View.GONE);
if(response.isSuccessful()){
PlaceList placeList = response.body();
placeArrayList.addAll(placeList.results);
adapter = new PlaceAdapter(MainActivity.this, placeArrayList);
recyclerView.setAdapter(adapter);
} else {
}
}
@Override
public void onFailure(Call<PlaceList> call, Throwable throwable) {
progressBar.setVisibility(View.GONE);
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 100) { // 요청 코드가 100일 때만 처리
// grantResults 배열의 길이를 확인하여 IndexOutOfBoundsException 방지
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
// 위치 권한이 승인되었을 때
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (locationManager != null) { // locationManager가 null이 아닌지 확인
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
3000,
-1,
locationListener); // minTimeMs초마다 한 번씩 또는 minDistanceM미터가 변경될 때마다 한 번씩 실행
}
}
}
}
}
}
카드뷰 누르면 지도 뜨는 MapActivity 만들기
activity_map.xml : 지도로 한다.
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
MapActivity.java
package com.~.placeapp;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
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 com.google.android.gms.maps.model.MarkerOptions;
import com.yujinoh.placeapp.model.Place;
public class MapActivity extends AppCompatActivity {
Place place;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
place = (Place) getIntent().getSerializableExtra("place");
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull GoogleMap googleMap) {
// 위에서 받아온 place 객체에 저장되어 있는 위도, 경도 꺼내서
LatLng latLng = new LatLng(place.geometry.location.lat, place.geometry.location.lng);
// 1. 지도의 위치를 이 위도, 경도를 중심으로 해서 이동시킨다.
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 17));
// 2. 마커로 표시한다.
MarkerOptions markerOptions = new MarkerOptions();
if(place.name == null){
markerOptions.position(latLng).title("상점명 없음");
}else{
markerOptions.position(latLng).title(place.name);
}
googleMap.addMarker(markerOptions);
}
});
}
}
클릭하면,
액션바의 지도 아이콘 클릭하면, 검색한 곳을 모두 지도로 마커 표시하게 하기
메뉴를 만든다.
Activity 만들기 : PlaceActivity로 하였다.
activity_place.xml 화면 코드 : 지도로 한다.
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
PlaceActivity 작성하기
package com.~.placeapp;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
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 com.google.android.gms.maps.model.MarkerOptions;
import com.yujinoh.placeapp.model.Place;
import java.util.ArrayList;
public class PlaceActivity extends AppCompatActivity {
Place place;
ArrayList<Place> placeArrayList;
double lat;
double lng;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_place);
// 데이터 받아오기
placeArrayList = (ArrayList<Place>) getIntent().getSerializableExtra("placeArrayList");
lat = getIntent().getDoubleExtra("lat", 0);
lng = getIntent().getDoubleExtra("lng", 0);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull GoogleMap googleMap) {
// 1. 위에서 받아온 place 객체에 저장되어 있는 위도, 경도 꺼내서
LatLng myLocation = new LatLng(lat, lng);
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myLocation, 17));
// 2. 위에서 받은 어레이리스트에 들어있는 플레이스를 반복문해서 마커로 만든다.
for( Place place : placeArrayList ){
LatLng latLng = new LatLng(place.geometry.location.lat, place.geometry.location.lng);
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(latLng).title(place.name);
googleMap.addMarker(markerOptions);
}
}
});
}
}
GPS가 준비되어있지 않았는데, 지도를 누르면 에러가 발생할 수 있다.
이러한 오류를 잡기위해 MainActivity에 아래와 같이 작성한다.
(1) 변수 설정
(2) 아직 준비가 안됐을 때, 팝업창을 띄운다.
(3) 준비가 되었을 때에는 잘 동작하도록 true로 한다
'Android' 카테고리의 다른 글
안드로이드 스튜디오에서 구글 맵 사용하기 (0) | 2024.06.18 |
---|---|
안드로이드 스튜디오 GPS 기능으로 지도에 마커 추가하기 (0) | 2024.06.18 |
안드로이드 스튜디오 탭 바(Tab Bar) 구현하기 (0) | 2024.06.18 |
사진을 포스팅하는 어플리케이션(App) 개발하기 (0) | 2024.06.17 |
(안드로이드 스튜디오) 카메라 촬영 사진 업로드 권한 설정, 이미지 업로드하는 기능 만들기 (2) | 2024.06.14 |