美文网首页安卓开发androidandroid
ListView详解--绘图、优化、适配器、观察者

ListView详解--绘图、优化、适配器、观察者

作者: cxm11 | 来源:发表于2016-04-18 22:38 被阅读2284次

    Android源码之ListView的适配器模式

    Adapter Pattern
    适配器模式分为两种,即类适配器,对象适配器模式。

    类适配器是通过实现Target接口以及继承Adaptee类来实现接口转换;而对象适配器模式是通过实现Target接口和代理Adaptee的某个方法来实现(即在类内部定义Adaptee的变量)。

    角色介绍:
    目标(Target)角色:这就是所期待得到的接口。
    源(Adaptee)角色:需要适配的接口。
    适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。


    ListView中的Adapter模式

    在开发过程中,ListView的Adapter是我们最为常见的类型之一。一般的用法大致如下:

    ListView myListView = (ListView)findViewById(listview_id);
    //设置适配器
    myListView.setAdapter(new MyAdapter(context, myDatas));
    //适配器
    public class MyAdapter extends BaseAdapter{
    
    private LayoutInflater mInflater;
    List<string> mDatas;
    
    public MyAdatper(Context context, List<string> datas){
    this.mInflater = LayoutInflater.from(context);
    mDatas = datas;
    }
    @Override
    public int getCount() {
    return mDatas.size();
    }
    @Override
    public String getItem(int pos){
    return mDatas.get(pos);
    }
    @Override
    public long getItemId(int pos){
    return pos;
    }
    
    //解析、设置、缓存convertView以及相关内容
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    //Item View的复用
    if(converView == null) {
    convertView = mInflater.inflate(R.layout.my_listview_item, null);
    //获取title
    holder.title = (TextView)convertView.findViewById(R.id.title);
    convertView.setTag(holder);
    }else {
    holder = (ViewHolder)convertView.getTag();
    }
    holder.title.setText(mDatas.get(position));
    return convertView;
    }
    } 
    

    通过代理数据集来告知ListView数据的个数(getCount函数)以及每个数据的类型(getItem函数),最重要的是要解决Item View的输出。Item View千变万化,但终究它都是View类型,Adapter统一将Item View输出为View(getView函数),这样就很好的应对了Item View的可变性。

    那么ListView是如何通过Adapter模式(不止Adapter模式)来运作的呢?ListView继承自AbsListView,Adapter定义在AbsListView中,我们看一下这个类

    public abstract class AbsListView extends AdapterView<listadapter> implements TextWatcher, ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,ViewTreeOberserver.OnTouchModeChangeListener,RemoteViewAdapter.RemoteAdapterConnectionCallback{
    
    /** 
    * The adapter containing the data to be displayed by this view 
    */    
    ListAdapter mAdapter;
    
    //关联到Window时调用的函数
    @Override
    protected void onAttachedToWindow() {
    //代码省略
    //给适配器注册一个观察者,该模式下一篇介绍。
    if(mAdapter != null && mDataSetObserver == null ){
    mDataSetObserver = new AdapterDataSetObserver();            mAdapter.registerDataSetObserver(mDataSetObserver);
    
            // Data may have changed while we were detached. Refresh.
            mDataChanged = true;
            mOldItemCount = mItemCount
            // 获取Item的数量,调用的是mAdapter的getCount方法
            mItemCount = mAdapter.getCount();
    }
    mIsAttached = true;
    }
      /**
     * 子类需要覆写layoutChildren()函数来布局child view,也就是Item View
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mInLayout = true;
        if (changed) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                getChildAt(i).forceLayout();
            }
            mRecycler.markChildrenDirty();
        }
    
        if (mFastScroller != null && mItemCount != mOldItemCount) {
            mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);
        }
        // 布局Child View
        layoutChildren();
        mInLayout = false;
    
        mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
    }
    
    // 获取一个Item View
    View obtainView(int position, boolean[] isScrap) {
        isScrap[0] = false;
        View scrapView;
        // 从缓存的Item View中获取,ListView的复用机制就在这里
        scrapView = mRecycler.getScrapView(position);
    
        View child;
        if (scrapView != null) {
            // 代码省略
            child = mAdapter.getView(position, scrapView, this);
            // 代码省略
        } else {
            child = mAdapter.getView(position, null, this);
            // 代码省略
        }
    
        return child;
    }
    

    AbsListView定义了集合视图的框架,比如Adapter模式的应用、复用Item View的逻辑、布局Item View的逻辑等。子类只需要覆写特定的方法即可实现集合视图的功能,例如ListView。

    ListView中的相关方法。

    @Override
    protected void layoutChildren() {
        // 代码省略
    
        try {
            super.layoutChildren();
            invalidate();
            // 代码省略
            // 根据布局模式来布局Item View
            switch (mLayoutMode) {
            case LAYOUT_SET_SELECTION:
                if (newSel != null) {
                    sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);
                } else {
                    sel = fillFromMiddle(childrenTop, childrenBottom);
                }
                break;
            case LAYOUT_SYNC:
                sel = fillSpecific(mSyncPosition, mSpecificTop);
                break;
            case LAYOUT_FORCE_BOTTOM:
                sel = fillUp(mItemCount - 1, childrenBottom);
                adjustViewsUpOrDown();
                break;
            case LAYOUT_FORCE_TOP:
                mFirstPosition = 0;
                sel = fillFromTop(childrenTop);
                adjustViewsUpOrDown();
                break;
            case LAYOUT_SPECIFIC:
                sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);
                break;
            case LAYOUT_MOVE_SELECTION:
                sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);
                break;
            default:
                // 代码省略
                break;
            }
    
    }
    
    // 从上到下填充Item View  [ 只是其中一种填充方式 ]
    private View fillDown(int pos, int nextTop) {
        View selectedView = null;
    
        int end = (mBottom - mTop);
        if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
            end -= mListPadding.bottom;
        }
    
        while (nextTop < end && pos < mItemCount) {
            // is this the selected item?
            boolean selected = pos == mSelectedPosition;
            View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);
    
            nextTop = child.getBottom() + mDividerHeight;
            if (selected) {
                selectedView = child;
            }
            pos++;
        }
    
        return selectedView;
    }
    
    // 添加Item View
    private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
            boolean selected) {
        View child;
    
        // 代码省略 
        // Make a new view for this position, or convert an unused view if possible
        child = obtainView(position, mIsScrap);
    
        // This needs to be positioned and measured
        setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
    
        return child;
    }
    

    ListView覆写了AbsListView中的layoutChilden函数,在该函数中根据布局模式来布局Item View。Item View的个数、样式都通过Adapter对应的方法来获取,获取个数、Item View之后,将这些Item View布局到ListView对应的坐标上,再加上Item View的复用机制,整个ListView就基本运转起来了。

    当然这里的Adapter并不是经典的适配器模式,但是却是对象适配器模式的优秀示例,也很好的体现了面向对象的一些基本原则。这里的Target角色和Adapter角色融合在一起,Adapter中的方法就是目标方法;而Adaptee角色就是ListView的数据集与Item View,Adapter代理数据集,从而获取到数据集的个数、元素。

    通过增加Adapter一层来将Item View的操作抽象起来,ListView等集合视图通过Adapter对象获得Item的个数、数据元素、Item View等,从而达到适配各种数据、各种Item视图的效果。因为Item View和数据类型千变万化,Android的架构师们将这些变化的部分交给用户来处理,通过getCount、getItem、getView等几个方法抽象出来,也就是将Item View的构造过程交给用户来处理,灵活地运用了适配器模式,达到了无限适配、拥抱变化的目的。


    Android设计模式系列(9)--SDK源码之适配器模式

    Adapter.jpg

    适配器原理解析

    为了简明直接,省略了相关的其他适配器,只以此两个适配器为例。

    ListView中有一个变量ListAdapter mAdapter(在AbsListView中定义);是显示在view视图上的数据;

    /** 
    * The adapter containing the data to be displayed by this view 
    */ 
    ListAdapter mAdapter;
    

    ListView作为Client,他所需要的目标接口(target interface)就是ListAdapter,包含getCount(),getItem(),getView()等几个基本方法,为了兼容List<T>,Cursor等数据源,我们专门定义两个适配器来适配它们:ArrayAdapter和CursorAdapter。这两个适配器,说白了就是针对目标接口对数据源进行兼容修饰。这就是适配器模式。
    其中BaseAdapter实现了isEmpty()方法,使子类在继承BaseAdapter后不需要再实现此方法,这就是缺省适配器,这也是缺省适配器的一个最明显的好处。


    ListView适配器Adapter介绍与优化
    Adapter继承结构关系

    Adapter2.JPG

    Android学习四、Android中的Adapter
    在实际应用中,adapter的继承体系应用广泛,所以,要对Adapter的方法有所了解:

    public interface Adapter {  
    // 为了避免产生大量的View浪费内存,在Android中,AdapterView中的View是可回收的使用的。比如你有100项数据要显示,而你的屏幕一次只能显示10条数据,则   
    // 只产生10个View,当往下拖动要显示第11个View时,会把第1个View的引用传递过去,更新里面的数据再显示,也就是说View可重用,只是更新视图中的数据用于显示新 
    // 的一项,如果一个视图的视图类型是IGNORE_ITEM_VIEW_TYPE的话,则此视图不会被重用    
    static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; 
    static final int NO_SELECTION = Integer.MIN_VALUE;
    
    // 注册一个Observer,当Adapter所表示的数据改变时会通知它,DataSetObserver是一个抽象类,定义了两个方法:onChanged与onInvalidated 
    void registerDataSetObserver(DataSetObserver observer); 
    
    // 取消注册一个Observer   
    void unregisterDataSetObserver(DataSetObserver observer);   
    
    // 所表示的数据的项数    
    int getCount(); 
    // 返回指定位置的数据项   
    Object getItem(int position);   
    // 返回指定位置的数据项的ID    
    long getItemId(int position);   
    // 表示所有数据项的ID是否是稳定的,在BaseAdapter中默认返回了false,假设是不稳定的,在CursorAdapter中返回了true,Cursor中的_ID是不变的  
    boolean hasStableIds(); 
    // 为每一个数据项产生相应的视图   
    View getView(int position, View convertView, ViewGroup parent); 
    // 获得相应位置的这图类型  
    int getItemViewType(int position);  
    // getView可以返回的View的类型数量。(在HeaderViewListAdapter中可以包含Header和Footer,getView可以返回Header、Footer及Adapter 
    // 中的视图,但其getViewTypeCount的实现只是调用了内部Adapter的getViewTypeCount,忽略了Header、Footer中的View 
    // Type,不懂。 
    int getViewTypeCount(); 
    //是否为空  
    boolean isEmpty();
    }
    

    ListAdapter中定义了所需要的接口函数:

    public interface ListAdapter extends Adapter {
    public boolean areAllItemsEnabled();
    boolean isEnabled(int position);
    }
    

    抽象类BaseAdapter,我省略其他代码,只列出两个方法,以作示意:

    public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    //...
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
    return getView(position, convertView, parent);
    }
    public boolean isEmpty() {
    return getCount()==0;
    }
    }
    

    ArrayAdapter对List<T>进行封装成ListAdapter的实现,满足ListView的调用:

    public class ArrayAdapter<T> extends BaseAdapter implements Filterable {
    private List<T> mObjects;
    //我只列出这一个构造函数,大家懂这个意思就行
    public ArrayAdapter(Context context, int textViewResourceId, T[] objects){
    init(context, textViewResourceId, 0,     Arrays.asList(objects));
    }
    private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
    mContext = context;
    mInflater = (LayoutInFlater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mResource = mDropDownResource = resource;
    mObjects = objects; //引用对象,也是表达了组合优于继承的意思
    mFieldId = textViewResourceId;
    }
    public int getCount() {
    return mObjects.size();
    }
    public T getItem(int position){
    return mObjects.get(position);
    }
    public View getView(int position, View convertView,     ViewGroup parent) {
    return createViewFromResource(position,convertView,parent,mResource);
    }
    //...
    }
    

    我们就如此成功的把List<T>作为数据源以ListView想要的目标接口的样子传给了ListView,同理CursorAdapter也是一模一样的道理,就不写具体代码了。


    BaseAdapter

    类BaseAdapter(省略了部分继承和要实现的方法)

    BaseAdapter中增加了两个方法,即通知数据改变和通知数据无效。定义了一个变量,通过改变量实现了注册、取消注册、通知等。

    public abstract class BaseAdapter 
    extends Object
    implements ListAdapter, SpinnerAdapter{
    
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    //...省略不必要的代码
    public BaseAdapter();
    public void registerDataSetObserver(DataSetObserver observer){
    mDataSetObservable.registerObserver(observer);
    }
    public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
    }
    public void notifyDataChanged(){
    mDataSetObservable.notifychanged();
    }
    //Notifies the attached View that the underlying data has been changed and it should refresh itself
    public void notifyDataSetInvalidated(){
    mDataSetObservable.notifyInvalidated();
    }
    //...省略不必要的代码
    }
    

    BaseAdapter是一个抽象类,实现了ListAdapter和SpinnerAdapter两个接口,这两个接口都继承自Adapter接口。在这个接口中申明了我们需要实现的四个重要方法。

    接下来,我们进入ListView中查看setAdapter方法,ListView就是通过调用这个方法与适配器联系起来的。该方法的传入参数为ListAdapter类型,ListAdapter同样继承了adapter,也就是说,我们只重写adapter中的一些回调方法才会起效。该方法中,首先会调用它的父类ABSListView中的requestLayout方法,该方法调用后会回调其中的onLayout方法,该方法会调用ListView中的layoutChildren方法,该方法会调用fillSpecific方法,fillSpecific方法通过调用makeAndAddView方法得到需要的view,然后将view放入list。查看makeAndAddView方法,它会调用父类的obtainView方法,而该方法会调用适配器中重写的getView方法。

    AbsListView中定义了变量 ListAdapter mAdapter;


    ![ListView和Adapter之间的关系.JPG]](http:https://img.haomeiwen.com/i1563413/dc5bcf46fbbfcb65.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    其实说到底,控件就是为了交互和展示数据用的,只不过ListView更加特殊,它是为了展示很多很多数据用的,但是ListView只承担交互和展示工作而已,至于这些数据来自哪里,ListView并不关心。

    ListView显示出来需要3个东西

    1. ListView(用来显示数据的列表)
    2. Data(需要显示的数据)
    3. 一个绑定data和ListView的适配器ListAdapter

    ListView的每一项其实都是TextView。
    通过setAdapter方法来调用一个ListAdapter来绑定数据

    ListView.JPG

    Android ViewGroup系列控件的使用

    public abstract class AdapterView<T extends Adapter>extends ViewGroup{
    //AdapterView继承ViewGroup,但AdapterView的child view由Adapter决定,不能通过addView()来添加。 
    //setAdapter()来设置Adapter,getAdapter()获取。
    
    //部分方法
    abstract T getAdapter() 
    abstract void setAdapter(T adapter) 
    }
    

    Adapter的getView方法详解
    getView详解 Recycler机制

    ListView_Recycler.JPG

    getView的API:

    public abstract View getView(int position, View convertView, ViewGroup parent)
    

    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 [inflate(int, android.view.ViewGroup, boolean)](http://localhost:8080/android/docs/reference/android/view/LayoutInflater.html#inflate(int, android.view.ViewGroup, boolean))
    to specify a root view and to prevent attachment to the root.

    Parameters

    • position
      The position of the item within the adapter's data set of the item whose view we want.
    • 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 getViewTypeCount()
      and getItemViewType(int)
      ).
    • parent
      The parent that this view will eventually be attached to

    Returns
    A View corresponding to the data at the specified position.

    position是指当前dataset的位置,通过getCount和getItem来使用。如果list向下滑动的话那么就是最低端的item的位置,如果是向上滑动的话那就是最上端的item的位置。convert是指可以重用的视图,即刚刚出队的视图。parent应该就是list。


    Android ListView工作原理解析

    RecycleBin机制

    这个机制是ListView能够实现成百上千条数据都不会OOM最重要的原因。RecycleBin是写在ABSListView中的一个内部类。所以所有继承自AbsListView的子类,也就是ListView和GridView,都可以使用这个机制。

    方法简介:

    • fillActiveViews() 这个方法接收两个参数,第一个参数表示要存储的view的数量,第二个参数表示ListView中第一个可见元素的position值。RecycleBin当中使用mActiveViews这个数组来存储View,调用这个方法后就会根据传入的参数来将ListView中的指定元素存储到mActiveViews数组当中。
    • getActiveView() 这个方法和fillActiveViews()是对应的,用于从mActiveViews数组当中获取数据。该方法接收一个position参数,表示元素在ListView当中的位置,方法内部会自动将position值转换成mActiveViews数组对应的下标值。需要注意的是,mActiveViews当中所存储的View,一旦被获取了之后就会从mActiveViews当中移除,下次获取同样位置的View将会返回null,也就是说mActiveViews不能被重复利用。
    • addScrapView() 用于将一个废弃的View进行缓存,该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕),就应该调用这个方法来对View进行缓存,RecycleBin当中使用mScrapViews和mCurrentScrap这两个List来存储废弃View。
    • getScrapView 用于从废弃缓存中取出一个View,这些废弃缓存中的View是没有顺序可言的,因此getScrapView()方法中的算法也非常简单,就是直接从mCurrentScrap当中获取尾部的一个scrap view进行返回。
    • setViewTypeCount() 我们都知道Adapter当中可以重写一个getViewTypeCount()来表示ListView中有几种类型的数据项,而setViewTypeCount()方法的作用就是为每种类型的数据项都单独启用一个RecycleBin缓存机制。实际上,getViewTypeCount()方法通常情况下使用的并不是很多,所以我们只要知道RecycleBin当中有这样一个功能就行了
    第一次Layout

    不管怎么说,ListView是继承自View的,因此它的执行流程还将会按照View的规则来执行。可参考Android视图绘制流程完全解析,带你一步步深入了解View(二)

    View的执行流程无非分为三步,onMeasure()用于测量View的大小,onLayout()用于确定View的布局,onDraw()用于将View绘制到界面上。ListView当中,onMeasure()并没有什么特殊的地方,因为它终归是一个View。onDraw()在ListView当中也没有什么意义,因为ListView本身也不负责绘制,而是由ListView当中的子元素来进行绘制的。那么ListView大部分的神奇功能其实都是在onLayout()方法中进行的了。

    onLayout()方法在AbsListView中覆写

    /**
     * Subclasses should NOT override this method but {@link #layoutChildren()}
     * instead.
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    mInLayout = true;
    if (changed) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).forceLayout();
        }
        mRecycler.markChildrenDirty();
    }
    layoutChildren();
    mInLayout = false;
    }
    

    可以看到,onLayout()方法中并没有做什么复杂的逻辑操作,主要就是一个判断,如果ListView的大小或者位置发生了变化,那么changed变量就会变成true,此时会要求所有的子布局都强制进行重绘。除此之外倒没有什么难理解的地方了,不过我们注意到,在第16行调用了layoutChildren()这个方法,从方法名上我们就可以猜出这个方法是用来进行子元素布局的,不过进入到这个方法当中你会发现这是个空方法,没有一行代码。这当然是可以理解的了,因为子元素的布局应该是由具体的实现类来负责完成的,而不是由父类完成。那么进入ListView的layoutChildren()方法

    然后调用fillFromTop()方法

    然后调用fillDown()方法

    调用makeAndAddView()方法

    调用obtainView()
    在obtainView中调用了mAdapter.getView(position,null,this);

    mAdapter就是当前ListView关联的适配器
    mAdapter是ListAdapter类型,在AbsListView中定义。
    在setAdapter函数中,传入我们定义的适配器adapter。有adapter构造mAdapter.从而在调用mAdapter.getView函数时调用是我们覆写的函数。从而完成了适配器模式

    在setAdapter函数的最后调用了 requestLayout();请求重新布局,重新调用:onMeasure,onLayout,onDraw;从而开始了ListView的绘制过程。

    Android中View的生命周期,调用invalidate()和requestLayout()会触发哪些方法,一图道破天机。

    requestLayout.JPG
    第二次Layout
    滑动加载更多数据

    ListView初始化简单分析

    ListView.onLayout过程与普通视图的layout过程完全不同,下面是一个流程的思维导图

    ListView_init.JPG
    1. 先看构造函数,上图中1.1就不分析了,主要是读取一些ListView参数,直接来看1.2 ViewGroup构造函数源码

       private void initViewGroup() { 
       ...... 
       // 初始化保存当前ViewGroup中所有View的数组 
       mChildren = new View[ARRAY_INITIAL_CAPACITY]; 
       // 初始时其Child个数为0 
       mChildrenCount = 0; 
       ...... 
       }
      
    2. 接着2即ListView.onMeasure方法,只是获取当前ListView的宽高

       @Override  
       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
       // Sets up mListPadding  
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
      
       // 获取当前ListView总宽高  
       int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
       int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
      
       ......  
      
       setMeasuredDimension(widthSize , heightSize);  
       mWidthMeasureSpec = widthMeasureSpec;          
       }  
      
    3. ListView的onLayout

    4. ListView.layoutChildren

    5. ListView.fillFromTop

    6. ListView.fillDown

    7. ListView.makeAndAddView

    8. ListView.setupChild


    Android ListView使用BaseAdapter与ListView的优化

    当系统开始绘制ListView的时候,首先调用getCount()方法,得到它的返回值,即ListView的长度,然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。

    系统显示列表时,首先需要实例化一个适配器.当手动完成适配时,必须手动映射数据,这需要重写getView方法。系统在绘制列表的每一行的时候将调用此方法。


    1. AdapterView
      adapter的相关抽象函数getAdapter、setAdapter
      mEmptyView
      观察者模式
      Accessibility

    2. AbsListView
      AbsListView定义了集合视图的框架
      比如Adapter模式的应用
      复用Item View的逻辑
      布局Item View的逻辑
      滑动事件相关
      定义了变量: ListAdapter mAdapter;
      使用了对象适配器。
      (1) 拥有RecycleBin类,负责处理view的生成和回收,没有子视图的空间定位信息
      (2) 滑动事件相关--加载数据并显示更多数据等
      onTouchEvent

    1. ListView
      ListView覆写了AbsListView中的layoutChilden函数,在该函数中根据布局模式来布局Item View。Item View的个数、样式都通过Adapter对应的方法来获取,获取个数、Item View之后,将这些Item View布局到ListView对应的坐标上,再加上Item View的复用机制,整个ListView就基本运转起来了。

    补充知识点:

    无论抽象类还是接口,抽象方法都需要在子类中实现,而且在子类中实现这些方法一个都不能少。而抽象类里面的非抽象方法,则在子类可以不重写实现里面的行为。


    参考

    Android ViewGroup系列控件的使用

    Android源码学习之适配器模式应用


    Android开发优化-Adapter优化

    Android高手进阶:Adapter深入理解与优化

    AndroidAdapter用法总结
    主要提供了ArrayAdapter,SimpleAdapter、simpleCursorAdapter实例

    Android ListView工作原理完全解析,带你从源码的角度彻底理解,androidlistview
    http://www.android100.org/html/201507/26/168809.html
    http://android.jobbole.com/81834/

    ListView源代码分析

    Android视图绘制流程完全解析,带你一步步深入了解View(二)

    Android设计模式源码解析之ListView观察者模式

    Android AdapterView 源码分析以及其相关回收机制的分析

    Android LayoutInflater原理分析,带你一步步深入了解View(一)

    相关文章

      网友评论

      本文标题:ListView详解--绘图、优化、适配器、观察者

      本文链接:https://www.haomeiwen.com/subject/ioualttx.html