本文继续讲解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); } }