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:

View Code
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 &lt;= position &lt;= 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使用,並展示。

     本人能力和時間有限(缺少“模式使用”內容,以后會添加),寫的很粗糙,恭候大家的批評指正,謝謝~~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM