本文介绍文件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); } } } }