Путь с указателями направления на карте с использованием Yandex Map Kit для Android

Здравствуйте!

В этом сообщении рассмотрим:
  1. Встраивание библиотеки Yandex Map Kit Android в проект
  2. Создание расширения для класса Overlay с целью вывода на карту пути с указателями направления и обработки событий «onClick» по карте


image

1. Встраивание библиотеки в проект (Eclipse, Windows)


Загружаем архив с библиотекой отсюда и получаем API-ключ.
Создаем проект в Eclipse, копируем с заменой папки \yandexmapkit-library\res и \yandexmapkit-library\libs из загруженного архива в папку нашего проекта.
В настройках проекта добавляем ссылку на yandexmapkit-android.jar из папки libs нашего проекта (Build Path->Configure Buil Path->Libraries->Add External JARs)
Добавляем разрешения в манифест нашего проекта

  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  <uses-permission android:name="android.permission.WAKE_LOCK"/>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
  <uses-permission android:name="android.permission.READ_PHONE_STATE"/> 

Изменяем разметку activity

  <?xml version="1.0" encoding="utf-8"?>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
      <ru.yandex.yandexmapkit.MapView
        android:id="@+id/map"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:apiKey="ваш ключ" />
  </RelativeLayout>

2. Создание расширения для класса Overlay с целью вывода на карту пути с указателями направления и обработки событий «onClick» по карте


Чтобы нарисовать путь, нам потребуется расширить класс OverlayIRender, который реализует интерфейс IRender

public class MyOverlayIRender extends OverlayIRender {

	Overlay mOverlay;

	public MyOverlayIRender(Overlay overlay) {
		super();
		mOverlay = overlay;
	}

	@Override
	public void draw(Canvas canvas, OverlayItem arg1) {
		super.draw(canvas, arg1);
		List<OverlayItem> oi = mOverlay.getOverlayItems();
		if (oi.size() < 2)
			return;
		Paint mPaint = new Paint();
		mPaint.setDither(true);
		mPaint.setColor(Color.RED);
		mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeWidth(2);
		mPaint.setAntiAlias(true);
		for (int i = 0; i < oi.size() - 1; i++) {
			Path path = new Path();
			ScreenPoint p1 = mOverlay.getMapController().getScreenPoint(
			oi.get(i).getGeoPoint());
			ScreenPoint p2 = mOverlay.getMapController().getScreenPoint(
			oi.get(i + 1).getGeoPoint());
			float angle = (float) (Math.atan2(p2.getY() - p1.getY(), 
					p2.getX()- p1.getX()) * 180 / 3.14);
			path.moveTo(p2.getX(), p2.getY());
			path.lineTo(p1.getX(), p1.getY());
			canvas.drawPath(path, mPaint);
			canvas.save();
			canvas.rotate(angle + 90, p2.getX(), p2.getY());
			Path path2 = new Path();
			path2.moveTo(p2.getX(), p2.getY());
			path2.lineTo(p2.getX() + 5, p2.getY() + 8.66f);
			path2.lineTo(p2.getX() - 5, p2.getY() + 8.66f);
			path2.close();
			canvas.drawPath(path2, mPaint);
			canvas.restore();
		}
	}
}

Если количество точек добавленных в наш путь больше 1, то в цикле отрисовываются отрезки пути и стрелки направления.
Теперь нужно создать наследника Overlay MyPathOverLay, назначить ему MyOverlayIRender для отрисовки.

public class MyPathOverLay extends Overlay {

	MapView mMmapView;

	public MyPathOverLay(MapController arg0, MapView mapView) {
		super(arg0);
		mMmapView = mapView;
		this.setIRender(new MyOverlayIRender(this));
	}

	@Override
	public int compareTo(Object arg0) {
		return 0;
	}

	@Override
	public boolean onLongPress(float x, float y) {
		OverlayItem m = new OverlayItem(
				this.c.getGeoPoint(new ScreenPoint(x, y)),
				BitmapFactory.decodeResource(
						this.c.getContext().getResources(),
						R.drawable.flag2leftred));
		m.setOffsetY(-23);
		this.addOverlayItem(m);
		this.c.setPositionNoAnimationTo(this.c
				.getGeoPoint(new ScreenPoint(x, y)));
		return true;
	}
}

В переопределенном методе onLongPress будем добавлять промежуточные точки для нашего пути долгим нажатием на карту.
R.drawable.flag2leftred — картинка для точки,
m.setOffsetY(-23); — смещение, которое нужно подобрать, если нужно сместить картинку относительно точки нажатия на карту, по-умолчанию центр картинки совмещается с точкой нажатия.
Осталось добавить наш Overlay на карту и протестировать

public class BlogYandexActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		MapView mMap = (MapView) findViewById(R.id.map);
		MapController mMapController = mMap.getMapController();
		OverlayManager mOverlayManager = mMapController.getOverlayManager();
		mOverlayManager.addOverlay(new MyPathOverLay(mMapController, mMap));
		mMap.showBuiltInScreenButtons(true);
		mOverlayManager.getMyLocation().setEnabled(false);
	}
}

Результат

image
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 16

    +2
    Уберите эти идиотские флажки, они только мешаются.
    Или перекрасьте их.
      +4
      Ну и еще: MapView пихать как единственного ребенка RelativeLayout… Тоже не комильфо.
        0
        Это же пример использования, а не готовое приложение )
          +3
          Примеры тоже надо правильно писать, дабы ученики не повторяли ошибок.
            +1
            Да, я солидарен с BOOMik
            По примерам ведь копи-пастом пойдут ваши ляпы.
              0
              не согласен, цель примера показать работу с апи яндекса, а не выбор графики и компановки разметки
                +1
                Ну окей, отбросим графику и разметку.
                BitmapFactory.decodeResource(
                this.c.getContext().getResources(),
                R.drawable.flag2leftred)
                Тут вот какая-никакая, а утечка памяти. Не лучше-ли будет декодировать картинку один раз, и не мучить хип?

                path2.lineTo(p2.getX() + 5, p2.getY() + 8.66f);
                path2.lineTo(p2.getX() — 5, p2.getY() + 8.66f);
                Вот эти магические числа не поясните? Что за 5 и 8.66?
                  0
                  Тут вот какая-никакая, а утечка памяти. Не лучше-ли будет декодировать картинку один раз, и не мучить хип?

                  согласен, плюс еще можно проверять, что точки попадают в видимую область карты, и не рисовать невидимые

                  Вот эти магические числа не поясните? Что за 5 и 8.66?

                  Это константы, которые расчитал заранее, для определения недостающих вершин равностороннего треугольника, который образует стрелку
        0
        Большое спасибо за статью, очень помогла. А у вас получилось определить, попадают ли точки в видимую область карты? В моем случае, метод draw() почему-то вызывается только тогда, когда видна первая точка. Таким образом получается, что я не могу отрисовывать полилинию по частям, только целиком, что очень нехорошо.
          0
          у контролера есть метод getGeoPoint(ScreenPoint) передавай туда размеры своей карты MapView.getHeight, MapView.getWidth для получения нижней правой координаты и 0,0 для получения верхней
            0
            Да нет, здесь проблема не в получении координат. Проблема в том, что метод MyOverlayIRender.draw() отвечающий за отрисовку, постоянно вызывается только тогда, когда на экране видна первая точка полилинии. Как только она уходит из поля зрения, перерисовка перестает работать, MyOverlayIRender.draw() просто не вызывается. Если отрисовывать сразу всю полилинию целиком, то тогда все работает нормально. Только карта начинает сильно тормозить. Видимо, нужно как-то сообщить оверлэю, что там тоже должно быть что-то нарисовано.
              +1
              MapController.notifyRepaint()?
                0
                Нет, это не помогло и не помогло совершенно правильно: ошибка нашлась, банальная опечатка. Тем не менее, выяснилась другая особенность. Метод draw() вызывается только если на экране присутствует хотя бы одна из точек. Если ни одна из точек не отображается, то и линия исчезает. Таким образом легко можно получить ситуацию, когда например одна точка далеко справа за границей экрана, а другая далеко слева и линия между ними не будет отображаться. Есть ли идеи, как это можно исправить?
                  +1
                  добавлять две ближайшие к центру карты в любом случае
                    0
                    Да, действительно выход. Добавил в OnMapListener / MapEvent.MSG_SCROLL_END автодобавление невидимой точки по центру. Спасибо!
                      0
                      в Overlay несколько иначе проверяют видимость OverlayItem, посмотри, может так быстрее будет

        Only users with full accounts can post comments. Log in, please.