• View 的滑动


    View 的滑动

    学习自

    《Android开发艺术探索》

    滑动漫谈

    因为Android手机屏幕大小的原因,所以为了显式更多的信息,我们必须采用滚动的方式来处理,因为滚动就涉及到了滑动,有的滑动十分生硬,而有的滑动却是圆润并且绚丽的,View的滑动就是我们本章要学习的内容。

    使用scrollTo/scrollBy

    注意,这种方式只是改变了View内容的位置,并没有改变View的位置,以Button为例,那就是仅仅改变了Button中的文本的位置,而没有改变Button的位置。

    scrollTo 实现了指定位置的绝对滑动

    /**
    * Set the scrolled position of your view. This will cause a call to
    * {@link #onScrollChanged(int, int, int, int)} and the view will be
    * invalidated.
    * 设置View的滚动的位置,这一操作将会回到onScrollChange事件
    * 这一操作将会使View失效
    * @param x the x position to scroll to
    * x 滑动到的x坐标
    * @param y the y position to scroll to
    * y 滑动到的y坐标
    */
    public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }
    

    scrollBy 实现了基于当前位置的相对滑动

    /**
     * Move the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the amount of pixels to scroll by horizontally
     * x 横向滚动的像素的总和
     * @param y the amount of pixels to scroll by vertically
     * y 纵向滚动的像素的总和
     */
    public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }
    

    代码分析,通过查看scrollBy的代码,我么发现scrollBy的实现也是基于scrollTo的,所以我们需要分析scrollTo的代码,在scrollTo的代码中,有 mScrollX mScrollY 这两个值需要我们多注意一下,其中,mScrollX 值代表的是view的内容和View左边缘的距离,mScrollY带边的view的内容与view上边缘的值。

    通过动画

    通过补间动画来完成移动

    这里省略的补间动画实现的介绍,因为补间动画并不会真正的改变View的位置,而只是改变的View的影像。这里就不说,因为已经有了属相动画来替代补间动画。

    通过属性动画俩完成移动

    scrollBtn.setOnClickListener {
        ObjectAnimator
                .ofFloat(scrollBtn, "translationX", 0.0F, 100.0F)
                .setDuration(1000L)
                .start()
    }
    

    改变布局参数

    通过改变ViewLayoutParams的值,我们也可以实现View的移动。方式如下

    scrollBtn.setOnClickListener {
        var params = scrollBtn.layoutParams as ViewGroup.MarginLayoutParams
        params.leftMargin = 100
        scrollBtn.requestLayout()
    }
    

    总结

    上面的三种方法都可以实现View的滑动。我们来总结一下

    • scrollTo/scrollBy,仅仅改变的是View内容的位置,而不嫩改变View自身的位置
    • 使用动画,如果是Android3.0及以上的版本使用动画实现滑动是一个非常好的方式,如果在3.0已下就需要处理View的移动的问题了,推荐使用在没有交互的View上,如果使用的3.0以上则另当别论。
    • 是真正的改变了View的位置,推荐使用在有交互性的View上。

    平滑的滑动

    通过上面的集中方法我们已经可以实现View的滑动,但是上面的方法除了通过动画的方式外,实现的效果都比较生硬,所以现在我们来学习一下,如何平滑地实现滑动。

    Scroller

    Scroller类并能帮助我们进行滑动操作,它仅仅是为我们完成滑动过程中的计算,实际的滑动的功能还需要我们自己实现,ViewPager中就中就采用了Scroller来实现,下面我们以一个Dome(将一个TextView向右滑动)的形式来学习Scroller。

    record.gif | left | 348x580

    
    
    class MyTextView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {
        var mScroller = Scroller(context)
    
        fun smoothScroll(destX: Int, destY: Int) {
            var deltaX = destX - x
            L.e(deltaX.toString())
            this.mScroller.startScroll(x.toInt(), 0, deltaX.toInt(), 0, 1000)
            invalidate()
        }
    
        override fun computeScroll() {
            if (mScroller.computeScrollOffset()) {
                this.x = mScroller.currX.toFloat()
                postInvalidate()
            }
        }
    }
    //调用
    scrollBtn.setOnClickListener {
        helloTV.smoothScroll(this.helloTV.x.toInt() + 100, 0)
    }
    

    Scroller原理浅析

    首先我们调用的是Scroller的 startScroll 方法,下面是此方法的源码,其中没有任何和滑动相关的代码,仅仅是将我么传递的参数存储了起来。参数解释:

    • startX 开始X轴的坐标
    • startY 开始Y的坐标
    • dx X轴前进的距离
    • dy Y轴前进的距离
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartX = startX;
        mStartY = startY;
        mFinalX = startX + dx;
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }
    

    startScroll 方法中我们发现,滚动并没有在这个方法中,那滑动的效果是怎么实现的,答案是通过利用View的重绘机制实现的。请注意我们调用了 invalidate() 方法,此方法会导致View重绘,View重绘时会调用View的 draw 的方法,此方法会调用我们 computeScroll 方法,因为我们已经重写此方法,所以会调用我们重写的方法,在我们重写的 computeScroll方法中,我们移动了View后,调用 postInvalidate 方法再次引发重绘,如此循环直到结束。

    上面我们提到了computeScroll,接下来看看此方法:

    override fun computeScroll() {
        //判断是否需要继续滑动
        //true 表示滑动还未结束,需要继续滑动
        //false 表示滑动已经完成
        if (mScroller.computeScrollOffset()) {
            //改变View的位置
            this.x = mScroller.currX.toFloat()
            //引发重绘
            postInvalidate()
        }
    }
    

    如果你查看了 computeScrollOffset 的源码的话,就会发现其中是计算偏移的长度的,并且返回值表示的是,是否需要继续滑动。到此我们已经对Scroller的工作方式有一个大概的了解了。

    利用属性动画

    scrollBtn.setOnClickListener {
        var animator = ValueAnimator.ofInt(0, 1).setDuration(1000)
        animator.addUpdateListener {
            var faction = it.animatedFraction
            scrollBtn.scrollTo(startX + (dalteX * faction).toInt(), 0)
        }
        animator.start()
    }
    

    使用延时策略

    通过Handler或者View的Handler或者View的 postDelayed 方法或者利用线程睡眠的方式,我们也可以实现滑动的效果。

    var end = 200
    var distance = 0
    var offset = 10
    
    private fun scroll() {
        Handler().postDelayed({
            var temp = offset
            distance += offset
            if (distance > end) {
                temp = distance - end
            }
            if (distance < end) {
                scrollBtn.scrollBy(temp, 0)
                scroll()
            }
        }, 100)
    }
    
  • 相关阅读:
    python 之os模块用法大全
    web自动化测试常用的定位方式有哪些?
    Jenkins
    吴恩达机器学习笔记:如何debug一个学习算法?
    贝叶斯网络
    leetcode-32. Longest Valid Parentheses
    leetcode4 median Of two sorted array
    leetcode21
    vs2017问题集锦
    LIS
  • 原文地址:https://www.cnblogs.com/slyfox/p/9249329.html
Copyright © 2020-2023  润新知