ListView桥接模式的应用

文章开头先贴下另外几篇设计模式的分析,感兴趣的可以看下:
设计模式-简单说明
ListView适配器模式的应用
ListView观察者模式的应用
AsyncTask模板模式的应用
这篇文章我们主要是分析一下Adapter和AdapterView中对桥接模式的应用,照理我们先简单的说明一下桥接模式的含义和使用场景。

含义:将抽象部分和实现部分分离(注意抽象部分并不一定就是抽象类,实现部分也不一定就是具体类),使他们都可以在自己的维度独立变化

场景:

  1. 如果觉得继承太繁琐,子类中含有太多平常基本用不到的API,可以使用桥接来代替继承,更加灵活
  2. 如果一个类存在多维度的独立变化,且各维度可以独立变化

在这里我们先贴一下桥接模式UML类图,简单梳理一下:

ListView桥接模式的应用_第1张图片

从图中我们可以发现AdapterView是一个ViewGroup,Adaper则会提供一个生成View的getView方法,通过桥接模式(AbsListView持有ListAdapter的引用)调用ListAdapter的getView方法将子View添加进父容器中。简单的梳理过后我们看下源码。先从Adapter接口的源码:

/**
 * 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.
 * 
 * Adapter的类声明,简单明了--> Adapter对象,充当AdapterView和数据视图之间的桥梁。提供了访问数据项的功能
 */
    public interface Adapter {

    static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
    static final int NO_SELECTION = Integer.MIN_VALUE;

    void registerDataSetObserver(DataSetObserver observer);
    void unregisterDataSetObserver(DataSetObserver observer);
    int getCount();   
    Object getItem(int position);
    long getItemId(int position);
    boolean hasStableIds();
    View getView(int position, View convertView, ViewGroup parent);
    int getItemViewType(int position);
    int getViewTypeCount();
    boolean isEmpty();

    default @Nullable CharSequence[] getAutofillOptions() {
        return null;
    }
}

这些都是很常见的方法了,涉及到观察者模式以及适配器模式的使用,有兴趣的可以点击查看另外两篇文章。根据我的理解,这里使用到桥接模式的原因是:

  1. 符合使用场景,Adapter有多种类型的子类(见下图),每个子类都有自己的实现方式,简单的理解就是在Adapter生产View这个维度来讲每个子类有自己独立的变化,例如通过cursor生成View、通过ListshengchengView、通过Array生成View等
  2. 符合单一职责原则,就是一个类的职责要保持单一,这样逻辑上来说更清晰,当我们需要使用的时候直接找相应的类即可,而不用到一个混杂了多个职责的类里面去寻找。
  3. 防止API的膨胀,增加使用成本

    ListView桥接模式的应用_第2张图片

那么AdapterView和Adapter是怎么关联的呢,我们接下来看下AdapterView。在AdapterView类的声明中,第一句就是AdapterView是一个由Adapter决定子View长啥样的ViewGroup,在AdapterView的类声明中持有一个继承自Adapter的泛型,然后提供了get/set方法,如下:

/**
  * Returns the adapter currently associated with this widget.
  */
 public abstract T getAdapter();

 /**
  * Sets the adapter that provides the data and the views to represent the data
  * in this widget.
  */
 public abstract void setAdapter(T adapter);

然后在它的子类,ListView的父类AbsListView中,实现了setAdapter,注意AbsListView已经指定了泛型为ListAdapter,看下AbsListView的实现:

@Override
public void setAdapter(ListAdapter adapter) {
    if (adapter != null) {
        mAdapterHasStableIds = mAdapter.hasStableIds();
        if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds &&
                mCheckedIdStates == null) {
            mCheckedIdStates = new LongSparseArray();
        }
    }

    if (mCheckStates != null) {
        mCheckStates.clear();
    }

    if (mCheckedIdStates != null) {
        mCheckedIdStates.clear();
    }
}

我们惊奇的发现这不科学,因为没有为成员变量mAdapter赋值,不要急,我们看下ListView的setAdapter:

 @Override
 public void setAdapter(ListAdapter adapter) {
     if (mAdapter != null && mDataSetObserver != null) {
         mAdapter.unregisterDataSetObserver(mDataSetObserver);
     }

     resetList();
     mRecycler.clear();

     if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
         mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
     } else {
         //为成员变量赋值
         mAdapter = adapter;
     }

     mOldSelectedPosition = INVALID_POSITION;
     mOldSelectedRowId = INVALID_ROW_ID;

     // AbsListView#setAdapter will update choice mode states.
     super.setAdapter(adapter);

     if (mAdapter != null) {
         mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
         mOldItemCount = mItemCount;
         mItemCount = mAdapter.getCount();
         checkFocus();

         mDataSetObserver = new AdapterDataSetObserver();
         mAdapter.registerDataSetObserver(mDataSetObserver);

         mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

         int position;
         if (mStackFromBottom) {
             position = lookForSelectablePosition(mItemCount - 1, false);
         } else {
             position = lookForSelectablePosition(0, true);
         }
         setSelectedPositionInt(position);
         setNextSelectedPositionInt(position);

         if (mItemCount == 0) {
             // Nothing selected
             checkSelectionChanged();
         }
     } else {
         mAreAllItemsSelectable = true;
         checkFocus();
         // Nothing selected
         checkSelectionChanged();
     }

     requestLayout();
 }

在这里主要是做了一些初始化操作,比如先注销原来的观察者,然后注册新的观察者;为成员变量mAdapter赋值;调用requestLayout重新布局子View等,感兴趣的可以看下文章前面另两篇文章的分析。好了,此时我们已经通过setAdapter成功的将Adapter实例保存起来,在需要的地方直接调用即可,比如在setAdapter里面就用到了mAdapter.getCount()方法。

ListView中,其实准确的说应该是Adapter和AdapterView中桥接模式的应用已经分析完毕,如有不足还请指出。

你可能感兴趣的:(设计模式)