среда, 5 декабря 2012 г.

Android Google Maps Api v2. Урок 4. Строим маршрут на карте

Итак, продолжаем нашу серию спонтанных уроков, вызванных необходимостью переводить своё приложение на новый API
Сегодня мы разберемся, как получить маршрут и отобразить его на карте. Для этого обратимся к API Google Directions.

Получать маршрут мы будем грамотно, используя AsyncTask. Мы должны обратиться к Google Service Directions, передать координаты начальной и конечной точек и распарсить JSON ответ. Например вот такой: http://maps.google.com/maps/api/directions/json?origin=55.772851,37.586806&destination=55.415003,37.899904&sensor=false

При этом мы в ответе мы получаем шифрованную полилайн для каждого шага, я написал метод decodePolilyne. Можно конечно ограничиться начальными и конечными координатами шага, но полученный результат вас не устроит, проверено на себе.

Итак, пишем интерфейс для AsyncTask, сохраняем его в файл OnRouteCalcCompleted.java:

package com.example.mapexample;

import java.util.ArrayList;

import com.google.android.gms.maps.model.LatLng;

public interface OnRouteCalcCompleted{
    void onRouteCalcBegin();
    void onRouteCompleted( ArrayList route );
}


И саму AsyncTask RouteHandler.java:


package com.example.mapexample;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.gms.maps.model.LatLng;

import android.os.AsyncTask;
import android.util.Log;


/**
 * Класс возвращает полилинию дороги, если указан mapView, отрисовывается на нём
 * @author awacs
 *
 */
public class RouteHandler extends AsyncTask {
   
    private final static String TAG = "RouteHandler";
    

    /**
     * Для точного интерполирования марштура
     */
    public static final int FINE_ROUTE = 1;
   
    /**
     * Для грубого интерполирования маршрута
     */
    public static final int COARSE_ROUTE = 2;    

 
    private final HttpClient client = new DefaultHttpClient();
    private String content;
    private boolean error = false;
    private String error_msg = "";
   
    private ArrayList polyline;
   
    private int accuracyRoute = 1;
   
    private long distance;
   
    private OnRouteCalcCompleted listener;
   
    public RouteHandler( OnRouteCalcCompleted l ){
        this.listener = l;
    }
   
    public double getDistance(){
        Log.d(TAG, "distance = " + distance + "m");
        return distance/1000;
    }
   
    public void calculateRoute( double latStart, double lonStart, double latEnd, double lonEnd, int accuracy ){
       
        this.accuracyRoute = accuracy;
       
        StringBuilder origin = new StringBuilder();
        origin.append( Double.toString(latStart));
        origin.append(",");
        origin.append( Double.toString(lonStart));       

        StringBuilder destination = new StringBuilder();
        destination.append( Double.toString(latEnd));
        destination.append(",");
        destination.append( Double.toString(lonEnd));       
       
        List nameValuePairs = new ArrayList();
        nameValuePairs.add(new BasicNameValuePair("origin", origin.toString()));
        nameValuePairs.add(new BasicNameValuePair("destination", destination.toString() ));
        nameValuePairs.add(new BasicNameValuePair("sensor", "false"));
        String paramString = URLEncodedUtils.format(nameValuePairs, "utf-8");
        execute( "http://maps.google.com/maps/api/directions/json" + "?" + paramString );

 
    }
   

    public boolean isError(){
        return error;
    }
   
    public String getErrorMsg(){
        return error_msg;
    }
   
    @Override
    protected String doInBackground(String... urls) {
       
        Log.d(TAG, "RouteHandler::doInBackground");

        try {
            Log.v(TAG, urls[0]);
            HttpPost httppost = new HttpPost(urls[0]);
            ResponseHandler responseHandler = new BasicResponseHandler();
            content = client.execute( httppost, responseHandler );
        } catch (ClientProtocolException e) {
            Log.d(TAG, "GetRouteHandler::ClientProtocolException");
            e.printStackTrace();
            error = true;
            cancel(true);
         } catch (IOException e) {
            Log.d(TAG, "GetRouteHandler::IOException");
            e.printStackTrace();
            error = true;
            cancel(true);
         }
        return content;
    }
   
    protected void onPostExecute(String content) {
        if (error) {
            error_msg = "Offline";
        } else {
            try {
                JSONObject response = new JSONObject(content);
                String status = response.getString("status");
                Log.v(TAG, content);
                if( status.equalsIgnoreCase("OK") ){
                    polyline = new ArrayList();
               
                    JSONArray routesArray = response.getJSONArray("routes");
                    JSONObject route = routesArray.getJSONObject(0);
                    // массив с информацией об отрезке маршрута
                    JSONArray legs = route.getJSONArray("legs");
                    JSONObject leg = legs.getJSONObject(0);   
                   
                    JSONObject distanceObj = leg.getJSONObject("distance");
                    distance = distanceObj.getLong("value");
                   
                    JSONObject durationObj = leg.getJSONObject("duration");
                   
                    // содержит куб выделения информационного окна для маршрута.
                    JSONObject bounds = route.getJSONObject("bounds");
                    JSONObject bounds_southwest = bounds.getJSONObject("southwest");
                    JSONObject bounds_northeast = bounds.getJSONObject("northeast");
                   
                    double maxLat = bounds_northeast.getDouble("lat");
                    double maxLon = bounds_northeast.getDouble("lng");
                    double minLat = bounds_southwest.getDouble("lat");
                    double minLon = bounds_southwest.getDouble("lng");
           

                    JSONArray steps = leg.getJSONArray("steps");
                    for( int i=0; i
                        JSONObject step = steps.getJSONObject(i);
                        JSONObject start_location = step.getJSONObject("start_location");
                        JSONObject end_location = step.getJSONObject("end_location");

                        double latitudeStart = start_location.getDouble("lat");
                        double longitudeStart = start_location.getDouble("lng");
                        double latitudeEnd = end_location.getDouble("lat");
                        double longitudeEnd = end_location.getDouble("lng");
                        LatLng startGeoPoint = new LatLng(latitudeStart,longitudeStart);
                        LatLng endGeoPoint = new LatLng(latitudeEnd,longitudeEnd);
                        JSONObject polylineObject = step.getJSONObject("polyline");
                        if( accuracyRoute == FINE_ROUTE ){
                            List points = decodePoly(polylineObject.getString("points"));
                            Log.d(TAG, " " + points.size());
                            polyline.addAll(points);
                        } else {
                            polyline.add(startGeoPoint);
                            polyline.add(endGeoPoint);
                        }
                    }

                } else if( status.equalsIgnoreCase("NOT_FOUND")){
                    // по крайней мере для одной заданной точки (исходной точки, пункта назначения или путевой точки) геокодирование невозможно.
                } else if( status.equalsIgnoreCase("ZERO_RESULTS")){
                    // между исходной точкой и пунктом назначения не найдено ни одного маршрута.
                } else if( status.equalsIgnoreCase("MAX_WAYPOINTS_EXCEEDED")){
                    // в запросе задано слишком много waypoints. Максимальное количество waypoints равно 8 плюс исходная точка и пункт назначения. ( (Пользователи Google Maps Premier могут выполнять запросы с количеством путевых точек до 23.)
                } else if( status.equalsIgnoreCase("INVALID_REQUEST")){
                    // запрос недопустим
                }else if( status.equalsIgnoreCase("OVER_QUERY_LIMIT")){
                    // служба получила слишком много запросов от вашего приложения в разрешенный период времени.
                }else if( status.equalsIgnoreCase("REQUEST_DENIED")){
                    // служба Directions отклонила запрос вашего приложения.
                }else if( status.equalsIgnoreCase("UNKNOWN_ERROR")){
                    // обработка запроса маршрута невозможна из-за ошибки сервера. При повторной попытке запрос может быть успешно выполнен
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
       
        listener.onRouteCompleted( polyline );
       
    } // end postExecute
   
    /**
     * Декодирует полилинию из переданной гуглом строки
     * @param encoded
     * @return
     */
    private List decodePoly(String encoded) {

        List poly = new ArrayList();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng( lat/1E5, lng/1E5);
            poly.add(p);
        }

        return poly;
    } // end decodePoly      
   
} // end class


Для использования этого таска в нашей активити, нужно имплементить OnRouteCalcCompleted, соответвенно в Activity добавятся методы:
    @Override
    public void onRouteCalcBegin() {
        // Тут можно добавить например вызов прогрессбара, "Ждите, строим маршрут...."
    }

а отобразить готовую полилинию можно в добавленном onRouteCompleted:


    @Override
    public void onRouteCompleted(ArrayList route) {
        mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
        // а заодно и удалить прогрессбар :)
    }


Да, теперь добавляем например в инициализацию mMap из урока 3 код для вызова AsyncTask и компилируем готовое приложение.

        private RouteHandler routeHandler;

    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        }
        routeHandler = new RouteHandler( this );
        routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, RouteHandler.FINE_ROUTE);       
    }


Код полной активити:

package com.example.mapexample;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import java.sql.Time;
import java.util.ArrayList;

import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;


public class MainActivity extends FragmentActivity implements LocationSource, LocationListener, OnRouteCalcCompleted{
   
    private final static String TAG = "MainActivity";

    private OnLocationChangedListener mListener;
    private LocationManager lManager;
   
    private GoogleMap mMap;
   
    private static double mLatitude;
    private static double mLongitude;   
   
    private RouteHandler routeHandler;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        //mMap.setTrafficEnabled(true);
        mMap.setMyLocationEnabled(true);
        mMap.setOnMapLongClickListener(this);
        lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);       
        setUpMapIfNeeded();
    }
   
    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
        if( lManager != null ){
            lManager.requestLocationUpdates(lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
        }       
    }
   
    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        }
        routeHandler = new RouteHandler( this );
        routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, AppSettings.FINE_ROUTE);       
    }


     protected void onPause() {
         if( lManager != null ){
             lManager.removeUpdates(this);
         }
         super.onPause();
     }   
   



    @Override
    public void onLocationChanged(Location location) {
        if( mListener != null ){
            mListener.onLocationChanged( location );
        }
    }

    @Override
    public void activate(OnLocationChangedListener listener) {
        mListener = listener;
       
    }

    @Override
    public void deactivate() {
        mListener = null;
    }

    @Override
    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onRouteCalcBegin() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onRouteCompleted(ArrayList route) {
        mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
    }

}



Android Google Maps API v2. Урок 3. Получение и отображение текущей позиции

Итак, продолжаем нашу серию экспериментов над новым API Google Maps.
В уроке #1 мы получали Google Api Key, в уроке #2 мы отображали карту. Следующим этапом мы научимся показывать нашу текущую позицию. Сейчас информации крайне мало, возможно в будущем код будет отрефакторен.

Наш главный layout сейчас имеет вид


    
    


и нисколько в данном уроке не изменится.

1. Имплементим к нашей Activity интерфейс LocationSource и LocationListener
Первый нужен для отображения текущей позиции, второй для получения этой самой позиции

Объявляем следующие члены класса:

    private OnLocationChangedListener mListener;
    private LocationManager lManager;
    private GoogleMap mMap;


В onCreate добавляем вызов private метода setUpMapIfNeeded(), в котором:
а) получаем mMap с нашего лэйаута
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();

б) Включаем слушатель текущего положения:
mMap.setMyLocationEnabled(true);

Далее инициализируем LocationManager для получения текущего местоположения:
        lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);

Весь код метода onCreate:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);       
        setUpMapIfNeeded();
    }


    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
            mMap.setMyLocationEnabled(true);
        }
    }

Т.к. мы имплементили LocationSource, нам будут добавлены два метода, activate() и deactivate(), где следует инициализировать и уничтожать mListener, слушающий текущее местоположение и отображающий его на mMap:

    @Override
    public void activate(OnLocationChangedListener listener) {
        mListener = listener;
       
    }

    @Override
    public void deactivate() {
        mListener = null;
    }

Аналогично с LocationListener добавляются несколько методов, нас сейчас будет интересовать только onLocationChanged(), в аргументы которого передается текущее местоположение. Этот параметр нам нужно просто передать mListener, остальное сделает за нас GoogleMap:
    @Override
    public void onLocationChanged(Location location) {
        if( mListener != null ){           
             mListener.onLocationChanged( location );
        }
    }

Ну и не забываем убивать и восстанавливать LocationManager в onResume / onPause. Этим мы уберегаем себя от жалоб пользователей на повышенный расход батареи от нашего приложения ;)

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
        if( lManager != null ){
            lManager.requestLocationUpdates(lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
        }       
    }

    @Override
     protected void onPause() {
         if( lManager != null ){
             lManager.removeUpdates(this);
         }
         super.onPause();
     }   


Получившийся код нашей Activity:





package com.example.mapexample;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import java.sql.Time;
import java.util.ArrayList;

import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;


public class MainActivity extends FragmentActivity implements LocationSource, LocationListener, OnRouteCalcCompleted, OnMapLongClickListener{
 
 private final static String TAG = "MainActivity";

 private OnLocationChangedListener mListener;
 private LocationManager lManager;
 
 private GoogleMap mMap;
 
 private static double mLatitude;
 private static double mLongitude; 
 
 private RouteHandler routeHandler;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
  lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);  
  setUpMapIfNeeded();
 }
 
    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
  if( lManager != null ){ 
      mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
      mMap.setMyLocationEnabled(true);
  }        
    }
    
    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        }
    }


  protected void onPause() {
   if( lManager != null ){
    lManager.removeUpdates(this);
   }
   super.onPause();
  } 
 



 @Override
 public void onLocationChanged(Location location) {
  if( mListener != null ){
   mListener.onLocationChanged( location );
  }
 }

 @Override
 public void activate(OnLocationChangedListener listener) {
  mListener = listener;
  
 }

 @Override
 public void deactivate() {
  mListener = null;
 }

 @Override
 public void onProviderDisabled(String provider) {
  // TODO Auto-generated method stub
  
 }

 @Override
 public void onProviderEnabled(String provider) {
  // TODO Auto-generated method stub
  
 }

 @Override
 public void onStatusChanged(String provider, int status, Bundle extras) {
  // TODO Auto-generated method stub
  
 }

}

iTunes

В силу специфики своей работы имею несколько смартфонов и планшетов. Большинство из них работают под Android, но есть среди них и один iPhone. Но скажите мне, каким местом думала Apple, создавая iTunes? Не, задумка в-принципе нормальная, не самая лучшая, но и не худшая. Но этот интуитивно-непонятный интерфейс меня полностью выбешивает. Разобраться как добавить в этот е**ый iTunes папку с музыкой с компьютера, потом сделать из них мало-мальски понятный плейлист, затем всё это положить на устройство, при этом делается всё через левую пятку правого уха... А в следующий раз начинается всё сначала, потому что последовательность этого шаманства полностью вылетает из головы. Возможно конечно, что всё это сделано для покупки контента у них

А вот и отзыв Алекса Экслера. Я с ним полностью солидарен :)

вторник, 4 декабря 2012 г.

Android Google Maps API v2. Урок 2. Отображение карты

Итак, в первом уроке мы получили ключ разработчика и сейчас хотим его использовать по назначению.

1. Скачиваем необходимые библиотеки

Первым делом необходимо скачать и установить Google Play SDK и Android Support Library, сделать это можно из Eclipse. Для этого заходим в меню Window - Android SDK Manager, внизу находим Extras - Google Play Services и Extras - Android Support Library, жмем чекбокс и кнопку Install Packages.

Пакеты устанавливаются...

Устанавливаются...

Установились.

2. Добавляем библиотеки в Eclipse.

Необходимо в Eclipse добавить эти экстрасы. Правой кнопкой в Package Explorer. Import. Android - Existing Android Code Into Workspace. Выбираем директорию, в которой был установлен Android SDK, далее в ней папку Extras, и далее:
а) android - support - v4. Finish
б) google - google_play_services - libproject - google-play-services_lib. Finish

3. Создаем новый проект в Eclipse

Как создать проект я думаю все знают. А кто не знает - найдет кучу инструкций по Android Hello World. После создания проекта подключаем к нему библиотеки. Правой кнопкой по проекту в Package Explorer. Properties. Android. Видим вкладку Library и кнопку Add. Добавляем скаченные и импортнутые библиотеки.
Заодно проверяем версию проекта, она должна быть не ниже 2.2!


4. Правим Android Manifest

Добавляем необходимые разрешения. Здесь они немного избыточные, но это нам пригодится в будущем:
    
    

    
    

   
    
    
    
    
    

    

    

    

    

    

    

    
    
 
Здесь нужно подставить название своего пакета


В секции (!!!) добавляем свой API ключ:
        



И собственно всё

5. Правим Activity
Вместо Activity в секцию extends пишем FragmentActivity

6. Правим Layout
В layout нашей активити пишем:


Собираем приложение и проверяем. Желательно на устройстве, нормальной работы интернета на эмуляторе лично я не получил.


Android Google Maps API v2. Урок 1. Получаем код API


Итак, чтобы писать приложения Android, использующие карты Google необходимо обзавестись API ключом. Для этого:
  1. Заходим на консоль Google API Console и авторизуемся. Естественно, аккаунт на один из сервисов Google у вас уже должен быть
  2. Для нас уже будет создан проект под названием API Project. Но можно создать и свой (нажать на кнопку API Project сверху слева, выбрать из меню пункт Create, ввести имя проекта)
  3. Выбираем в левом меню пункт Services, получаем список сервисов которые можно подключить к проекту. Ищем в списке Google Maps Android API v2. Переводим переключатель в значение ВКЛ. 
В меню слева выбрать пункт API access (Доступ к API). Ваш ключ указан на этой странице.

Этот ключ необходимо скопировать в ваш Android Manifest. Куда именно - будет рассказано в следующем уроке

среда, 28 ноября 2012 г.

Android. Как поставить маркер (пин, булавку) на карте MapView

Допустим мы хотим поставить маркеры или булавки (или как они называются) на карте, зная широту и долготу.

1. После создания проекта Android вы должны создать класс MyItemizedOverlay, наследуемый от ItemizedOverlay и переопределить некоторые методы. Код показан ниже:


package com.example;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;
import java.util.ArrayList;
/**
 * Description
 *
 * @author : awacs
 * @since: 28/11/12
 */
public class PinOverlay extends ItemizedOverlay {
    private ArrayList mOverlays = new ArrayList();
    Context mContext;
    public PinOverlay(Drawable marker) {
        // центром каплевидного маркера считаем его нижнюю центральную часть.
        super(boundCenterBottom(marker));
    }
    public MyItemizedOverlay(Drawable marker, Context context) {
        super(boundCenterBottom(marker));
        mContext = context;
    }
    public void addOverlay(OverlayItem overlay) {
        mOverlays.add(overlay);
        populate();
    }
    @Override
    protected OverlayItem createItem(int i) {
        return mOverlays.get(i);
    }
    @Override
    public int size() {
        return mOverlays.size();
    }
    @Override
    protected boolean onTap(int i) {
        //Когда мы тапаем по маркеру, вызывается этот Алерт
        OverlayItem item = mOverlays.get(i);
        AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
        dialog.setTitle(item.getTitle());
        dialog.setMessage(item.getSnippet());
        dialog.show();
        return true;
    }
}
2. Находим на просторах интернета какой-нибудь маркер и копируем его в папку drawable проекта. Называем его marker.png
3. А в главной Activity, которая у нас наследуется от MapActivity пишем следующее:
package com.example;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import com.google.android.maps.*;
import java.util.List;
public class MyActivity extends MapActivity {
    private MapView mapView;
    private MapController mapController;
    private GeoPoint geoPoint;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mapView = (MapView) findViewById(R.id.mapview);
        //Устанавливаем масштаб
        mapView.getController().setZoom(12);
        //разрешаем пользоваться зумом
        mapView.setBuiltInZoomControls(true);
        // список слоев
        List mapOverlays = mapView.getOverlays();
        // наш маркер
        Drawable drawable = this.getResources().getDrawable(R.drawable.marker);
         
                // слой с маркером
        PinOverlay itemizedoverlay = new PinOverlay(drawable, this);
       // координаты где будем ставить маркер (46.06, 23.57) 
       GeoPoint point = new GeoPoint(46066940, 23570000);
       
       // центрируем карту на нашей точке  
       mapView.getController().setCenter(point);
       
       // создаем пин, назначаем его заголовок и текст для алерта
       OverlayItem overlayitem = new OverlayItem(point, "Title for dialog", "Alba Iulia City From Romania");
               
                // еще один пин
        GeoPoint point2 = new GeoPoint(35410000, 139460000);
        OverlayItem overlayitem2 = new OverlayItem(point2, "Title for dialog", "Japan");
                
                // добавляем пин на слой
        itemizedoverlay.addOverlay(overlayitem);
        itemizedoverlay.addOverlay(overlayitem2);
        
        // добавляем слой     
        mapOverlays.add(itemizedoverlay);
    }
    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }
}

вторник, 27 ноября 2012 г.

МФУ HP Deskjet 3525

Купил МФУ HP Deskjet 3525 в Эльдорадо. Выбрал этот магазин потому что в маркете разброс цен небольшой. Если заказывать с интернета, получится экономия в 200 рублей. А магазин находился в Химках почти рядом с домом, ждать не хотелось

Официальная страница принтера

Характеристики

Общие характеристики
Устройствопринтер/сканер/копир
Тип печатицветная
Технология печатитермическая струйная
Размещениенастольный
Область примененияперсональный
Количество страниц в месяц1000

Принтер
Максимальный форматA4
Автоматическая двусторонняя печатьесть
Печать фотографийесть
Количество цветов4
Печать без полейесть
Максимальное разрешение для ч/б печати1200x600 dpi
Максимальное разрешение для цветной печати4800x1200 dpi
Время печати 10x15 см (цветн.)19 с


Сканер
Тип сканерапланшетный
Максимальный формат оригиналаA4
Максимальный размер сканирования216x297 мм
Глубина цвета24 бит
Оттенки серого256
Разрешение сканера1200x1200 dpi
Поддержка стандартовTWAIN

Копир
Максимальное разрешение копира (ч/б)600x600 dpi
Максимальное разрешение копира (цветн.)600x600 dpi
Изменение масштаба25-400 %
Максимальное количество копий за цикл50 

Лотки
Подача бумаги80 лист. (стандартная), 80 лист. (максимальная)
Вывод бумаги15 лист. (стандартный), 15 лист. (максимальный)
 
Расходные материалы
Плотность бумаги75-300 г/м2
Печать на:карточках, пленках, этикетках, фотобумаге, глянцевой бумаге, конвертах, матовой бумаге
Ресурс цветного картриджа/тонера600 страниц
Ресурс ч/б картриджа/тонера550 страниц
Количество картриджей4
Тип картриджа/тонеракартриджи HP 655: черный CZ109A, голубой CZ110A, пурпурный CZ111A, желтый CZ112A
 
 
Интерфейсы
ИнтерфейсыWi-Fi, 802.11n, USB 2.0
Поддержка AirPrintесть
Шрифты и языки управления
Поддержка PostScriptнет
Дополнительная информация
Поддержка ОСWindows, Mac OS
Отображение информацииЖК-панель
Диагональ дисплея2 дюйм.
Потребляемая мощность (при работе)13.38 Вт
Потребляемая мощность (в режиме ожидания)2.72 Вт
Габариты (ШхВхГ)440x144x365 мм
Вес5 кг
Особенностискорость черно-белой печати: ISO, лазерное качество - до 8 стр./мин; скорость цветной печати: ISO, лазерное качество - до 7.5 стр./мин



Упаковка

Коробка на вид красивая, но пенопласта пожалели. Упакован в картон, кабели/инструкция/картриджи - болтаются в картонный поддоне прямо под отверстиями для переноски. Выпасть им не составляет особого труда.












Внешний вид, комплектация


Внешний вид МФУ производит впечатление "бюджетного". Дешевый черный матовый пластик, который быстро царапается, хлипкая крышка, хлипкий лоток. Открыл верхнюю крышку. Она чиркнула по подставке с карандашами - и вот первая царапина от цветного карандаша. В сложенном виде нет никаких технологических отверстий, т.е. пылиться будет только внешний корпус, что радует. Вообще принтер очень компактный, спокойно поместится на низкой полке. Особенно если учитывать что из проводов ему нужен только шнур питания. Бумажной инструкции по эксплуатации нет ни на одном языке. На русском только руководство по устранению неисправностей. Компакт-диск на наличие инструкции не изучал, взял с него только драйверы.

На внешней панели расположены псевдосенсорные (обычные, под пленкой) управляющие кнопки по бокам ЖК дисплея, кнопки включения/отключения WiFi и ePrint с синим индикатором включения, а также "железная" кнопка питания. ЖК панель черно-белая, яркая, с подсветкой, отлично видно всегда и везде. Т.к. дисплей черно-белый, внизу есть метки под цвет картриджей.
Лоток сканера небольшой, под формат А4, не Legal. Т.к. у нас в-основном используется этот формат - это плюс, нет лишних ненужных перекосов бумаги, особенно при копировании.


Функциональность

Хотя внешний вид не очень, функциональность подкупает. Во-первых, это беспроводное соединение по WiFi как в HP 3050 или HP 3070. Но в 3050 вы получаете дорогие быстрорасходуемые картриджи, примерно в половину меньше на картридж чем в 3525. А здесь - дешевые легально перезаправляемые картриджи и двустороннюю печать. Брался как "школьный" принтер для сына но оказалось, что он отлично печатает и фотографии.
Стартовый комплект картриджей сначала показывает 100% уровень заправки. Пока не знаю, насколько его хватит, но отпишусь прямо здесь, не забуду.


Загрузка бумаги в передней части, но не совсем понятно до какого упора ее туда надо пихать. Можно и до упора, можно до первого сопротивления - всё равно глотает нормально. Но может потом и выяснится, что так делать не надо было и начнет жевать бумагу. Отпечатки он отдает тоже спереди, если они упадут в подающий лоток то перепутаются с чистыми листами. В работе принтер немного шумноват, но никакого дискомфорта не вызывает.
На 3050 видел жалобы что он сложен в настройке сети. В 3525 все оказалось очень легко. Установил компакт в ноутбук, запустил программу установки, которая на одном из этапов попросит подключить принтер. Дальше она сделала всё сама. Сетку увидел очень быстро, ввел свой пароль и всё! В определенный момент принтер отпечатает страницу, которую нужно будет положить в сканер для калибровки. Затем он напечатает логин/пароль для входа в ePrint. Зарегистрировавшись в e-print можно отправлять любые документы по е-мэйл и они будут тут же отпечатаны.
Замечу, что WiFi модуль не очень хорош. Показывает уровень средний-слабый там, где ноуты/смартфоны берут на "отлично". Но у меня он стоит под роутером, так что не сильно напрягает. Передача данных между принтером-роутером-ноутом происходит мгновенно и незаметно для глаза пользователя (меня т.е.).

По локальному сетевому адресу доступна web-страница принтера, на которой можно происзвести определенные настройки, а также задать пароль (рекомендую) чтобы туда больше никто не залез.


Android. В GooglePlay по запросу hp print выдает с десяток программ от хьюлета. Всё описание на английском, так же как и меню программ. Так что придется разбираться. Первым делом чтоб увидеть МФУ нужно ставить HP Printer Control и производить в нем настройки.

P.S. Магазин Эльдорадо как всегда поразил своей безалаберностью. Если б мне не нужен был срочно принтер, ни за что туда не пошел. Менеджер подскочил довольно быстро. Попытался мне втюхать дополнительный USB кабель заявив, что в комплекте его нет (откуда он это взял - непонятно). Затем попытался мне втюхать дополнительный комплект картриджей потому что они у них "дешевые". Я прекрасно знаю минимальную их цену, цена в Эльдорадо от нее мало отличается. Ну фик с ним, допуслуги предлагать - это нормально, главное чтоб не врали. 
Оплатив на кассе, я спросил где я могу забрать товар. Кассир меня направил к пункту проверки, а пункт проверки зачем-то на охрану. Оказалось в итоге, что мне сначала нужно забрать коробку в зале у менеджера, затем подойти на пункт обслуживания для проверки/гарантии, а затем на охране сверить серийник чека и коробки. Больше такого геморроя и некомпетентности сотрудников я не видел ни в одном магазине.