Android开源代码解读の地图照片应用Panoramio的实现详解(六)

来源:互联网 发布:如何申请淘宝账号 编辑:程序博客网 时间:2024/05/16 23:41

本文介绍文件ViewImage.java和ViewMap.java。前者实现单张图片信息的浏览,后者实现自定义的地图,用于显示图片拍摄地点和用户当前所在地点。ViewImage实现的Activity界面如左下图,点击手机的菜单键时,弹出菜单选项界面如右下图:

                                                                          

上面Activity用到的布局文件view_image.xml如下:

<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:fillViewport="true">    <LinearLayoutandroid:id="@+id/content"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:gravity="center_horizontal"    >        <FrameLayout    android:layout_width="fill_parent"     android:layout_height="0dip"    android:layout_weight="1"        android:padding="10dip">           <LinearLayout    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:layout_gravity="center"    android:gravity="center_horizontal"    >    <ImageView android:id="@+id/image"android:layout_width="wrap_content"     android:layout_height="wrap_content"    android:background="@drawable/picture_frame"    android:scaleType="fitCenter"    android:adjustViewBounds="true"    android:maxHeight="320dip"    android:layout_alignParentTop="true"    android:layout_centerHorizontal="true"    />   <TextView    android:id="@+id/title" android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:textSize="20dip"    android:singleLine="true"    android:ellipsize="end"    android:textColor="?android:attr/textColorPrimary"    android:paddingLeft="5dip"       android:paddingRight="5dip"    android:layout_alignLeft="@id/image"    android:layout_alignRight="@id/image"       android:layout_below="@id/image"    />    <TextView    android:id="@+id/owner" android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:textSize="16dip"    android:singleLine="true"    android:ellipsize="end"    android:textColor="?android:attr/textColorPrimary"       android:paddingLeft="5dip"       android:paddingRight="5dip"android:layout_alignLeft="@id/title"    android:layout_alignRight="@id/title"android:layout_below="@id/title"    />    </LinearLayout>        </FrameLayout>     <TextView android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:textSize="13dip"    android:textColor="?android:attr/textColorSecondary"android:text="@string/copyright"android:gravity="center"android:paddingLeft="10dip"android:paddingRight="10dip"    />    </LinearLayout></ScrollView>

ViewImage根据从ImageList传递过来的URL等信息,开启后台线程从服务器加载中等大小图片信息,用到的UI组件主要有菜单选项,提示对话框等。

菜单选项的实现需要重写Activity的两个函数:onCreateOptionsMenu和onOptionsItemSelected,程序框架如下:

import android.app.Activity;import android.view.Menu;import android.view.MenuItem;public class MainActivity extends Activity {@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// 在这个函数内创建菜单选项return super.onCreateOptionsMenu(menu);}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// 定义菜单项被选中时的响应事件return super.onOptionsItemSelected(item);}}

对话框的实现需要重写Activity的onCreateDialog函数,并在函数中创建对话框。然后在需要显示的地方调用Activity的showDialog函数,显示onCreateDialog中创建的对话框。ViewImage的实现代码如下:

package com.google.android.panoramio;import com.google.android.maps.GeoPoint;import android.app.Activity;import android.app.AlertDialog;import android.app.Dialog;import android.content.ActivityNotFoundException;import android.content.Intent;import android.graphics.Bitmap;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.Window;import android.widget.ImageView;import android.widget.TextView;   /** * 显示单张图片的Activity */public class ViewImage extends Activity {    private static final String TAG = "Panoramio";    private static final int MENU_RADAR = Menu.FIRST + 1;    private static final int MENU_MAP = Menu.FIRST + 2;    private static final int MENU_AUTHOR = Menu.FIRST + 3;    private static final int MENU_VIEW = Menu.FIRST + 4;    private static final int DIALOG_NO_RADAR = 1;    PanoramioItem mItem;    private Handler mHandler;    private ImageView mImage;    private TextView mTitle;    private TextView mOwner;        private View mContent;    private int mMapZoom;    private int mMapLatitudeE6;    private int mMapLongitudeE6;    @Override    protected void onCreate(Bundle savedInstanceState) {        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        super.onCreate(savedInstanceState);        setContentView(R.layout.view_image);        // 从ImageList传递过来的搜索区域信息        Intent i = getIntent();        mItem = i.getParcelableExtra(ImageManager.PANORAMIO_ITEM_EXTRA);        mMapZoom = i.getIntExtra(ImageManager.ZOOM_EXTRA, Integer.MIN_VALUE);        mMapLatitudeE6 = i.getIntExtra(ImageManager.LATITUDE_E6_EXTRA, Integer.MIN_VALUE);        mMapLongitudeE6 = i.getIntExtra(ImageManager.LONGITUDE_E6_EXTRA, Integer.MIN_VALUE);                mHandler = new Handler();        mContent = findViewById(R.id.content);        mImage = (ImageView) findViewById(R.id.image);        mTitle = (TextView) findViewById(R.id.title);        mOwner = (TextView) findViewById(R.id.owner);                //初始化时将内容设置为不可见,等待后台线程加载数据完成后再显示        mContent.setVisibility(View.GONE);        getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,                Window.PROGRESS_VISIBILITY_ON);        new LoadThread().start();    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        super.onCreateOptionsMenu(menu);        menu.add(0, MENU_RADAR, 0, R.string.menu_radar) //添加雷达菜单项                .setIcon(R.drawable.ic_menu_radar)                .setAlphabeticShortcut('R');        menu.add(0, MENU_MAP, 0, R.string.menu_map) //添加地图菜单项                .setIcon(R.drawable.ic_menu_map)                .setAlphabeticShortcut('M');        menu.add(0, MENU_AUTHOR, 0, R.string.menu_author) //添加作者信息菜单项                .setIcon(R.drawable.ic_menu_author)                .setAlphabeticShortcut('A');        menu.add(0, MENU_VIEW, 0, R.string.menu_view) //添加浏览器中浏览图片菜单项                .setIcon(android.R.drawable.ic_menu_view)                .setAlphabeticShortcut('V');        return true;    }        @Override    public boolean onOptionsItemSelected(MenuItem item) {        switch (item.getItemId()) {        case MENU_RADAR: {            // 启动radar应用            Intent i = new Intent("com.google.android.radar.SHOW_RADAR");            GeoPoint location = mItem.getLocation();            i.putExtra("latitude", (float)(location.getLatitudeE6() / 1000000f));            i.putExtra("longitude", (float)(location.getLongitudeE6() / 1000000f));            try {                startActivity(i);            } catch (ActivityNotFoundException ex) {                showDialog(DIALOG_NO_RADAR); //radar应用不存在,弹出提示对话框            }            return true;        }        case MENU_MAP: {            // 启动自定义的地图 ViewMap            Intent i = new Intent(this, ViewMap.class);            i.putExtra(ImageManager.PANORAMIO_ITEM_EXTRA, mItem);            i.putExtra(ImageManager.ZOOM_EXTRA, mMapZoom);            i.putExtra(ImageManager.LATITUDE_E6_EXTRA, mMapLatitudeE6);            i.putExtra(ImageManager.LONGITUDE_E6_EXTRA, mMapLongitudeE6);                        startActivity(i);            return true;        }        case MENU_AUTHOR: {            // 在浏览器中显示作者信息            Intent i = new Intent(Intent.ACTION_VIEW);            i.setData(Uri.parse(mItem.getOwnerUrl()));            startActivity(i);            return true;        }        case MENU_VIEW: {        // 在浏览器中显示图片信息            Intent i = new Intent(Intent.ACTION_VIEW);            i.setData(Uri.parse(mItem.getPhotoUrl()));            startActivity(i);            return true;        }        }        return super.onOptionsItemSelected(item);    }        @Override    protected Dialog onCreateDialog(int id) {        switch (id) {        case DIALOG_NO_RADAR:        //构造提示对话框            AlertDialog.Builder builder = new AlertDialog.Builder(this);            return builder.setTitle(R.string.no_radar_title)                .setMessage(R.string.no_radar)                .setIcon(android.R.drawable.ic_dialog_alert)                .setPositiveButton(android.R.string.ok, null).create();        }        return null;    }    /**     * 用于加载medium大小位图的线程     */    private class LoadThread extends Thread {        public LoadThread() {        }        @Override        public void run() {            try {                String uri = mItem.getThumbUrl();                uri = uri.replace("thumbnail", "medium"); //修改图片URL                final Bitmap b = BitmapUtils.loadBitmap(uri); //加载中等尺寸的位图                                //通告UI线程更改界面                mHandler.post(new Runnable() {                    public void run() {                        mImage.setImageBitmap(b);                        mTitle.setText(mItem.getTitle());                        mOwner.setText(mItem.getOwner());                        mContent.setVisibility(View.VISIBLE);                        getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,                                Window.PROGRESS_VISIBILITY_OFF);                    }                });            } catch (Exception e) {                Log.e(TAG, e.toString());            }        }    }}

ViewMap继承自MapActivity,用于在自定义地图上显示当前用户位置和照片拍摄位置,实现的界面如下图:

                        

在Android中使用Google的地图服务,需要实现MapView组件(或实现MapActivity),当程序中用到MapView时,需要在AndroidManifest.xml文件的application标签内添加类库使用说明:

<uses-library android:name="com.google.android.maps" />

同时添加权限许可说明如下:

<uses-permission android:name="android.permission.INTERNET"/>

具体代码如下所示:

package com.google.android.panoramio;import com.google.android.maps.GeoPoint;import com.google.android.maps.MapActivity;import com.google.android.maps.MapController;import com.google.android.maps.MapView;import com.google.android.maps.MyLocationOverlay;import com.google.android.maps.Overlay;import com.google.android.maps.Projection;import android.content.Intent;import android.graphics.Canvas;import android.graphics.Point;import android.graphics.drawable.Drawable;import android.os.Bundle;import android.view.Gravity;import android.view.View;import android.view.ViewGroup.LayoutParams;import android.widget.FrameLayout;import java.util.ArrayList;import java.util.List;/** * 自定义的地图,显示照片中所在地和用户现在所在地点 */public class ViewMap extends MapActivity {    private MapView mMapView;    private MyLocationOverlay mMyLocationOverlay;    ArrayList<PanoramioItem> mItems = null;    private PanoramioItem mItem;    private Drawable mMarker; //照片拍摄位置标识点    private int mMarkerXOffset; //标识点的X坐标    private int mMarkerYOffset; //标识点的Y坐标    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //代码实现布局        FrameLayout frame = new FrameLayout(this);        mMapView = new MapView(this, "MapViewCompassDemo_DummyAPIKey");        frame.addView(mMapView,                 new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));        setContentView(frame);        mMyLocationOverlay = new MyLocationOverlay(this, mMapView);        //加载资源文件        mMarker = getResources().getDrawable(R.drawable.map_pin);                // 给mMarker设置绘制在Overlay上的边界        final int intrinsicWidth = mMarker.getIntrinsicWidth();        final int intrinsicHeight = mMarker.getIntrinsicHeight();        mMarker.setBounds(0, 0, intrinsicWidth, intrinsicHeight);                mMarkerXOffset = -(intrinsicWidth / 2);        mMarkerYOffset = -intrinsicHeight;                // 获取从ViewImage传递过来的数据        Intent i = getIntent();        mItem = i.getParcelableExtra(ImageManager.PANORAMIO_ITEM_EXTRA);        int mapZoom = i.getIntExtra(ImageManager.ZOOM_EXTRA, Integer.MIN_VALUE);        int mapLatitudeE6 = i.getIntExtra(ImageManager.LATITUDE_E6_EXTRA, Integer.MIN_VALUE);        int mapLongitudeE6 = i.getIntExtra(ImageManager.LONGITUDE_E6_EXTRA, Integer.MIN_VALUE);                //给MapView添加两层Overlay        final List<Overlay> overlays = mMapView.getOverlays();        overlays.add(mMyLocationOverlay);        overlays.add(new PanoramioOverlay());                //设置MapView的缩放级别和中心点        final MapController controller = mMapView.getController();        if (mapZoom != Integer.MIN_VALUE && mapLatitudeE6 != Integer.MIN_VALUE                && mapLongitudeE6 != Integer.MIN_VALUE) {            controller.setZoom(mapZoom);            controller.setCenter(new GeoPoint(mapLatitudeE6, mapLongitudeE6));        } else {        //Locaiton获取失败,设置默认缩放级别为15,并给消息队列中添加一个Runnable对象        //当Location修复时,将在新的线程中将地图移动到指定中心点            controller.setZoom(15);            mMyLocationOverlay.runOnFirstFix(new Runnable() {                public void run() {                    controller.animateTo(mMyLocationOverlay.getMyLocation());                }            });        }        mMapView.setClickable(true);        mMapView.setEnabled(true);        mMapView.setSatellite(true);        addZoomControls(frame); //给地图添加缩放控制组件    }    @Override    protected void onResume() {        super.onResume();        //尝试使能Location服务,该函数注册为LocationManager.GPS_PROVIDER和        //LocationManager.NETWORK_PROVIDER的观察者        mMyLocationOverlay.enableMyLocation();    }    @Override    protected void onStop() {    //停止Location更新        mMyLocationOverlay.disableMyLocation();        super.onStop();    }    /**     * 获取缩放控制组件,并添加到MapView视图的下方     */    @SuppressWarnings("deprecation")private void addZoomControls(FrameLayout frame) {        View zoomControls = mMapView.getZoomControls();        FrameLayout.LayoutParams p =             new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,                LayoutParams.WRAP_CONTENT, Gravity.BOTTOM + Gravity.CENTER_HORIZONTAL);        //将地图缩放组件放到地图MapView的下方        frame.addView(zoomControls, p);    }        @Override    protected boolean isRouteDisplayed() {        return false; //不支持路线信息显示    }        /**     * 显示照片拍摄地点图钉资源的Overlay     */    public class PanoramioOverlay extends Overlay {        @Override        public void draw(Canvas canvas, MapView mapView, boolean shadow) {            if (!shadow) {                Point point = new Point();                //获取投影类,在屏幕像素点和经纬度坐标点之间转换                Projection p = mapView.getProjection();                //将经纬度点信息GeoPoint对象转换成MapView视图上的坐标点Point对象                p.toPixels(mItem.getLocation(), point);                 super.draw(canvas, mapView, shadow);                //在指定的x和y坐标点上绘制drawable对象mMarker                drawAt(canvas, mMarker, point.x + mMarkerXOffset, point.y + mMarkerYOffset, shadow);            }        }    }    }