内容简介:最近去小米之家体验了下小米9,发现MIUI有一个挺特别的列表动画效果,在系统上的各种应用上都能见到它的身影。方法,计算手指滑动距离来缩放内部控件。这种方式适合对View触摸分发机制比较熟悉的同学,代码比较复杂,看了下现有的库也都没能实现MIUI中Fling状态的弹性效果。正好最近看了下
最近去小米之家体验了下小米9,发现MIUI有一个挺特别的列表动画效果,在系统上的各种应用上都能见到它的身影。
ScrollView ,然后重写
onInterceptTouchEvent 方法和
OnTouchEvent
方法,计算手指滑动距离来缩放内部控件。
这种方式适合对View触摸分发机制比较熟悉的同学,代码比较复杂,看了下现有的库也都没能实现MIUI中Fling状态的弹性效果。正好最近看了下 NestedScrolling 的相关知识,发现能很好地实现这些效果,所以就让我们来看看吧。
预备知识
需要先了解下 NestedScrollChild 和 NestedScrollParent ,所谓的NestedScrolling机制是这样的:内部NestedScrollingChild在滚动的时候,预先将dx,dy通过 NestedScrollingChildHelper 传递给 NestedScrollingParent , NestedScrollingParent 可先对其进行部分消耗,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。
弹性列表实现
为方便解析,我们先只实现下滑的弹性动画:
//子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
}
复制代码
弹性效果
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
CASIO fx-5800P编程计算器公路与铁路施工测量程序
2011-8 / 40.00元
《CASIO fx-5800P 编程计算器公路与铁路施工测量程序(第2版)》内容简介:第2版是一本全新的图书。书中的QH2-7T与QH2-8T程序都具有三维中边桩坐标正、反算,路基超高及边桩设计高程计算,边坡坡口与坡脚计算,桥墩桩基坐标计算,隧道超欠挖计算等功能。QH2-7T为交点法程序,QH2-8T为线元法程序,两个程序均使用数据库子程序输入平竖曲线的全部设计数据。测试程序各项功能所用的案例均取......一起来看看 《CASIO fx-5800P编程计算器公路与铁路施工测量程序》 这本书的介绍吧!