读BaseAdapter的一点感悟_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > 读BaseAdapter的一点感悟

读BaseAdapter的一点感悟

 2015/3/24 21:39:59  supersheep6  程序员俱乐部  我要评论(0)
  • 摘要:适配器,作为android应用层的开发中,具有很重要的作用。在诸如ListView,gallery等sdk中提供的展示批量数据的控件中,起到一个适配数据源的作用。sdk中已经为我们提供了一个简单的并且适用性很广的适配器SimpleAdapter,该类就是继承自抽象类BaseAdapter实现的一个具体的适配器,并且能接收List<?extendsMap<String,?>>形式的数据源格式的数据。但是很多情况下,我们使用ListView等容器的时候
  • 标签:ASE

  适配器,作为android应用层的开发中,具有很重要的作用。在诸如ListView,gallery等sdk中提供的展示批量数据的控件中,起到一个适配数据源的作用。sdk中已经为我们提供了一个简单的并且适用性很广的适配器SimpleAdapter,该类就是继承自抽象类BaseAdapter实现的一个具体的适配器,并且能接收 List<? extends Map<String, ?>>形式的数据源格式的数据。但是很多情况下,我们使用ListView等容器的时候,往往要在渲染每一条数据的时候做一些我们自己想做的事情,此时就要实现BaseAdapter来实现自己的一个Adapter。


 

  在进入正文之前,先说一下关于抽象类与接口的区别,以下摘自百度知道

  你选择使用接口和抽象类的依据是什么? 接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。第一点. 接口是抽象类的变体,接口中所有的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类。 第二点. 接口可以继承,抽象类不行 第三点. 接口定义方法,不能实现,而抽象类可以实现部分方法。 第四点. 接口中基本数据类型为static 而抽类象不是的。当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。 接口可以实现也可以继承,抽象类不行 抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度的。

  


 

  上面说的很清楚了,接口是对动作的抽象,而抽象类是对根源的抽象。抽象类表示的是这个对象是什么,而接口表示的是这个对象能做什么。同时在我阅读BaseAdapter源码的时候也有些感悟,BaseAdapter是一个抽象类,它实现了ListAdapter和SpinnerAdapter接口,而这两个接口又实现了Adapter接口。我们在构造自己的Adapter的时候经常要必须实现的getCount()和getView()方法都是在Adapter接口中定义的。

  在大牛们写的源码中,往往都是接口与抽象类同时使用。有关于BaseAdapter中的源码,则把最基本的一些功能(能做什么)抽象为一个接口,放在最底层,而后通过继承这个接口再抽象一些子接口,当有抽象类实现了这些子接口的时候,可以选择性的(这是抽象类允许的)实现接口中定义的功能,作为一个具有统一功能的更具体的但是同样以抽象的形式而存在,最后我们要构造这种经过抽象的类型的具体类的时候,则需要将抽象类没有实现的功能全部实现(这是必须的)甚至可以重写抽象类中已经实现的某些方法(一般情况下不必这样做),这时我们可以通过实例化我们自己构造的具体类来完成一些工作了。

  这种层级关系让我体验到了从无到有的过程,也深刻的理解了什么叫抽象到具体的过程,在这种模型的基础上,我们可以从容的扩展和具象化。不必担心功能的丢失或者偏离设计此类型的初衷,由简化繁需要强大的根基,抽象类和接口的结合就是面向对象变成最强大的根基。

 

 

  下面贴出源码来具体看

  既然上面说到了由简化繁,那么就从最基本的接口开始

  1、Adapter

  1 package android.widget;
  2 
  3 import android.database.DataSetObserver;
  4 import android.view.View;
  5 import android.view.ViewGroup;
  6 
  7 /**
  8  * 。。。。。。
  9  */
 10 public interface Adapter {
 11     /**
 12      * Register an observer that is called when changes happen to the data used by this adapter.
 13      *
 14      * @param observer the object that gets notified when the data set changes.
 15      */
 16     void registerDataSetObserver(DataSetObserver observer);
 17 
 18     /**
 19      * Unregister an observer that has previously been registered with this
 20      * adapter via {@link #registerDataSetObserver}.
 21      *
 22      * @param observer the object to unregister.
 23      */
 24     void unregisterDataSetObserver(DataSetObserver observer);
 25 
 26     /**
 27      * How many items are in the data set represented by this Adapter.
 28      * 
 29      * @return Count of items.
 30      */
 31     int getCount();   
 32     
 33     /**
 34      * Get the data item associated with the specified position in the data set.
 35      * 
 36      * @param position Position of the item whose data we want within the adapter's 
 37      * data set.
 38      * @return The data at the specified position.
 39      */
 40     Object getItem(int position);
 41     
 42     /**
 43      * Get the row id associated with the specified position in the list.
 44      * 
 45      * @param position The position of the item within the adapter's data set whose row id we want.
 46      * @return The id of the item at the specified position.
 47      */
 48     long getItemId(int position);
 49     
 50     /**
 51      * Indicates whether the item ids are stable across changes to the
 52      * underlying data.
 53      * 
 54      * @return True if the same id always refers to the same object.
 55      */
 56     boolean hasStableIds();
 57     
 58     /**
 59      * 。。。。。。
 60      */
 61     View getView(int position, View convertView, ViewGroup parent);
 62 
 63     /**
 64      * An item view type that causes the {@link AdapterView} to ignore the item
 65      * view. For example, this can be used if the client does not want a
 66      * particular view to be given for conversion in
 67      * {@link #getView(int, View, ViewGroup)}.
 68      * 
 69      * @see #getItemViewType(int)
 70      * @see #getViewTypeCount()
 71      */
 72     static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
 73     
 74     /**
 75      * Get the type of View that will be created by {@link #getView} for the specified item.
 76      * 
 77      * @param position The position of the item within the adapter's data set whose view type we
 78      *        want.
 79      * @return An integer representing the type of View. Two views should share the same type if one
 80      *         can be converted to the other in {@link #getView}. Note: Integers must be in the
 81      *         range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
 82      *         also be returned.
 83      * @see #IGNORE_ITEM_VIEW_TYPE
 84      */
 85     int getItemViewType(int position);
 86     
 87     /**
 88      * 。。。。。。
 89      */
 90     int getViewTypeCount();
 91     
 92     static final int NO_SELECTION = Integer.MIN_VALUE;
 93  
 94      /**
 95       * @return true if this adapter doesn't contain any data.  This is used to determine
 96       * whether the empty view should be displayed.  A typical implementation will return
 97       * getCount() == 0 but since getCount() includes the headers and footers, specialized
 98       * adapters might want a different behavior.
 99       */
100      boolean isEmpty();
101 }

 

  然后是继承自这个接口的两个接口

  2、ListAdapter、SpinnerAdapter

  (由于内容非常少,则粘在一起)

  

 1 package android.widget;
 2 
 3 /**
 4  * 。。。。。。
 5  */
 6 public interface ListAdapter extends Adapter {
 7 
 8     /**
 9      * 。。。。。。
10      */
11     public boolean areAllItemsEnabled();
12 
13     /**
14      * 。。。。。。
15      */
16     boolean isEnabled(int position);
17 }
18 
19 
20 
21 
22 package android.widget;
23 
24 import android.view.View;
25 import android.view.ViewGroup;
26 
27 /**
28  * Extended {@link Adapter} that is the bridge between a
29  * {@link android.widget.Spinner} and its data. A spinner adapter allows to
30  * define two different views: one that shows the data in the spinner itself and
31  * one that shows the data in the drop down list when the spinner is pressed.</p>
32  */
33 public interface SpinnerAdapter extends Adapter {
34     /**
35      * 。。。。。。
36      */
37     public View getDropDownView(int position, View convertView, ViewGroup parent);
38 }

 

 

 

  这里BaseAdapter用到的是观察者模式,在ListView的setAdapter里注册一个AdapterDataSetObserver观察者,相当于在使用按钮时候,对按钮set一个OnClickListener。这里的AdapterDataSetObserver是一个内部类,这个类继承自DataSetObserver这个抽象类,并且重写了onChanged()和onInvalidated()方法,而OnClickListener则是一个接口的子类,实现了这个接口的匿名类或者是实体类,重写了OnClick()方法。

  3、BaseAdapter

  

 1 public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
 2     // 这里实例化一个观察者操作类
 3     private final DataSetObservable mDataSetObservable = new DataSetObservable();
 4   
 5     public boolean hasStableIds() {
 6         return false;
 7     }
 8       
 9     // 这里DataSetObserver是一个观察者的抽象类型  通过参数传入具体的观察者
10     public void registerDataSetObserver(DataSetObserver observer) {
11         mDataSetObservable.registerObserver(observer);
12     }
13   
14     public void unregisterDataSetObserver(DataSetObserver observer) {
15         mDataSetObservable.unregisterObserver(observer);
16     }
17       
18     /**
19      * Notifies the attached observers that the underlying data has been changed
20      * and any View reflecting the data set should refresh itself.
21      */
22     public void notifyDataSetChanged() {
23         // 通过观察者操作类回调注册的观察者中用户编写的OnChanged()方法
24         mDataSetObservable.notifyChanged();
25     }
26   
27     /**
28      * Notifies the attached observers that the underlying data is no longer valid
29      * or available. Once invoked this adapter is no longer valid and should
30      * not report further data set changes.
31      */
32     public void notifyDataSetInvalidated() {
33         mDataSetObservable.notifyInvalidated();
34     }
35   
36     public boolean areAllItemsEnabled() {
37         return true;
38     }
39   
40     public boolean isEnabled(int position) {
41         return true;
42     }
43   
44     public View getDropDownView(int position, View convertView, ViewGroup parent) {
45         return getView(position, convertView, parent);
46     }
47   
48     public int getItemViewType(int position) {
49         return 0;
50     }
51   
52     public int getViewTypeCount() {
53         return 1;
54     }
55       
56     public boolean isEmpty() {
57         return getCount() == 0;
58     }
59 }

 

  4、观察者抽象类DataSetObserver

  

 1 /*
 2  * Copyright (C) 2007 The Android Open Source Project
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.database;
18 
19 /**
20  * Receives call backs when a data set has been changed, or made invalid. The typically data sets
21  * that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
22  * DataSetObserver must be implemented by objects which are added to a DataSetObservable.
23  */
24 public abstract class DataSetObserver {
25     /**
26      * This method is called when the entire data set has changed,
27      * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
28      */
29     public void onChanged() {
30         // Do nothing
31     }
32 
33     /**
34      * This method is called when the entire data becomes invalid,
35      * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
36      * {@link Cursor}.
37      */
38     public void onInvalidated() {
39         // Do nothing
40     }
41 }

  5、实现了功能的具体观察者AdapterDataSetObserver----实则是抽象类AdapterView的一个内部类,而ListView类继承自抽象类AbsListView,AbsListView继承自抽象类AdapterView,其中这两个抽象类的其他实现我们不关注,只关注有关于ListView的地方。

  

 1 class AdapterDataSetObserver extends DataSetObserver {
 2 
 3         private Parcelable mInstanceState = null;
 4 
 5         @Override
 6         public void onChanged() {
 7             mDataChanged = true;
 8             mOldItemCount = mItemCount;
 9             mItemCount = getAdapter().getCount();
10 
11             // Detect the case where a cursor that was previously invalidated has
12             // been repopulated with new data.
13             if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
14                     && mOldItemCount == 0 && mItemCount > 0) {
15                 AdapterView.this.onRestoreInstanceState(mInstanceState);
16                 mInstanceState = null;
17             } else {
18                 rememberSyncState();
19             }
20             checkFocus();
21             requestLayout();
22         }
23 
24         @Override
25         public void onInvalidated() {
26             mDataChanged = true;
27 
28             if (AdapterView.this.getAdapter().hasStableIds()) {
29                 // Remember the current state for the case where our hosting activity is being
30                 // stopped and later restarted
31                 mInstanceState = AdapterView.this.onSaveInstanceState();
32             }
33 
34             // Data is invalid so we should reset our state
35             mOldItemCount = mItemCount;
36             mItemCount = 0;
37             mSelectedPosition = INVALID_POSITION;
38             mSelectedRowId = INVALID_ROW_ID;
39             mNextSelectedPosition = INVALID_POSITION;
40             mNextSelectedRowId = INVALID_ROW_ID;
41             mNeedSync = false;
42 
43             checkFocus();
44             requestLayout();
45         }
46 
47         public void clearSavedState() {
48             mInstanceState = null;
49         }
50     }

  6、观察者操作类的抽象类Observable<T>

  为了方便阅读,删去了注释,只看方法名比较容易理解,这里主要实现的是观察者的注册,实则是在一个ArrayList中不断的添加观察者DataSetObserver实体

package android.database;

import java.util.ArrayList;

public abstract class Observable<T> {
    protected final ArrayList<T> mObservers = new ArrayList<T>();

    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);
        }
    }

    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);
        }
    }

    public void unregisterAll() {
        synchronized(mObservers) {
            mObservers.clear();
        }
    }
}

  7、观察者操作类的具体实现DataSetObservable

  可以看到,这里定义了notifyChanged方法的实现,通过同步代码块逐条的对集合中的每一条数据进行OnChanged方法调用。

 1 /*
 2  * Copyright (C) 2007 The Android Open Source Project
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.database;
18 
19 /**
20  * A specialization of {@link Observable} for {@link DataSetObserver}
21  * that provides methods for sending notifications to a list of
22  * {@link DataSetObserver} objects.
23  */
24 public class DataSetObservable extends Observable<DataSetObserver> {
25     /**
26      * Invokes {@link DataSetObserver#onChanged} on each observer.
27      * Called when the contents of the data set have changed.  The recipient
28      * will obtain the new contents the next time it queries the data set.
29      */
30     public void notifyChanged() {
31         synchronized(mObservers) {
32             // since onChanged() is implemented by the app, it could do anything, including
33             // removing itself from {@link mObservers} - and that could cause problems if
34             // an iterator is used on the ArrayList {@link mObservers}.
35             // to avoid such problems, just march thru the list in the reverse order.
36             for (int i = mObservers.size() - 1; i >= 0; i--) {
37                 mObservers.get(i).onChanged();
38             }
39         }
40     }
41 
42     /**
43      * Invokes {@link DataSetObserver#onInvalidated} on each observer.
44      * Called when the data set is no longer valid and cannot be queried again,
45      * such as when the data set has been closed.
46      */
47     public void notifyInvalidated() {
48         synchronized (mObservers) {
49             for (int i = mObservers.size() - 1; i >= 0; i--) {
50                 mObservers.get(i).onInvalidated();
51             }
52         }
53     }
54 }

  8、最后就是ListView中setAdapter方法的实现了,小小的setAdapter方法中涉及到了BaseAdapter的引用,DataSetObserver的引用。

  这里实际上是通过入参传入了我们自己实现的Adapter,一个继承自BaseAdapter的Adapter,并且在方法中已经实例化了一个上文提到的AdapterDataSetObserver,一个继承自DataSetObserver的具体观察者。

  

@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 = new HeaderViewListAdapter(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();//AdapterDataSetObserver是ListView的基类AdapterView的内部类
            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();//更新视图
    }

 

上一篇: Swift 闭包表达式 下一篇: 没有下一篇了!
发表评论
用户名: 匿名