仿MIUI弹性列表

栏目: IOS · Android · 发布时间: 5年前

内容简介:最近去小米之家体验了下小米9,发现MIUI有一个挺特别的列表动画效果,在系统上的各种应用上都能见到它的身影。方法,计算手指滑动距离来缩放内部控件。这种方式适合对View触摸分发机制比较熟悉的同学,代码比较复杂,看了下现有的库也都没能实现MIUI中Fling状态的弹性效果。正好最近看了下

最近去小米之家体验了下小米9,发现MIUI有一个挺特别的列表动画效果,在系统上的各种应用上都能见到它的身影。

仿MIUI弹性列表
网上查了下,小米早在几个系统版本前就有这个,网上也有了实现这个效果的控件库。实现方法大同小异,大多都是通过继承 ScrollView ,然后重写 onInterceptTouchEvent 方法和 OnTouchEvent

方法,计算手指滑动距离来缩放内部控件。

这种方式适合对View触摸分发机制比较熟悉的同学,代码比较复杂,看了下现有的库也都没能实现MIUI中Fling状态的弹性效果。正好最近看了下 NestedScrolling 的相关知识,发现能很好地实现这些效果,所以就让我们来看看吧。

预备知识

需要先了解下 NestedScrollChildNestedScrollParent ,所谓的NestedScrolling机制是这样的:内部NestedScrollingChild在滚动的时候,预先将dx,dy通过 NestedScrollingChildHelper 传递给 NestedScrollingParentNestedScrollingParent 可先对其进行部分消耗,Parent处理完后,再将剩余的部分还给内部 NestedScrollingChild 处理,最后再把剩下的dx,dy再给Parent做最后处理,这样一次触摸滑动事件将可以由多个控件共同消耗处理,这样就可以很方便解决之前一次触摸滑动事件只能被一个控件响应而产生的嵌套滑动问题。

先看下 NestedScrollParent

public interface NestedScrollingParent {
 
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
 
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
 
    public void onStopNestedScroll(View target);
    
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed);
    
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
 
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
 
    public boolean onNestedPreFling(View target, float velocityX, float velocityY);
 
    public int getNestedScrollAxes();
}
复制代码

先看下 NestedScrollingChild

public interface NestedScrollingChild {
  ​
      void setNestedScrollingEnabled(boolean enabled);
  ​
      boolean isNestedScrollingEnabled();
  ​
      boolean startNestedScroll(int axes);
  ​
      void stopNestedScroll();
  ​
      boolean hasNestedScrollingParent();
  ​
      boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
              int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
  ​
      boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
      
      boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
     
      boolean dispatchNestedPreFling(float velocityX, float velocityY);
  }
复制代码

可以看到parent和child的api命名很类似,是成对出现的,确实,它们之前存在发起方和接收方的事件调用关系,都是由child先响应滑动触摸实现,通过 NestedScrollingChildHelper 分发给parent。

仿MIUI弹性列表

弹性列表实现

为方便解析,我们先只实现下滑的弹性动画:

//子view,需事先NestedScrollingChild
    private var childView: View? = null

    private val mNestedScrollingParentHelper: NestedScrollingParentHelper

    private var offsetScale = 0f
    private var flingScale = 0f
    private var consumedDy = 0
        set(value) {
            field = if (value > 0) {
                0
            } else {
                value
            }
        }
    //是否是Fling滑动
    private var filing = false
    //判定滑动的最小距离
    private var touchSlop: Int = 0

    private var animator: ValueAnimator? = null


    init {
        mNestedScrollingParentHelper = NestedScrollingParentHelper(this)
        touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    }


    override fun onFinishInflate() {
        super.onFinishInflate()
        childView = getChildAt(0)
    }

    /**
     * 滚动开始
     */
    override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean {
        filing = false
        consumedDy = 0
        return child === childView && ViewCompat.SCROLL_AXIS_VERTICAL == nestedScrollAxes
    }


    /**
     * 先于child滚动
     */
    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) {
        if (childView!!.scrollY == 0 && (dy < 0 || consumedDy < 0)) {
            consumedDy += dy
            if (Math.abs(consumedDy) > touchSlop) {
                //计算缩放值,最大放大1.3倍
                offsetScale = (1.3 - 600f / (2000 + Math.pow(Math.abs(consumedDy).toDouble(), 2.0))).toFloat()
                startBouncingTop()
                //存放消耗的距离,child会接收
                consumed[1] = dy
            }
        }
    }

    override fun onNestedScrollAccepted(child: View, target: View, nestedScrollAxes: Int) {
        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes)
    }

    /**
     * 先于child处理Fling
     */
    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
        if (velocityY < 0 && childView!!.scrollY == 0) {
            filing = true
            consumedDy = (consumedDy + velocityY).toInt()
            flingScale = (0.3 - 600f / (2000 + Math.pow(Math.abs(consumedDy).toDouble(), 2.0))).toFloat()
            return true
        }
        return false
    }

    override fun onNestedFling(target: View, velocityX: Float, velocityY: Float, consumed: Boolean): Boolean {
        return filing
    }

    override fun onStopNestedScroll(target: View) {
        mNestedScrollingParentHelper.onStopNestedScroll(target)
        backBouncing(filing)
    }

    override fun getNestedScrollAxes(): Int {
        return mNestedScrollingParentHelper.nestedScrollAxes
    }

    /**
     * 进行回弹
     */
    private fun backBouncing(filing: Boolean) {
        //初始化
        if (animator != null && animator!!.isRunning) {
            animator!!.cancel()
            animator = null
        }
        if (filing) {
            animator = ValueAnimator.ofFloat(offsetScale, flingScale, 0f)
            animator!!.duration = 400
        } else {
            animator = ValueAnimator.ofFloat(offsetScale, 0f)
            animator!!.duration = 250
        }
        animator!!.interpolator = OvershootInterpolator()
        animator!!.addUpdateListener {
            offsetScale = it.animatedValue as Float
            startBouncingTop()
        }
        animator!!.start()
    }

    /**
     * 从顶部开始滑动
     */
    private fun startBouncingTop() {
        childView!!.pivotY = 0f
        childView!!.pivotX = 0f
        childView!!.scaleY = offsetScale
    }
复制代码

弹性效果

仿MIUI弹性列表
Fling弹性效果
仿MIUI弹性列表

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Twisted Network Programming Essentials

Twisted Network Programming Essentials

Abe Fettig / O'Reilly Media, Inc. / 2005-10-20 / USD 29.95

Developing With Python's Event-driven Framework一起来看看 《Twisted Network Programming Essentials》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换