Создание приложения для Android для отображения списка местных ресторанов

Всем привет! Прежде всего, я хотел бы поблагодарить вас за то, что вы нашли время прочитать этот пост. Меня зовут Ник, я специализируюсь на компьютерных науках в Университете Центральной Флориды. Недавно я работал над разработкой приложений для Android для некоторых проектов и поэтому пытался улучшить навыки работы с Android Studio IDE вместе с языком JAVA. Чтобы сориентироваться, я сделал приложение, которое находит рестораны, расположенные на определенной долготе и широте. В этом посте я объясню, как работают ключевые компоненты приложения, и предоставлю ссылку на GitHub, содержащий код.

Это упрощенное приложение всего с двумя видами деятельности: MainActivity и DisplayRestaurants. Начнем с обсуждения MainActivity. Здесь пользователь встречает наш целевой экран, отображающий заголовок приложения и красивую зеленую кнопку. Фоновое изображение отображается аккуратно. И заголовок, и кнопка ограничены 16dp как с левой, так и с правой стороны. Заголовок устанавливается на 85 dp от верхней части экрана, а кнопка - на 172 dp от нижней части экрана. Для шрифтов MainActivity я выбрал семейство случайных шрифтов. Вы можете легко импортировать свои собственные шрифты или изменить заданные по умолчанию.

Вот XML-код MainActivity:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id = "@+id/layout"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/food_background"
    tools:context="com.example.basicswork.MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="153dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="172dp"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:backgroundTint="@android:color/holo_green_light"
        android:fontFamily="casual"
        android:radius="80dp"
        android:text="@string/restaurants"
        android:textAppearance="@style/TextAppearance.AppCompat.Menu"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="85dp"
        android:fontFamily="casual"
        android:text="@string/restaurant_locator"
        android:textColor="@android:color/white"
        android:textSize="30sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

XML-кодом можно легко манипулировать и изменять по своему усмотрению. Фоновое изображение добавляется в нашу папку app / res / drawable, которую можно использовать в нашем XML-файле, как вы можете видеть выше в тексте android: background.

Теперь о коде JAVA, здесь происходит настоящее волшебство. Основная проблема, которую пытается решить наша MainActivity, - это получение разрешения от пользователя на доступ к их местоположению, а затем получение их долготы и широты. Нам нужны их долгота / широта, чтобы передать их следующему действию и использовать это в поиске мест Google, который будет объяснен позже. Чтобы получить местоположение пользователя в приложениях Android, нам сначала нужно добавить строку кода в наш манифест.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.basicswork">
...
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
...

</manifest>

ACCESS_FINE_LOCATION позволяет нам получить долготу и широту пользователя, которые очень близки к их точному местоположению. Хотя теперь мы должны спросить у пользователя разрешение на использование его местоположения. Я добился этого с помощью уведомлений Snackbar в приложении. Это своего рода дизайнерский виджет, который можно добавить и использовать для повышения эстетики уведомлений. У Snackbar есть несколько параметров, которые нужно включить. Во-первых, макет, текст, который вы хотите отобразить, и продолжительность уведомления. Чтобы использовать это в этом приложении, я добавил следующую строку кода к зависимостям файла build.gradle. Версии могут отличаться, поэтому обязательно проверьте это еще раз.

compile 'com.android.support:design:26.1.0'

Если вы хотите увидеть код MainActivity целиком, пропустите немного вперед.

Начиная сверху, наш общедоступный класс MainActivity расширяет AppCompatActivity и реализует OnRequestPermissionsResultCallback. AppCompatActivity - это просто поддержка Snackbar, а OnRequestPermissionsResultCallback - для получения обратных вызовов для запроса разрешений.

PERMISSION_REQUEST_LOCATION - это просто целочисленная проверка, чтобы узнать, предоставлено ли нам разрешение на использование их местоположения. View mLayout установлен в нашем методе onCreate, который связан с идентификатором нашего макета действий в нашем XML. Внутри onCreate мы устанавливаем простой прослушиватель onClick для запуска метода после нажатия кнопки ресторана.

public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {

    private static final int PERMISSION_REQUEST_LOCATION = 0;
    private View mLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLayout = findViewById(R.id.layout);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showRestaurants();
            }
        });
    }
...
}

После нажатия мы сначала попадаем в метод showRestaurants. В этом методе мы сначала проверяем, получили ли мы уже разрешение на использование их местоположения, а затем вызываем наш метод startRestaurants. Если у нас нет разрешения, мы переходим к нашему методу requestLocationPermission.

public void showRestaurants(){
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {
        // Permission is already available, show restaurants
        Snackbar.make(mLayout,
                "Location permission available. Show restaurants.",
                Snackbar.LENGTH_SHORT).show();
        startRestaurants();
    } else {
        // Permission is missing and must be requested.
        requestLocationPermission();
    }
}

Мы начинаем здесь с shouldShowRequestPermissionRationale, которое может быть дополнительным стимулом для пользователя, позволяющего нам использовать свое местоположение. В случае отклонения при следующем запросе разрешения может появиться сообщение с описанием его использования. Внутри этой Snackbar мы создаем еще одну с прослушивателем onClick, установленным на неопределенное время, до тех пор, пока пользователь не нажмет «ОК», чтобы снова запросить разрешение на использование местоположения. В операторе else мы обрабатываем случай, когда местоположение может быть недоступно, и передаем его в onRequestPermissionResult.

private void requestLocationPermission() {
    // Permission has not been granted and must be requested.
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.ACCESS_FINE_LOCATION)) {
        // Provide an additional rationale to the user if the permission was not granted
        // and the user would benefit from additional context for the use of the permission.
        // Display a SnackBar with a button to request the missing permission.
        Snackbar.make(mLayout, "Location access is required to display restaurants near you.",
                Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Request the permission
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        PERMISSION_REQUEST_LOCATION);
            }
        }).show();

    } else {
        Snackbar.make(mLayout,
                "Permission is not available. Requesting location permission.",
                Snackbar.LENGTH_SHORT).show();
        // Request the permission. The result will be received in onRequestPermissionResult().
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                PERMISSION_REQUEST_LOCATION);
    }
}

Внутри onRequestPermissionResult мы проверяем статус нашего разрешения. Если это разрешено, у нас есть уведомление Snackbar о том, что разрешение на определение местоположения было предоставлено, и мы переходим к нашему методу startRestaurants. В противном случае мы говорим, что в разрешении на местоположение было отказано. Если вы еще не поняли, это приложение полно уведомлений Snackbar. Вещи становятся загроможденными, но при разборке становятся упрощенными.

public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                       int[] grantResults) {
    if (requestCode == PERMISSION_REQUEST_LOCATION) {
        // Request for location permission.
        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission has been granted. Start preview Activity.
            Snackbar.make(mLayout, "Location permission granted. Showing restaurants.",
                    Snackbar.LENGTH_SHORT)
                    .show();
            startRestaurants();
        } else {
            // Permission request was denied.
            Snackbar.make(mLayout, "Location permission request was denied.",
                    Snackbar.LENGTH_SHORT)
                    .show();
        }
    }
}

В действии startRestaurants мы дважды проверяем оператор if, чтобы увидеть наши разрешения. В случае неудачи наш метод ничего не сделает. Если это пройдет, мы начнем с создания менеджера местоположения и соберем долготу и широту пользователей. Эти значения представлены в виде двойников, но они нам понадобятся в качестве строк для нашего следующего действия. Мы соответствующим образом конвертируем их, передаем их нашему намерению и переходим к следующему действию.

public void startRestaurants() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED)
    {
        Intent intent = new Intent(this, DisplayRestaurants.class);
        LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        Double longitude = location.getLongitude();
        Double latitude = location.getLatitude();
        String longit = Double.toString(longitude);
        String lat = Double.toString(latitude);
        intent.putExtra("long", longit);
        intent.putExtra("lat", lat);
        startActivity(intent);
    }
}

В качестве последнего примечания к первому действию, если вы имитируете телефон, вам может потребоваться отправить координаты в приложение. Щелкните многоточие на панели рядом с эмуляцией, перейдите к настройкам местоположения, а затем щелкните отправить, пока приложение все еще находится на главной странице, прежде чем нажимать рестораны.

Вот код основной деятельности полностью.

package com.example.basicswork;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;


public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {

    private static final int PERMISSION_REQUEST_LOCATION = 0;
    private View mLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLayout = findViewById(R.id.layout);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showRestaurants();
            }
        });
    }

    @SuppressLint("MissingPermission")
    public void startRestaurants() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED)
        {
            Intent intent = new Intent(this, DisplayRestaurants.class);
            LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
            Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            Double longitude = location.getLongitude();
            Double latitude = location.getLatitude();
            String longit = Double.toString(longitude);
            String lat = Double.toString(latitude);
            intent.putExtra("long", longit);
            intent.putExtra("lat", lat);
            startActivity(intent);
        }
    }

    public void showRestaurants(){
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            // Permission is already available, show restaurants
            Snackbar.make(mLayout,
                    "Location permission available. Show restaurants.",
                    Snackbar.LENGTH_SHORT).show();
            startRestaurants();
        } else {
            // Permission is missing and must be requested.
            requestLocationPermission();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                           int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_LOCATION) {
            // Request for location permission.
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start preview Activity.
                Snackbar.make(mLayout, "Location permission granted. Showing restaurants.",
                        Snackbar.LENGTH_SHORT)
                        .show();
                startRestaurants();
            } else {
                // Permission request was denied.
                Snackbar.make(mLayout, "Location permission request was denied.",
                        Snackbar.LENGTH_SHORT)
                        .show();
            }
        }
    }

    private void requestLocationPermission() {
        // Permission has not been granted and must be requested.
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {
            // Provide an additional rationale to the user if the permission was not granted
            // and the user would benefit from additional context for the use of the permission.
            // Display a SnackBar with a button to request the missing permission.
            Snackbar.make(mLayout, "Location access is required to display restaurants near you.",
                    Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // Request the permission
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                            PERMISSION_REQUEST_LOCATION);
                }
            }).show();

        } else {
            Snackbar.make(mLayout,
                    "Permission is not available. Requesting location permission.",
                    Snackbar.LENGTH_SHORT).show();
            // Request the permission. The result will be received in onRequestPermissionResult().
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSION_REQUEST_LOCATION);
        }
    }
}

Теперь переходим к деятельности DisplayRestaurants. XML для этого действия содержит только простой ListView, а стиль установлен на «Widget.DeviceDefault.Light.ListView». Мы также устанавливаем идентификатор нашего списка в listView для использования в коде.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.basicswork.DisplayRestaurants">

    <ListView
        android:id="@+id/listView"
        style="@android:style/Widget.DeviceDefault.Light.ListView"
        android:layout_width="344dp"
        android:layout_height="495dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />
</android.support.constraint.ConstraintLayout>

Здесь мы начнем использовать Google Place Search. Для использования этой услуги вам понадобится ключ API. Вы можете получить его на этой странице здесь https://developers.google.com/places/web-service/get-api-key

Мы начинаем наш код с определения группы частных переменных. Эти переменные определяют части HTML-ссылки, которую мы создаем, чтобы получить информацию о ресторанах.

public class DisplayRestaurants extends AppCompatActivity {

    private ListView mListView;
    private static final String API_KEY = "YOUR_API_KEY";

    private static final String PLACES_API_BASE = "https://maps.googleapis.com/maps/api/place";

    private static final String TYPE_AUTOCOMPLETE = "/autocomplete";
    private static final String TYPE_DETAILS = "/details";
    private static final String TYPE_SEARCH = "/nearbysearch";
    private static final String OUT_JSON = "/json?";
    private static final String LOG_TAG = "ListRest";

Информацию о создании ссылок можно найти здесь https://developers.google.com/places/web-service/search

Сначала мы переходим к нашему методу onCreate. Мы получаем намерение из предыдущего действия и получаем долготу и широту. Затем мы включаем пару ключевых строк кода, использование которых вызывает сомнения. В общем, вы хотите выполнять действия API в фоновом режиме. Поскольку это приложение настолько упрощено, а вся цель действий зависит от возврата API и не имеет другой функции, можно разрешить его в основном потоке.

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

Затем мы анализируем наши строки долготы и широты в Double’s и создаем радиус 1000. В создаваемой нами HTML-ссылке радиус - это расстояние от устройства до поиска ресторанов. Это расстояние в метрах. Не стесняйтесь изменять это расстояние по своему вкусу. Теперь мы создаем ArrayList of Place объектов. Мы устанавливаем этот список равным нашему методу поиска, который принимает в качестве параметров широту, долготу и радиус. Этот метод возвращает ArrayList, заполненный ресторанами рядом с пользователем. Если наш список не равен нулю, мы затем создаем listView, создаем ArrayAdapter, передавая наш ArrayList, и устанавливаем наш адаптер в наш listView.

...
    Double lng = Double.parseDouble(longitude);
    Double lat = Double.parseDouble(latitude);
    int radius = 1000;

    ArrayList<Place> list = search(lat, lng, radius);

    if (list != null)
    {
        mListView = (ListView) findViewById(R.id.listView);
        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1, list);
        mListView.setAdapter(adapter);
    }
}

В методе поиска мы начинаем с создания нулевого объекта ArrayList of Place. Здесь мы создаем нашу HTML-ссылку с помощью StringBuilder и добавляем части в требуемом порядке. После того, как мы закончили наш StringBuilder, мы используем sb.toString (), чтобы завершить нашу строку и открыть соединение URL. Мы берем входной поток и читаем его с помощью буфера. Мы добавляем наши результаты в json по мере прохождения. Есть несколько исключений, на которые мы должны обратить внимание, например, некорректная обработка запроса или невозможность подключиться к Places API. Мы завершаем эту часть кода отключением от открытого соединения.

public static ArrayList<Place> search(double lat, double lng, int radius) {
    ArrayList<Place> resultList = null;

    HttpURLConnection conn = null;
    StringBuilder jsonResults = new StringBuilder();
    try {
        StringBuilder sb = new StringBuilder(PLACES_API_BASE);
        sb.append(TYPE_SEARCH);
        sb.append(OUT_JSON);
        sb.append("location=" + String.valueOf(lat) + "," + String.valueOf(lng));
        sb.append("&radius=" + String.valueOf(radius));
        sb.append("&type=restaurant");
        sb.append("&key=" + API_KEY);

        URL url = new URL(sb.toString());
        conn = (HttpURLConnection) url.openConnection();
        InputStreamReader in = new InputStreamReader(conn.getInputStream());

        int read;
        char[] buff = new char[1024];
        while ((read = in.read(buff)) != -1) {
            jsonResults.append(buff, 0, read);
        }
    } catch (MalformedURLException e) {
        Log.e(LOG_TAG, "Error processing Places API URL", e);
        return resultList;
    } catch (IOException e) {
        Log.e(LOG_TAG, "Error connecting to Places API", e);
        return resultList;
    } finally {
        if (conn != null) {
            conn.disconnect();
        }
    }
...

Теперь нам нужно пройти и обработать наши jsonResults. Мы создаем иерархию того, что получили. Мы создаем новый объект json и устанавливаем его равным нашим jsonResults. Затем мы конвертируем это в массив json. Теперь мы можем фактически извлечь рестораны и описания из наших результатов. Мы устанавливаем длину нашего списка массива resultList равной длине только что созданного jsonArray. С помощью цикла for, который проходит через этот массив, мы создаем новый объект Place для каждого элемента и добавляем его в наш список результатов. Если эта попытка не удалась, у нас есть ловушка, которая говорит, что у нас была ошибка где-то при обработке результатов json. Теперь мы наконец можем вернуть наш новый список.

    try {
        // Create a JSON object hierarchy from the results
        JSONObject jsonObj = new JSONObject(jsonResults.toString());
        JSONArray predsJsonArray = jsonObj.getJSONArray("results");

        // Extract the descriptions from the results
        resultList = new ArrayList<Place>(predsJsonArray.length());
        for (int i = 0; i < predsJsonArray.length(); i++) {
            Place place = new Place();
            place.reference = predsJsonArray.getJSONObject(i).getString("reference");
            place.name = predsJsonArray.getJSONObject(i).getString("name");
            resultList.add(place);
        }
    } catch (JSONException e) {
        Log.e(LOG_TAG, "Error processing JSON results", e);
    }

    return resultList;
}

Наконец, в конце мы создаем класс для наших объектов Place. Это просто определяет объект, который используется в наших списках массивов.

//Value Object for the ArrayList
    public static class Place {
        private String reference;
        private String name;

        public Place(){
            super();
        }
        @Override
        public String toString(){
            return this.name; //This is what returns the name of each restaurant for array list
        }
    }
}

Вот код целиком

package com.example.basicswork;

import android.content.Intent;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

public class DisplayRestaurants extends AppCompatActivity {

    private ListView mListView;
    private static final String API_KEY = "AIzaSyBO7_U7r1oST2upR26wkjwLQfYSMbAogQ4";

    private static final String PLACES_API_BASE = "https://maps.googleapis.com/maps/api/place";

    private static final String TYPE_AUTOCOMPLETE = "/autocomplete";
    private static final String TYPE_DETAILS = "/details";
    private static final String TYPE_SEARCH = "/nearbysearch";
    private static final String OUT_JSON = "/json?";
    private static final String LOG_TAG = "ListRest";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_display_restaurants);
        Intent intent = getIntent();
        String longitude = intent.getStringExtra("long");
        String latitude = intent.getStringExtra("lat");

        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);

        Double lng = Double.parseDouble(longitude);
        Double lat = Double.parseDouble(latitude);
        int radius = 1000;

        ArrayList<Place> list = search(lat, lng, radius);

        if (list != null)
        {
            mListView = (ListView) findViewById(R.id.listView);
            ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1, list);
            mListView.setAdapter(adapter);
        }
    }

    public static ArrayList<Place> search(double lat, double lng, int radius) {
        ArrayList<Place> resultList = null;

        HttpURLConnection conn = null;
        StringBuilder jsonResults = new StringBuilder();
        try {
            StringBuilder sb = new StringBuilder(PLACES_API_BASE);
            sb.append(TYPE_SEARCH);
            sb.append(OUT_JSON);
            sb.append("location=" + String.valueOf(lat) + "," + String.valueOf(lng));
            sb.append("&radius=" + String.valueOf(radius));
            sb.append("&type=restaurant");
            sb.append("&key=" + API_KEY);

            URL url = new URL(sb.toString());
            conn = (HttpURLConnection) url.openConnection();
            InputStreamReader in = new InputStreamReader(conn.getInputStream());

            int read;
            char[] buff = new char[1024];
            while ((read = in.read(buff)) != -1) {
                jsonResults.append(buff, 0, read);
            }
        } catch (MalformedURLException e) {
            Log.e(LOG_TAG, "Error processing Places API URL", e);
            return resultList;
        } catch (IOException e) {
            Log.e(LOG_TAG, "Error connecting to Places API", e);
            return resultList;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }

        try {
            // Create a JSON object hierarchy from the results
            JSONObject jsonObj = new JSONObject(jsonResults.toString());
            JSONArray predsJsonArray = jsonObj.getJSONArray("results");

            // Extract the descriptions from the results
            resultList = new ArrayList<Place>(predsJsonArray.length());
            for (int i = 0; i < predsJsonArray.length(); i++) {
                Place place = new Place();
                place.reference = predsJsonArray.getJSONObject(i).getString("reference");
                place.name = predsJsonArray.getJSONObject(i).getString("name");
                resultList.add(place);
            }
        } catch (JSONException e) {
            Log.e(LOG_TAG, "Error processing JSON results", e);
        }

        return resultList;
    }


    //Value Object for the ArrayList
    public static class Place {
        private String reference;
        private String name;

        public Place(){
            super();
        }
        @Override
        public String toString(){
            return this.name; //This is what returns the name of each restaurant for array list
        }
    }
}

Вот и все! Теперь мы успешно получили разрешения пользователя, выяснили местоположение пользователя и отобразили рестораны, расположенные для этого пользователя, в виде списка с помощью веб-API Google Place Search. Если вы пытаетесь сделать что-то подобное, надеюсь, это вам поможет. Вот ссылка GitHub на полный проект. Удачного кодирования.