临时
一、优先级和学习建议
必须掌握(使用最多)⭐⭐⭐⭐⭐
- 属性动画滑动 - View.animate() / ObjectAnimator
理由:最简单、最常用,90%场景够用view.animate().translationX(100f).duration = 300 - scrollTo/scrollBy - View基础滑动
理由:所有滚动控件的基础,必须理解坐标系scrollView.scrollTo(0, 100)
应该掌握(重要但场景特定)⭐⭐⭐⭐
- Scroller弹性滑动
理由:自定义View滑动必备,理解动画原理scroller.startScroll(startX, startY, dx, dy, duration)
了解即可(特殊场景)⭐⭐⭐
- ValueAnimator手动控制
理由:特殊动画需求时使用ValueAnimator.ofFloat(0f, 1f).addUpdateListener { ... } - Handler延时策略
理由:现在基本被动画API取代,了解原理即可
没必要深入学习(已过时/有更好替代)⭐
- 传统补间动画(TranslateAnimation等)
理由:不改变实际位置,已被属性动画取代 - 直接改LayoutParams滑动
理由:性能差,触发完整重布局params.leftMargin += 100; view.requestLayout()
二、每种滑动需要做的事情
- 属性动画滑动(最简单实用)
需要做的:
// ✅ 1. 调用函数(最简单)
view.animate()
.translationX(100f) // X方向移动
.translationY(50f) // Y方向移动
.setDuration(300) // 持续时间
.setInterpolator(DecelerateInterpolator()) // 减速效果
.withEndAction { // 动画结束回调
Log.d("动画", "移动完成")
}
.start()
// ✅ 2. 也可以使用ObjectAnimator(更灵活)
val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f)
animator.duration = 300
animator.start()
不需要做的:
· ❌ 不需要自定义View类
· ❌ 不需要重写任何方法
· ❌ 不需要手动计算每一帧
- scrollTo/scrollBy(理解基础)
需要做的:
// ✅ 1. 理解坐标系(重点!)
// scrollTo(x, y):移动到绝对位置
// scrollBy(dx, dy):相对当前位置移动
// ✅ 2. 注意方向(容易混淆!)
// 正值:内容向左/上移动,View向右/下显示
scrollView.scrollBy(100, 0) // 内容向左移100px,View显示右边内容
// ✅ 3. 获取当前滚动位置
val currentX = scrollView.scrollX
val currentY = scrollView.scrollY
// ✅ 4. 常用场景
scrollView.scrollTo(0, 0) // 回到顶部
scrollView.scrollTo(0, contentHeight) // 滚到底部
不需要做的:
· ❌ 不需要动画效果(瞬间移动)
· ❌ 不需要处理复杂边界
- Scroller弹性滑动(自定义View必备)
需要做的:
// ✅ 1. 自定义View中声明Scroller
class MyScrollView(context: Context) : View(context) {
private val scroller = Scroller(context)
// ✅ 2. 提供启动方法
fun smoothScrollTo(destX: Int, destY: Int, duration: Int = 300) {
val startX = scrollX
val startY = scrollY
scroller.startScroll(startX, startY, destX - startX, destY - startY, duration)
invalidate() // ⭐ 关键:触发重绘
}
// ✅ 3. 必须重写computeScroll()
override fun computeScroll() {
if (scroller.computeScrollOffset()) {
scrollTo(scroller.currX, scroller.currY)
postInvalidate() // ⭐ 关键:继续动画
}
}
}
// ✅ 4. 使用
val myView = MyScrollView(context)
myView.smoothScrollTo(100, 50, 500) // 500ms内滑动到(100,50)
学习顺序建议:
- 先掌握属性动画(立即能用)
- 理解scrollTo/scrollBy的坐标系
- 最后学习Scroller(理解原理)
三、具体到你的学习项目
对于坐标系演示,应该:
当前代码(直接改translation):
demoView.translationX += 50f // 瞬间移动
改进为属性动画(推荐):
demoView.animate()
.translationX(demoView.translationX + 50f)
.setDuration(300)
.setInterpolator(DecelerateInterpolator())
.withEndAction {
// 移动完成后更新坐标显示
updateCoordinateDisplay(demoView)
}
.start()
如果要演示Scroller(教学目的):
// 1. 创建自定义View
class CoordinateDemoView(context: Context) : TextView(context) {
private val scroller = Scroller(context)
fun smoothMove(dx: Float, dy: Float) {
scroller.startScroll(
translationX.toInt(),
translationY.toInt(),
dx.toInt(),
dy.toInt(),
300
)
invalidate()
}
override fun computeScroll() {
if (scroller.computeScrollOffset()) {
translationX = scroller.currX.toFloat()
translationY = scroller.currY.toFloat()
postInvalidate()
}
}
}
// 2. XML中使用
<com.example.CoordinateDemoView
android:id="@+id/coordinate_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
// 3. 代码中调用
val view = findViewById<CoordinateDemoView>(R.id.coordinate_view)
view.smoothMove(50f, 0f) // 300ms内向右平滑移动50px
四、学习路线图
第1步:属性动画(立即见效)
↓
第2步:scrollTo/scrollBy(理解基础)
↓
第3步:Scroller(深入原理)
↓
第4步:其他(按需学习)
总结建议:
对于初学者:
- 先用属性动画实现所有滑动需求
- 理解scrollTo/scrollBy的坐标系概念
- 暂时跳过Scroller,等需要自定义View时再学
对于你的演示项目:
· 保持当前:直接改translation(简单明了)
· 或改进为:属性动画(更美观)
· 不必实现:Scroller(增加复杂度,教学价值有限)
你想先从哪种开始改进?
View.animate() 返回的是 ViewPropertyAnimator,它是属性动画的一种简化封装/快捷方式。
在属性动画体系中的位置:
属性动画体系 (Property Animation)
├── ValueAnimator (基础)
│ └── ObjectAnimator (最常用) ← 最灵活
│
├── AnimatorSet (动画组合)
│
└── ViewPropertyAnimator (View专属快捷方式) ← View.animate() 返回这个
具体对比:
- ViewPropertyAnimator (View.animate())
view.animate()
.translationX(100f)
.translationY(50f)
.rotation(180f)
.setDuration(300)
.start()
特点:
· 专为View设计的链式调用API
· 自动处理属性名的映射(不用写字符串)
· 性能优化:多个属性动画会合并执行
· 简单易用,但不够灵活
- ObjectAnimator (标准方式)
ObjectAnimator.ofFloat(view, "translationX", 0f, 100f).apply {
duration = 300
start()
}
// 或者多个属性
val animatorX = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f)
val animatorY = ObjectAnimator.ofFloat(view, "translationY", 0f, 50f)
AnimatorSet().apply {
playTogether(animatorX, animatorY)
duration = 300
start()
}
特点:
· 更灵活:可以动画任何对象的任何属性
· 需要手动指定属性名字符串
· 多个动画需要自己组合
- ValueAnimator (最底层)
ValueAnimator.ofFloat(0f, 100f).apply {
duration = 300
addUpdateListener { animator ->
val value = animator.animatedValue as Float
view.translationX = value
}
start()
}
特点:
· 完全手动控制每一帧
· 最灵活但也最复杂
在你的学习项目中的应用建议:
简单移动就用 View.animate()
// 你的情况:简单移动View
demoView.animate()
.translationX(targetX)
.setDuration(300)
.withEndAction {
// 动画结束后更新显示
updateCoordinateDisplay(demoView)
}
.start()
需要复杂控制时用 ObjectAnimator
// 如果需要特殊插值器、重复、反转等
val animator = ObjectAnimator.ofFloat(demoView, "translationX", currentX, targetX)
animator.apply {
duration = 300
interpolator = BounceInterpolator() // 弹跳效果
repeatCount = 1
repeatMode = ValueAnimator.REVERSE
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
updateCoordinateDisplay(demoView)
}
})
start()
}
总结:
View.animate() 是:
· 属性动画的一种快捷实现
· 专为View优化的简化API
· 适合大多数View动画场景
· 底层仍然是ObjectAnimator
对于你的学习:
- 先用 View.animate() - 简单够用
- 需要时再学 ObjectAnimator - 更高级控制
- 暂时不用管 ValueAnimator - 太底层

浙公网安备 33010602011771号