本文继续讲解Panoramio的实现,主要介绍ImageAdapter.java和ImageList.java 这两个文件,这两个文件实现了如下所示的界面,左图是数据从网络加载过程中,有图是加载完成后的效果:
ImageAdapter继承自BaseAdapter类,实现图片适配器的功能,而ImageList则继承自ListActivity,用于以列表形式显示搜索到的图片信息。介绍之前,先来普及下DataSetObserver和DataSetObservable的知识。
从名字可以依稀猜到,DataSetObserver实现了观察者模式中的观察者角色(Observer)。当数据集发生变化或者变为无效时,DataSetObserver中的方法被回调,典型的数据集有Cursor和Adapter,当某个对象要添加到DataSetObservable中时,这个对象必须从DataSetObserver继承,DataSetObserver是一个抽象类,定义如下:
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
* {@link Cursor}.
*/
public void onInvalidated() {
// Do nothing
}
}
而DataSetObservable实现了观察者模式中的对象角色(Subject),它是Observable的具体实现,提供了调用DataSetObserver中各种回调函数的方法,定义如下所示:
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes onChanged on each observer. Called when the data set being observed has
* changed, and which when read contains the new state of the data.
*/
public void notifyChanged() {
synchronized(mObservers) {
for (DataSetObserver observer : mObservers) {
observer.onChanged();
}
}
}
/**
* Invokes onInvalidated on each observer. Called when the data set being monitored
* has changed such that it is no longer valid.
*/
public void notifyInvalidated() {
synchronized (mObservers) {
for (DataSetObserver observer : mObservers) {
observer.onInvalidated();
}
}
}
}
注意这里的Observable是定义在android.database包中的,而不是JDK中的Observable,它的定义如下:
package android.database;
import java.util.ArrayList;
/**
* Provides methods for (un)registering arbitrary observers in an ArrayList.
*/
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* Adds an observer to the list. The observer cannot be null and it must not already
* be registered.
* @param observer the observer to register
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is already registered
*/
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
/**
* Removes a previously registered observer. The observer must not be null and it
* must already have been registered.
* @param observer the observer to unregister
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is not yet registered
*/
public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}
/**
* Remove all registered observer
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
对观察者模式的详细描述,可见这篇文章http://blog.csdn.net/ace1985/article/details/5753658 。OK,言归正传,还是来看下我们的ImageAdapter.java,从上面的分析和代码里面的注释应该很好理解了:
package com.google.android.panoramio;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/**
* 用来给ImageList绑定图片资源的适配器
*/
public class ImageAdapter extends BaseAdapter {
/**
* Maintains the state of our data
*/
private ImageManager mImageManager;
private Context mContext;
private MyDataSetObserver mObserver;
/**
* ImageManager扮演的是Subject的角色,这个类的实例将被添加到ImagManager类
* 中的观察者列表中,当ImageList中的数据发生变化时(由ImageManager来检测)
* ImageManager将通告MyDataSetObserver实例发生的变化
*/
private class MyDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
//BaseAdapter维护了一个DataSetObservable对象mDataSetObservable
//这个函数用于通告mDataSetObservable的所有观察者数据发生变化
notifyDataSetChanged();
}
@Override
public void onInvalidated() {
//BaseAdapter维护了一个DataSetObservable对象mDataSetObservable
//这个函数用于通告mDataSetObservable的所有观察者数据变为无效
notifyDataSetInvalidated();
}
}
public ImageAdapter(Context c) {
mImageManager = ImageManager.getInstance(c);
mContext = c;
mObserver = new MyDataSetObserver();
mImageManager.addObserver(mObserver); //将mObserver设置为mImageManager的观察者
}
/**
* 返回显示的图片的数目
*
* @see android.widget.Adapter#getCount()
*/
public int getCount() {
return mImageManager.size();
}
/**
* 返回指定索引的图片
*
* @see android.widget.Adapter#getItem(int)
*/
public Object getItem(int position) {
return mImageManager.get(position);
}
/**
* 返回指定索引的图片ID
*
* @see android.widget.Adapter#getItemId(int)
*/
public long getItemId(int position) {
PanoramioItem s = mImageManager.get(position);
return s.getId();
}
/**
* 返回指定索引处用于显示图片的view
*
* @param position 索引
* @param convertView 可以重用的view,可能为null.
* @param parent 返回的view的父view.
* @return 用于显示指定索引处图片的view
*/
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
// 创建新的view
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.image_item, null);
} else {
// 使用已经存在的view
view = convertView;
}
PanoramioItem s = mImageManager.get(position);
ImageView i = (ImageView) view.findViewById(R.id.image);
i.setImageBitmap(s.getBitmap()); //将位图设置到view上
i.setBackgroundResource(R.drawable.picture_frame); //设置ImageView的背景图片
TextView t = (TextView) view.findViewById(R.id.title);
t.setText(s.getTitle()); //设置图片名称
t = (TextView) view.findViewById(R.id.owner);
t.setText(s.getOwner()); //设置图片作者
return view;
}
}
同理,ImageList.java文件内容如下所示:
package com.google.android.panoramio;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
/**
* 显示图片列表的Activity
*/
public class ImageList extends ListActivity {
ImageManager mImageManager;
private MyDataSetObserver mObserver = new MyDataSetObserver();
/**
* 保存用户在主界面选取搜索区域时所用的缩放级别
*/
private int mZoom;
/**
* 保存用户在主界面选取的搜索区域的中心点纬度
*/
private int mLatitudeE6;
/**
* 保存用户在主界面选取的搜索区域的中心点经度
*/
private int mLongitudeE6;
/**
* 注册为ImageManager实例的观察者,用于当ImageManager结束图片下载时将ImageList界面上的加载进度显示关闭
*/
private class MyDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
if (!mImageManager.isLoading()) {
getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_VISIBILITY_OFF);
}
}
@Override
public void onInvalidated() {
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);//显示不确定进度
super.onCreate(savedInstanceState);
mImageManager = ImageManager.getInstance(this);
//获取ListView,并在底部添加版权信息
ListView listView = getListView();
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View footer = inflater.inflate(R.layout.list_footer, listView, false);
listView.addFooterView(footer, null, false);
//将自定义的ImageAdapter设置给这个ListActivity
setListAdapter(new ImageAdapter(this));
//在AndroidManifest.xml文件中为我们的List设置了Theme.Light,这里将背景移除
listView.setBackgroundDrawable(null);
//当ImageManager还在下载资源时,显示进度条为忙,并注册观察者用于下载结束时隐藏进度条
if (mImageManager.isLoading()) {
getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_VISIBILITY_ON);
mImageManager.addObserver(mObserver);
}
//保存从Panoramio主界面传过来的搜索区域相关信息
Intent i = getIntent();
mZoom = i.getIntExtra(ImageManager.ZOOM_EXTRA, Integer.MIN_VALUE);
mLatitudeE6 = i.getIntExtra(ImageManager.LATITUDE_E6_EXTRA, Integer.MIN_VALUE);
mLongitudeE6 = i.getIntExtra(ImageManager.LONGITUDE_E6_EXTRA, Integer.MIN_VALUE);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
PanoramioItem item = mImageManager.get(position);
//创建Intent用于传递相关数据给ViewImage,用于显示单张图片信息
Intent i = new Intent(this, ViewImage.class);
i.putExtra(ImageManager.PANORAMIO_ITEM_EXTRA, item);
i.putExtra(ImageManager.ZOOM_EXTRA, mZoom);
i.putExtra(ImageManager.LATITUDE_E6_EXTRA, mLatitudeE6);
i.putExtra(ImageManager.LONGITUDE_E6_EXTRA, mLongitudeE6);
startActivity(i);
}
}