详细讲解Android中的AbsListView的源码

     不知道各位童鞋们在开发的过程中有没有感兴趣过ListView是如何实现的呢?其实本身ListView的父类AbsListView才是关键,但是如果大家看过源码的话,会发现AbsListView将近7000多行代码,是不是头大啊,呵呵,没事,下面咱们就一起来看看吧。

     我们先从类中的常量开始分析:

    public static final int TRANSCRIPT_MODE_DISABLED = 0;
    public static final int TRANSCRIPT_MODE_NORMAL = 1;
    public static final int TRANSCRIPT_MODE_ALWAYS_SCROLL = 2;
    static final int TOUCH_MODE_REST = -1;
    static final int TOUCH_MODE_DOWN = 0;
    static final int TOUCH_MODE_TAP = 1;
    static final int TOUCH_MODE_DONE_WAITING = 2;
    static final int TOUCH_MODE_SCROLL = 3;
    static final int TOUCH_MODE_FLING = 4;
    static final int TOUCH_MODE_OVERSCROLL = 5;
    static final int TOUCH_MODE_OVERFLING = 6;
    static final int LAYOUT_NORMAL = 0;
    static final int LAYOUT_FORCE_TOP = 1;
    static final int LAYOUT_SET_SELECTION = 2;
    static final int LAYOUT_FORCE_BOTTOM = 3;
    static final int LAYOUT_SPECIFIC = 4;
    static final int LAYOUT_SYNC = 5;
    static final int LAYOUT_MOVE_SELECTION = 6;
    public static final int CHOICE_MODE_NONE = 0;
    public static final int CHOICE_MODE_SINGLE = 1;
    public static final int CHOICE_MODE_MULTIPLE = 2;
    public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
上面的含义分别如下:

1、禁止副本模式

2、当数据集合发生变化的通知被接受到,列表将会自动的滚向底部。但条件必须是最后一条数据已经出现在屏幕上

3、列表将会自动的滚动到底部,不论当前的数据是否可见。

4、猜测我们并不是在触摸的手势中间。

5、假设我们接收到一个touch的触摸的事件,我们等待去看到它是否是一个滑动的手势。

6、预测到当前的touch事件是一个tap事件,我们正在等待这是否是一个长按的事件。

7、其余的常量与此类此,在此省略了。


接下来我们来看一一批与视图绘制相关的变量:

    Drawable mSelector;
    int mSelectorPosition = INVALID_POSITION;
    Rect mSelectorRect = new Rect();
    final RecycleBin mRecycler = new RecycleBin();
    int mSelectionLeftPadding = 0;
    int mSelectionTopPadding = 0;
    int mSelectionRightPadding = 0;

    int mSelectionBottomPadding = 0;
    Rect mListPadding = new Rect();
    int mWidthMeasureSpec = 0;
    View mScrollUp;
    View mScrollDown;
    boolean mCachingStarted;
    boolean mCachingActive;
    int mMotionPosition;
    int mMotionViewOriginalTop;
    int mMotionViewNewTop;

    int mMotionX;

    int mMotionY;

    int mTouchMode = TOUCH_MODE_REST;
    int mLastY;

    int mMotionCorrection;
    private VelocityTracker mVelocityTracker;

    private FlingRunnable mFlingRunnable;
    AbsPositionScroller mPositionScroller;

    int mSelectedTop = 0;

    boolean mStackFromBottom;
    boolean mScrollingCacheEnabled;

    boolean mFastScrollEnabled;
    boolean mFastScrollAlwaysVisible;
    private OnScrollListener mOnScrollListener;

上面的含义分别如下:

1、用来绘制选中项的图片

2、列表中当前被选中的位置

3、在绘制的时刻定义选中的location与对应的尺寸

4、这个数据被设置,存储未使用的视图,它们将会被重用,在接下来的布局中,避免重用。

5、选中的padding的位置

6、向上滚动的标志与向下滚动的标志的视图

7、当这个视图在滚动的时候,这个标志位被设置为true,预示着绘制缓存的子类在其子类上将会是能够的。

8、获取向下的移动的位置

9、其余的变量的注解省略。


接下来,我们看几个接口的定义

1、OnScrollListener

这个接口定义的是当列表或者是九宫格滚动的时候的回调。

在这个接口中存在下面的几个常量

public static int SCROLL_STATE_IDLE = 0;
public static int SCROLL_STATE_TOUCH_SCROLL = 1;
public static int SCROLL_STATE_FLING = 2;
分别指代的是当前的列表处于静止、手指处于触摸状态的滑动以及手指离开的减速滑动并趋向于静止。


其中接口中还定义了两个函数:

1、onScrollStateChanged 

2、onScroll


第一个视图是当视图滚动正准备进行时候的回调

第二个视图是当视图的滚动已经结束的回调


2、SelectionBoundsAdjuster

这个接口的含义是允许当前列表项的顶级视图实现这个接口去修改它的展示的边界区域。


接下来的代码选取几个Api来看一下:

1、setFastScrollerEnabledUiThread

private void setFastScrollerEnabledUiThread(boolean enabled) {
        if (mFastScroll != null) {
            mFastScroll.setEnabled(enabled);
        } else if (enabled) {
            mFastScroll = new FastScroller(this, mFastScrollStyle);
            mFastScroll.setEnabled(true);
        }

        resolvePadding();

        if (mFastScroll != null) {
            mFastScroll.updateLayout();
        }
    }

这个方法的功能是显而易见的,操纵的对象是FastScroller,设置是否支持快速的滑动,然后再进行重新绘制视图。


下面的几个方法是计算滚动区域与展示的效果的方法,我们选取几个来看一下:

 @Override
    protected int computeVerticalScrollExtent() {
        final int count = getChildCount();
        if (count > 0) {
            if (mSmoothScrollbarEnabled) {
                int extent = count * 100;

                View view = getChildAt(0);
                final int top = view.getTop();
                int height = view.getHeight();
                if (height > 0) {
                    extent += (top * 100) / height;
                }

                view = getChildAt(count - 1);
                final int bottom = view.getBottom();
                height = view.getHeight();
                if (height > 0) {
                    extent -= ((bottom - getHeight()) * 100) / height;
                }

                return extent;
            } else {
                return 1;
            }
        }
        return 0;
    }

这个是计算垂直方向上的滚动的区域。

这个算法我们可以看一下

1、

extent += (top * 100) / height;
这里面的100我们可以理解为系统假设单个的列表的选项的高度是100,本着多退少补的原则,不论height是大于100还是小于100, 100 / height,得到的数值可以理解为是缩放因子 scaleFactor, top * scaleFactor 计算得到的是最终需要多加的边距。


Ok,代码说到这里,算是抛砖引玉吧。





郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。