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

本文介绍文件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">
    
	<LinearLayout
		android: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 {

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// 在这个函数内创建菜单选项
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public 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);
            }
        }
    }
    
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值