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的代码中,有 mScrollXmScrollY 这两个值需要我们多注意一下,其中,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。



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

原文地址:https://www.cnblogs.com/slyfox/p/9249329.html

时间: 2024-08-30 12:01:18

View 的滑动的相关文章

android中实现view可以滑动的六种方法

在android开发中,经常会遇到一个view需要它能够支持滑动的需求.今天就来总结实现其滑动的六种方法.其实每一种方法的 思路都是一样的,即:监听手势触摸的坐标来实现view坐标的变化,从而实现view的滑动效果. 一.通过Layout方法来实现滑动 如果你将滑动后的目标位置的坐标传递给Layout,这样子就会把view的位置给重新布置了一下,在视觉上就是view的一个滑动的效果. 这就是利用Layout方法实现滑动的核心思路.我们来看一下代码: 新建项目,然后自定义一个view,代码如下:

一个Demo带你彻底掌握View的滑动冲突

最近在重新学习Android自定义View这一块的内容,遇到了平时开发中经常碰到的一个棘手问题:View的滑动冲突.相信不少小伙伴都有相同的感觉,看似简单真正做起来却又不知道从何下手.今天就从一个简单的Demo带你彻底掌握解决View滑动冲突的办法. 老规矩,先上图: 示例图中是一个常见的下拉回弹,手指向下滑动的时候,整个布局会一起滑动.下拉到一定距离的时候松手,布局会自动回弹到开始的位置:手指向上滑动的时候,布局的子View会滑动到最底部,然后手指再向下滑动,布局的子View会滑动到最顶部,最

view的滑动冲突解决方案

一.常见的滑动冲突场景 1.外部滑动方向和内部滑动方向不一致 2.外部滑动方向和内部滑动方向一致 3.上面两种情况的嵌套 二.滑动冲突处理的原则 场景1的处理原则是:当用户左右滑动时,需要让外部的view拦截点击事件,当用户上下滑动时,需要让内部的view拦截点击事件.场景2和场景3比较特殊,无法如同场景1一样原则的处理冲突,需要在业务上寻找突破点.比如业务上规定:当处于某种状态时需要外部View响应用户的滑动,而处于另一种状态时则需要内部View来响应View的滑动,根据这种业务上的需求我们也

View的滑动冲突

View的滑动冲突指的是当有内外两层View同时可以滑动的时候,这个时候就会产生滑动冲突.那么应该如何解决滑动呢,其实要用到View的事件分发机制. View的滑动冲突主要有以下三个场景: 场景一:外部滑动方向和内部滑动方向不一致: 场景二:外部滑动方向和内部滑动方向一致: 场景三:以上两种情况的嵌套. 这里主要讨论场景一的滑动冲突的解决,其他两种思想都是类似的,根据具体情况而定. 对于场景一,它的滑动冲突处理规则是:当用户左右滑动时,需要外部的View拦截点击事件,当用户上下滑动时,需要内部的

android中实现view可以滑动的六种方法续篇(一)

承接上一篇,如果你没有读过前四章方法,可以点击下面的链接: http://www.cnblogs.com/fuly550871915/p/4985053.html 下面开始讲第五中方法. 五.利用Scroller类实现滑动 (1)基础知识总结 为了便于下面的阅读代码,先来总结一下关于Scroller类的基础知识.利用Scroller类实现滑动,需要三个步骤: 步骤一: 初始化Scroller对象,即mScroller = new Scroller(context)步骤二: 重写computeSc

View的滑动

一.View的位置参数(都是相对于父容器的位置) 1.View的位置由左上角点.右下角点来确定的.分别对应(左上的X—getLeft,左上的Y—getTop,右下的X—getRight,右下的Y—getBottom). 2.View的偏移量(translation):表示左上角相对于父容器的偏移量(当前位置与移动后的位置之差叫做偏移量),默认为0.(进过测试只能通过setTranslation改变Translation,补间动画animation.xml无效)通过getTransaltion获取

自定义控件(视图)2期笔记12:View的滑动冲突之 外部拦截法

1. 外部拦截法: 点击事件通过父容器拦截处理,如果父容器需要就拦截,不需要就不拦截. 2. 下面通过一个Demo示例说明: (1)首先我们创建一个Android工程,如下: (2)我们来到activity_main.xml,如下: 1 <com.himi.viewconflict.ui.RevealLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="htt

自定义控件(视图)2期笔记13:View的滑动冲突之 内部拦截法

1. 内部拦截法: 父容器不拦截事件,所有的事件全部都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理. 这种方法和Android中的事件分发机制不一样,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法稍显负责一点. 我们需要重写子元素的dispatchTouchEvent方法. 这种方法的伪代码是: 1 @Override 2 public boolean dispatchTouchEvent(Motio

12.View的滑动

1.scrollTo和ScrollBy 为了实现滑动,View提供了两个方法来让我们实现这个功能,那就是scrollTo和scrollBy方法, 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. * @para