Android源码学习之适配器模式应用
主要内容:
一、适配器模式定义
适配器模式定义: |
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatile interfaces. |
将一个类的接口变成客户端所期待的另一中接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。 |
其中用到适配器模式的经典例子就是插座匹配问题,直接给图:
二、适配器模式分类
适配器模式主要分为两种:对象适配器和类适配器:
如上图所示(截取自《Head First Design Patterns》一书),主要包括三个部分:
1. Target目标角色。该角色定义把其他类转换为我们的期待接口。
2. Adaptee源角色。就是原始的类和接口对象,它是已经存在的,只是不符合现有的要求,而需要利用适配器角色的包装。
3. Adapter适配器角色。适配器模式的核心角色,其它两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单;把源角色转换为目标角色;通过继承或是组合的方式。
三、适配器模式优势
适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定它们。 |
增加了类的通透性,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心得。 |
调高了类的复用性和灵活性非常好。如果觉得适配器不够好,只要修改适配器就行,其它的代码都不用修改,适配器就是一个灵活的构件,想用就用。 |
四、适配器模式在Android源码中的应用
在Android源码中,ListView中用到的就是适配器模式。ListView用于显示列表数据,但列表数据形式多种多样(),为了处理和显示不同的数据,我们需要对应的适配器作为桥梁。
在ListView中有一个变量ListAdapter mAdapter;是显示在view试图上的数据:
/** * The adapter containing the data to be displayed by this view */ ListAdapter mAdapter;
在ListAdapter中定义了所需要的接口函数:
package android.widget; /** * Extended {@link Adapter} that is the bridge between a {@link ListView} * and the data that backs the list. Frequently that data comes from a Cursor, * but that is not * required. The ListView can display any data provided that it is wrapped in a * ListAdapter. */ public interface ListAdapter extends Adapter { /** * Indicates whether all the items in this adapter are enabled. If the * value returned by this method changes over time, there is no guarantee * it will take effect. If true, it means all items are selectable and * clickable (there is no separator.) * * @return True if all items are enabled, false otherwise. * * @see #isEnabled(int) */ public boolean areAllItemsEnabled(); /** * Returns true if the item at the specified position is not a separator. * (A separator is a non-selectable, non-clickable item). * * The result is unspecified if position is invalid. An {@link ArrayIndexOutOfBoundsException} * should be thrown in that case for fast failure. * * @param position Index of the item * * @return True if the item is not a separator * * @see #areAllItemsEnabled() */ boolean isEnabled(int position);
}
它是继承自Adapter:
package android.widget; import android.database.DataSetObserver; import android.view.View; import android.view.ViewGroup; /** * An Adapter object acts as a bridge between an {@link AdapterView} and the * underlying data for that view. The Adapter provides access to the data items. * The Adapter is also responsible for making a {@link android.view.View} for * each item in the data set. * * @see android.widget.ArrayAdapter * @see android.widget.CursorAdapter * @see android.widget.SimpleCursorAdapter */ public interface Adapter { /** * Register an observer that is called when changes happen to the data used by this adapter. * * @param observer the object that gets notified when the data set changes. */ void registerDataSetObserver(DataSetObserver observer); /** * Unregister an observer that has previously been registered with this * adapter via {@link #registerDataSetObserver}. * * @param observer the object to unregister. */ void unregisterDataSetObserver(DataSetObserver observer); /** * How many items are in the data set represented by this Adapter. * * @return Count of items. */ int getCount(); /** * Get the data item associated with the specified position in the data set. * * @param position Position of the item whose data we want within the adapter's * data set. * @return The data at the specified position. */ Object getItem(int position); /** * Get the row id associated with the specified position in the list. * * @param position The position of the item within the adapter's data set whose row id we want. * @return The id of the item at the specified position. */ long getItemId(int position); /** * Indicates whether the item ids are stable across changes to the * underlying data. * * @return True if the same id always refers to the same object. */ boolean hasStableIds(); /** * Get a View that displays the data at the specified position in the data set. You can either * create a View manually or inflate it from an XML layout file. When the View is inflated, the * parent View (GridView, ListView...) will apply default layout parameters unless you use * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} * to specify a root view and to prevent attachment to the root. * * @param position The position of the item within the adapter's data set of the item whose view * we want. * @param convertView The old view to reuse, if possible. Note: You should check that this view * is non-null and of an appropriate type before using. If it is not possible to convert * this view to display the correct data, this method can create a new view. * Heterogeneous lists can specify their number of view types, so that this View is * always of the right type (see {@link #getViewTypeCount()} and * {@link #getItemViewType(int)}). * @param parent The parent that this view will eventually be attached to * @return A View corresponding to the data at the specified position. */ View getView(int position, View convertView, ViewGroup parent); /** * An item view type that causes the {@link AdapterView} to ignore the item * view. For example, this can be used if the client does not want a * particular view to be given for conversion in * {@link #getView(int, View, ViewGroup)}. * * @see #getItemViewType(int) * @see #getViewTypeCount() */ static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; /** * Get the type of View that will be created by {@link #getView} for the specified item. * * @param position The position of the item within the adapter's data set whose view type we * want. * @return An integer representing the type of View. Two views should share the same type if one * can be converted to the other in {@link #getView}. Note: Integers must be in the * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can * also be returned. * @see #IGNORE_ITEM_VIEW_TYPE */ int getItemViewType(int position); /** * <p> * Returns the number of types of Views that will be created by * {@link #getView}. Each type represents a set of views that can be * converted in {@link #getView}. If the adapter always returns the same * type of View for all items, this method should return 1. * </p> * <p> * This method will only be called when when the adapter is set on the * the {@link AdapterView}. * </p> * * @return The number of types of Views that will be created by this adapter */ int getViewTypeCount(); static final int NO_SELECTION = Integer.MIN_VALUE; /** * @return true if this adapter doesn't contain any data. This is used to determine * whether the empty view should be displayed. A typical implementation will return * getCount() == 0 but since getCount() includes the headers and footers, specialized * adapters might want a different behavior. */ boolean isEmpty(); }
其中Adapter定义了getCount()、getItemViewType(int position)等接口函数。
此时的ListAdapter就是一个Target目标角色,而我们的ListView就是一个Client。因此为了适配和显示一些数据,如Cursor等,所以就需要相应的适配器CursorAdapter,代码如下:
public abstract class CursorAdapter extends BaseAdapter implements Filterable, CursorFilter.CursorFilterClient { 。。。 protected Cursor mCursor; protected ChangeObserver mChangeObserver; protected DataSetObserver mDataSetObserver; protected CursorFilter mCursorFilter; 。。。 /** * Returns the cursor. * @return the cursor. */ public Cursor getCursor() { return mCursor; } // 实现ListAdapter目标接口的getCount函数,通过返回源角色mCursor的方法getCount函数
/** * @see android.widget.ListAdapter#getCount() */ public int getCount() { if (mDataValid && mCursor != null) { return mCursor.getCount(); } else { return 0; } }
// 实现ListAdapter目标接口的getItem函数,通过返回源角色mCursor的方法moveToPosition函数 /** * @see android.widget.ListAdapter#getItem(int) */ public Object getItem(int position) { if (mDataValid && mCursor != null) { mCursor.moveToPosition(position); return mCursor; } else { return null; } } // 实现ListAdapter目标接口的getItemId函数,通过返回源角色mCursor的方法getLong函数
/** * @see android.widget.ListAdapter#getItemId(int) */ public long getItemId(int position) { if (mDataValid && mCursor != null) { if (mCursor.moveToPosition(position)) { return mCursor.getLong(mRowIDColumn); } else { return 0; } } else { return 0; } } @Override public boolean hasStableIds() { return true; }
其中源角色Cursor接口如下所示:
public interface Cursor { 。。。 /** * Returns the numbers of rows in the cursor. * * @return the number of rows in the cursor. */ int getCount(); /** * Returns the current position of the cursor in the row set. * The value is zero-based. When the row set is first returned the cursor * will be at positon -1, which is before the first row. After the * last row is returned another call to next() will leave the cursor past * the last entry, at a position of count(). * * @return the current cursor position. */ int getPosition(); 。。。 /** * Move the cursor to an absolute position. The valid * range of values is -1 <= position <= count. * * <p>This method will return true if the request destination was reachable, * otherwise, it returns false. * * @param position the zero-based position to move to. * @return whether the requested move fully succeeded. */ boolean moveToPosition(int position); 。。。 /** * Returns the value of the requested column as a long. * * <p>The result and whether this method throws an exception when the * column value is null, the column type is not an integral type, or the * integer value is outside the range [<code>Long.MIN_VALUE</code>, * <code>Long.MAX_VALUE</code>] is implementation-defined. * * @param columnIndex the zero-based index of the target column. * @return the value of that column as a long. */ long getLong(int columnIndex); 。。。 }
这就将Cursor类型接口通过CursorAdapter适配器转换成目标角色ListAdapter目标接口,继而让ListView使用,并展示。
本人能力和时间有限(缺少“模式使用”内容,以后会添加),写的很粗糙,恭候大家的批评指正,谢谢~~~