绘制滚动条

绘制滚动条是通过在View类 的 draw()函数中调用onDrawScrollBar()完成的。每个视图都可以有滚
* 动条,请 注 意 是 “每个”,因为滚动条是视图中包含的基本元素,就像视图的背景一样。举个例子,一
* 个 按 钮 Button)也可以有滚动条,读者可能觉得奇怪,按钮怎么会有滚动条呢?但的确是这样,而且
* 让按钮显示滚动条是件非常容易的事情,因为按钮本身也是一个视图,只_是从用户的角度来讲,按钮不
* 需要显示这个滚动条而已。
* 滚动条包含垂直滚动条和水平滚动条,滚动条本身是一个Drawable对象,View类内部使用
* ScrollBarDrawable类表示滚动条,View可以同时绘制水平滚动条和垂直滚动条。
* 在 ScrollBarDrawable类中,包含三个基本尺寸,分别是range、 offset、 extent。
* • range:代表该滚动条从头到尾滚动中所跨越的范围有多大。比如想用一个滚动条标识一万行代
* 码,那 么 range可以设为10000。
* • offset:代表滚动条当前的偏移量。比如当前在看第600行代码,那 么 offset就 是 600。
* • extent:代表该滚动条在屏幕上的实际高度,比如200,单位是dip。
* 有了以上三个尺寸后,ScrollBarDrawable内部就可以计算出滚动条的高度及滚动条的位置。
* 除了以上尺寸外,ScrollBarDrawable类内部还包含两个Drawable对象,一个标识滚动条的背景,
* 另一个标识滚动条自身。这两个Drawable对象分别是track和 thumble,水平方向和垂直方向分别有两
* 个该Drawable对象.**

    protected final void onDrawScrollBars(Canvas canvas) {
        // scrollbars are drawn only when the animation is running
        /**
         * onDrawScrollBarQ函数内部的执行流程如下。
         * 1) 判断该View对象内部是否存在ScrollabilityCache对象,即变量mScrollCache是否为空。对应
         * 用程序而言,当需要视图显示滚动条时,可以在XM L文件中使用androidiscrollbars属性为视图指定滚
         * 动条,属性值可以是vertical、 horzontal、 none,从而在View的构造函数中将会调用initializeScrollbars()
         * 创建一个ScrollablilityCache对象。如果View对象不是从XML文件中产生,而是通过程序动态产生,
         * 则可以调用View类 的 setScrollbarFaddingEnable(boolean)函数设置滚动条是否“ 自动隐藏(fadding)”,该
         * 函数内部则会调用initializeScrollbars()初 始 化 ScrollabilityCache对象。什么是“自动隐藏”?Android
         * 中的滚动条和PC上的滚动条有所不同,对 于 PC上的滚动条而言,在一般情况下滚动条会一直存在,
         * 在默认情况下滚动条并不显示,只有当用户做滚动操作时才会显示,滚动完毕后,滚动条就会自动藏起
         * 来,这就叫自动隐藏。应用程序可以调用setScmllbarFaddingEnable()设置是否自动隐藏,在默认情况下
         * 是自动隐。
         */
        final ScrollabilityCache cache = mScrollCache;
        if (cache != null) {
            /**
             * 2)如 果 cache存在,则继续判断cache的 状 态 (state)。 ScrollablilityCache内部有两种状态,分别
             * 为 ON和 OFF,ON意味着滚动条处于显示状态,OFF意味着滚动条处于隐藏状态。因此,本步骤判断
             * 如果是OFF状态的话,就直接返回。
             * 那么,什么时候是ON,什么时候是OFF呢?如果滚动条是自动隐藏,那么,在
             * setScrollbarFaddingEnable()函数中会将该cache的状态置为OFF,否则置为ON。如果是自动隐藏,那么
             * 当应用程序调用scrollBy()函数时,该函数内部会间接调用awakenScrollBars(),该函数中会把cache的
             * 状 态 “暂时置” 为 ON。为什么这里是暂时呢?因为该函数将cache状态置为ON后,紧接着会发送一
             * 个异步延迟消息,在指定的延迟时间后,消息的处理函数又会重新将cache的状态置为OFF,从而使得
             * 在下次绘制滚动条时会在本步骤中直接返回,这也就是“ 自动隐藏” 的具体过程。
             * awakenScrollBars()函数的类型是protected,意味着该函数只能被重载,而不能被应用程序直接调用,
             * 应用程序一般只能调用scrollBy()函数。而对于自定义View而言,比如ListView,其内部实际上并没有
             * 滚动值,所以也就不能调用 scrollBy()函数,而它实现滚动条自动隐藏的效果正是借助于
             * awakenScrollBars()函数。
             */
            int state = cache.state;
            
            if (state == ScrollabilityCache.OFF) {
                return;
            }
            
            boolean invalidate = false;
            /**
             * 3 判 断 cache的状态是否为FADING。FADDING的本质上依然是ON,只 是 它 “正在隐藏”,
             * 所 谓 “正在隐藏” 的效果一般就是滚动条“逐渐消失”,其实现方法是逐渐减少滚动条的阿尔法通道值。
             * 如果不是FADDING,则将阿尔法值设为Oxff,也就是完全不透明。
             */
            if (state == ScrollabilityCache.FADING) {
                // We're fading -- get our fade interpolation
                if (cache.interpolatorValues == null) {
                    cache.interpolatorValues = new float[1];
                }
                
                float[] values = cache.interpolatorValues;
                
                // Stops the animation if we're done
                if (cache.scrollBarInterpolator.timeToValues(values) ==
                        Interpolator.Result.FREEZE_END) {
                    cache.state = ScrollabilityCache.OFF;
                } else {
                    cache.scrollBar.setAlpha(Math.round(values[0]));
                }
                
                // This will make the scroll bars inval themselves after 
                // drawing. We only want this when we're fading so that
                // we prevent excessive redraws
                invalidate = true;
            } else {
                // We're just on -- but we may have been fading before so
                // reset alpha
                cache.scrollBar.setAlpha(255);
            }

            
            final int viewFlags = mViewFlags;

            /**
             * 4 如果存在水平或者垂直滚动条,则逐个进行绘制。( 1 ) 首先调用scrollBar.getSizeO获
             * 得滚动条的大小。对于垂直滚动条而言,大小就是指track或者
             * thumb的宽度;水平方向是指track或 者 thumb的高度,只 有
             * 当 size小 于 0 时,才 会使用X M L 中
             * android:scrollbarSize 属性的值。( 2 ) 绘制水平滚动条。( 3 ) 绘制
             * 垂直滚动条。绘制时,首先回调computeVerticalRange()等三个函数,获得当前的range、
             * offset及 extent值,然后将这三个值设置到scrollBar中。这就是为什么滚动条会“滚动” 的原因。自定
             * 义视图的设计者应该重载这三个computeXXXO函数,并在函数实现中根揮自定义的滚动情况返回相应
             * 的值,从而使得View系统能够绘制出具有正确位置的滚动条。源码中有一段注释是关于RTL语言的,
             * RTL是指从右向左阅读的语言。设置完这三个值后,回 调 onDrawVerticalScrollBarO函数,
             *
             * 即首先设置滚动条对应的剪切区,然后调用scroUBar的 draw()函数将其绘制到剪切区中。
             */
            final boolean drawHorizontalScrollBar =
                (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
            final boolean drawVerticalScrollBar =
                (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL
                && !isVerticalScrollBarHidden();

            if (drawVerticalScrollBar || drawHorizontalScrollBar) {
                final int width = mRight - mLeft;
                final int height = mBottom - mTop;

                final ScrollBarDrawable scrollBar = cache.scrollBar;
                int size = scrollBar.getSize(false);
                if (size <= 0) {
                    size = cache.scrollBarSize;
                }

                final int scrollX = mScrollX;
                final int scrollY = mScrollY;
                final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;

                int left, top, right, bottom;
                
                if (drawHorizontalScrollBar) {
                    scrollBar.setParameters(computeHorizontalScrollRange(),
                                            computeHorizontalScrollOffset(),
                                            computeHorizontalScrollExtent(), false);
                    final int verticalScrollBarGap = drawVerticalScrollBar ?
                            getVerticalScrollbarWidth() : 0;
                    top = scrollY + height - size - (mUserPaddingBottom & inside);                         
                    left = scrollX + (mPaddingLeft & inside);
                    right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
                    bottom = top + size;
                    onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom);
                    if (invalidate) {
                        invalidate(left, top, right, bottom);
                    }
                }
                /**
                 * 以上步骤已经完成了一次绘制,但如果当前滚动条正处于滚动状态,则需要继续调用invalidateO
                 * 发起一次重绘消息。而判断是否处于滚动状态的变量是invalidate,其值正是当cache状态为FADDING
                 * 时被赋值为true。调 用 invalidateO时,参数对应的矩形区仅仅是滚动条所在的区域。
                 * 至此,滚动条绘制就完成了。
                 */
                if (drawVerticalScrollBar) {
                    scrollBar.setParameters(computeVerticalScrollRange(),
                                            computeVerticalScrollOffset(),
                                            computeVerticalScrollExtent(), true);
                    // TODO: Deal with RTL languages to position scrollbar on left
                    left = scrollX + width - size - (mUserPaddingRight & inside);
                    top = scrollY + (mPaddingTop & inside);
                    right = left + size;
                    bottom = scrollY + height - (mUserPaddingBottom & inside);
                    onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom);
                    if (invalidate) {
                        invalidate(left, top, right, bottom);
                    }
                }
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值