绘制界面只是创建自定义视图的一个部分。您还需要让视图以与自己模仿的真实操作非常相似的方式响应用户输入。对象的行为方式应始终与真实物体相同。例如,图片不应瞬间消失后重新出现在其他地方,因为现实世界中的物体不会这样。相反,图片应从一个位置移动到另一个位置。
用户还能感受到界面中的细微行为变化或者给人带来的细微感觉变化,并对细微之处做出最佳反应来模仿现实世界。例如,当用户快滑某个界面对象时,开始快滑时要感受到导致运动延迟的摩擦力,结束时也要感受到使运动超出快滑范围的动量。
本课介绍了如何使用 Android 框架的功能将这些真实行为加到自定义视图中。
除了本课程,您还可以在输入事件和属性动画中找到更多相关信息。
处理输入手势
像许多其他界面框架一样,Android 支持输入事件模型。用户操作会转变为可触发回调的事件,您可以替换回调来自定义应用对用户的响应方式。Android 系统中最常见的输入事件是“轻触”,它将触发 替换此方法来处理事件:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean {
return super.onTouchEvent(event)
}Java
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
轻触事件本身并不特别实用。现代触控界面根据手势定义互动,例如点按、拉、推、快滑和缩放。为了将原始轻触事件转换成手势,Android 提供了
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
}
private val detector: GestureDetector = GestureDetector(context, myListener)Java
class MyListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
}
detector = new GestureDetector(PieChart.this.getContext(), new MyListener());
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean {
return detector.onTouchEvent(event).let { result ->
if (!result) {
if (event.action == MotionEvent.ACTION_UP) {
stopScrolling()
true
} else false
} else true
}
}Java
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = detector.onTouchEvent(event);
if (!result) {
if (event.getAction() == MotionEvent.ACTION_UP) {
stopScrolling();
result = true;
}
}
return result;
}
如果您向 false。然后,您可以运行自定义手势检测代码。
创建符合物理原理的运动
手势是控制触摸屏设备的一种强大方式,但它们可能违背常理且难以记忆,除非它们所产生的结果符合物理原理。就此而言,“快滑”手势是一个很好的例子,用户在屏幕上迅速移动手指,再抬起手指。如果界面响应为沿着快滑方向快速移动再放慢速度,就像用户推出飞轮并使其转动一样,那么这一手势就很合理。
不过,模拟飞轮的感觉并非易事。需要大量的物理知识和数学运算才能使飞轮模型正常工作。幸运的是,Android 提供了辅助类以模拟此行为和其他行为。
如需启动快滑,请调用
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
scroller.fling(
currentX,
currentY,
(velocityX / SCALE).toInt(),
(velocityY / SCALE).toInt(),
minX,
minY,
maxX,
maxY
)
postInvalidate()
return true
}Java
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);
postInvalidate();
return true;
}
注意:虽然
大多数视图会将
Kotlin
scroller.apply {
if (!isFinished) {
computeScrollOffset()
setPieRotation(currY)
}
}Java
if (!scroller.isFinished()) {
scroller.computeScrollOffset();
setPieRotation(scroller.getCurrY());
}
PieChart 示例采用的是第二种方法。此方法的设置稍微复杂一些,但可以更紧密地配合动画系统工作,并且不需要进行不必要的视图失效操作。缺点是
注意:您可以在面向较低 API 级别的应用中使用
Kotlin
private val scroller = Scroller(context, null, true)
private val scrollAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
addUpdateListener {
if (scroller.isFinished) {
scroller.computeScrollOffset()
setPieRotation(scroller.currY)
} else {
cancel()
onScrollFinished()
}
}
}Java
scroller = new Scroller(getContext(), null, true);
scrollAnimator = ValueAnimator.ofFloat(0,1);
scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (!scroller.isFinished()) {
scroller.computeScrollOffset();
setPieRotation(scroller.getCurrY());
} else {
scrollAnimator.cancel();
onScrollFinished();
}
}
});
实现流畅切换
用户希望现代界面能够在不同的状态之间流畅地切换。界面元素应淡入和淡出,而不是出现和消失。动作开始和结束都是平滑发生的,而不是突然开始和停止。Android 3.0 中引入的 Android 属性动画框架可以轻松实现平滑切换。
如需使用动画系统,每当属性更改会影响视图外观时,都不要直接更改属性,应使用
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "PieRotation", 0).apply {
setIntValues(targetAngle)
duration = AUTOCENTER_ANIM_DURATION
start()
}Java
autoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0);
autoCenterAnimator.setIntValues(targetAngle);
autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
autoCenterAnimator.start();
如果要更改的值是基本
Kotlin
animate()
.rotation(targetAngle)
.duration = ANIM_DURATION
.start()Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();