View的滑动方式

在Android中滑动是实现自定义控件的基础,同时在开发中我们也会难免遇到View的滑动处理,不管哪种滑动处理,其基本思想都是类似的。当点击事件传到View时,系统记下触摸点的坐标,手指移动时系统记下移动后触摸的坐标并算出偏移量,并通过偏移量来修改View的坐标。下面来总结下android系统中常见的滑动方案。

View的坐标系


在介绍View的滑动时候,先要介绍下View的坐标系

在这里插入图片描述

View的滑动就是通过手指的滑动偏移量,来动态设置View的位移,从而达到滑动的效果,而滑动偏移量的获取就是通过上图方法获取坐标差值来达到的。

MotionEvent


View的滑动就涉及到事件MotionEvent,这个类中提供了很多常量来区分相关动作状态,也提供了很多方法来获取焦点坐标各种方法。

  • getX() 获取点击事件距离控件左边的距离,即视图坐标
  • getY() 获取点击事件距离控件顶部的距离,即视图坐标
  • getRawX() 获取点击事件距离整个屏幕左边的距离,即绝对坐标
  • getRawY() 获取点击事件距离整个屏幕顶边的距离,即绝对坐标

View的滑动


下面来介绍下android中的各种滑动方案

  • 1 . layout()

通过修改View的left, top, right, bottom四种属性来控制View的坐标,接下来通过自定义View来看下用法,代码如下

class CustomView: View {

    constructor(contex: Context): super(contex)

    constructor(contex: Context, attrs: AttributeSet): super(contex, attrs)

    var lastX: Int? = null
    var lastY: Int? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {

        var x = Math.round(event.x)
        var y = Math.round(event.y)

        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE -> {
                var offsetX = x - lastX!!
                var offsetY = y - lastY!!
                layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)
            }
        }
        return true
    }

}
  • 2 .offsetLeftAndRight()和offsetTopAndBottom()

用法和layout()相关

class CustomView2: View {

    constructor(contex: Context): super(contex)

    constructor(contex: Context, attrs: AttributeSet): super(contex, attrs)

    var lastX: Int? = null
    var lastY: Int? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {

        var x = Math.round(event.x)
        var y = Math.round(event.y)

        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE -> {
                var offsetX = x - lastX!!
                var offsetY = y - lastY!!
                offsetLeftAndRight(offsetX)
                offsetTopAndBottom(offsetY)
            }
        }
        return true
    }

}
  • 3 .LayoutParams(改变布局相关参数)

通过获取View的布局参数,设置里面的leftMargin,topMargin来使View滑动

class CustomView3: View {

    constructor(contex: Context): super(contex)

    constructor(contex: Context, attrs: AttributeSet): super(contex, attrs)

    var lastX: Int? = null
    var lastY: Int? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {

        var x = Math.round(event.x)
        var y = Math.round(event.y)

        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE -> {
                var offsetX = x - lastX!!
                var offsetY = y - lastY!!

                var layoutParams = layoutParams as ViewGroup.MarginLayoutParams
                layoutParams.leftMargin = left + offsetX
                layoutParams.topMargin = top + offsetY
                setLayoutParams(layoutParams)
            }
        }
        return true
    }

}

注意:这里的根布局如果是constraintlayout,还需要设置相应的边界约束才会有作用。

  • 4 .动画

在android动画实现View的滑动有好几种,其中View动画只会改变View的投影,但是并不能改变View的位置,所以一般不会使用,可以使用属性动画,或者view自带的animate()

//1
customView.animate().translationX(300F).setDuration(4000).start()
//2
ObjectAnimator.ofFloat(customView, "translationX", 0F, 300F).setDuration(4000).start()
  • 5 scrollTo与scrollBy

scrollTo(x, y)表示移动一个具体的坐标点,而scrollBy(dx, dy)则表示移动的增量为dx,dy。其中scrollBy最终也是要调用scrollTo的。

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

   public void scrollBy(int x, int y) {
      scrollTo(mScrollX + x, mScrollY + y);
   }

这两种滑动方式移动的是View的内容,如果在ViewGroup中使用,则是移动其所有的子View。ACTION_MOVE中的代码替换成如下

((View)getParent()).scrollBy(-offsetX, -offsetY)

注意,通过设置偏移量这里需要设置成负值,这里的滑动是瞬间完成的。

  • 6 .Scroller

通过Scroller()设置的滑动是有过渡性的,代码如下

class CustomView5 : View{

    private var mScroll: Scroller? = null

    constructor(context: Context): this(context, null)

    constructor(context: Context, attr: AttributeSet?): super(context, attr){
        mScroll = Scroller(context)
    }

    override fun computeScroll() {
        super.computeScroll()
        if(mScroll!!.computeScrollOffset()){
            (parent as View).scrollTo(mScroll!!.currX, mScroll!!.currY)
            invalidate()
        }
    }

    fun smoothScrollTo(destX: Int, destY: Int){
        var scrollX = scrollX
        var scrollY = scrollY
        var deltaX = destX - scrollX
        var deltaY = destY - scrollY
        mScroll!!.startScroll(scrollX, scrollY, deltaX, deltaY, 2000)
        invalidate()
    }
}

Scroller本身不能实现View的滑动,它需要与View的computeScroll()方法配合才能实现弹性滑动,在computeScroll()方法中通过scroller来不断获取当前的滑动值,每滑动一小段距离我们就调用invalidate()方法不断进行重绘。从而实现一个平滑效果。

总结


滑动方式大概就这几种,真正在项目中使用,肯定不会这么简单,还是需要结合项目需求做具体调整。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值